git.iko.soy/typeable/postgresql-simple
on 32c3648 help sign in
73 files changed+1976−11251
since splitsnapshots
unifiedsplit
.github/workflows/haskell-ci.yml−256 .github/workflows/simple.yml−58 .gitignore+1−8 CHANGES.md−746 CONTRIBUTING.md−13 CONTRIBUTORS−36 README.md−4 bench/Select.hs−32 cabal.haskell-ci−5 cabal.project−4 cabal.project.local.win−4 postgresql-simple.cabal+56−169 sdist.sh−25 src/Database/PostgreSQL/Simple.hs+330−495 src/Database/PostgreSQL/Simple.hs-boot−23 src/Database/PostgreSQL/Simple/Arrays.hs−92 src/Database/PostgreSQL/Simple/BuiltinTypes.hs+403 src/Database/PostgreSQL/Simple/Compat.hs−41 src/Database/PostgreSQL/Simple/Copy.hs−269 src/Database/PostgreSQL/Simple/Cursor.hs−98 src/Database/PostgreSQL/Simple/Errors.hs−140 src/Database/PostgreSQL/Simple/Field.hs+15 src/Database/PostgreSQL/Simple/FromField.hs−708 src/Database/PostgreSQL/Simple/FromField.hs-boot−11 src/Database/PostgreSQL/Simple/FromRow.hs−487 src/Database/PostgreSQL/Simple/FromRow.hs-boot−17 src/Database/PostgreSQL/Simple/HStore.hs−36 src/Database/PostgreSQL/Simple/HStore/Implementation.hs−210 src/Database/PostgreSQL/Simple/HStore/Internal.hs−19 src/Database/PostgreSQL/Simple/Implementation.hs+187 src/Database/PostgreSQL/Simple/Internal.hs−637 src/Database/PostgreSQL/Simple/Internal/PQResultUtils.hs−123 src/Database/PostgreSQL/Simple/LargeObjects.hs−97 src/Database/PostgreSQL/Simple/Newtypes.hs−49 src/Database/PostgreSQL/Simple/Notification.hs−145 src/Database/PostgreSQL/Simple/Ok.hs−90 src/Database/PostgreSQL/Simple/Param.hs+205 src/Database/PostgreSQL/Simple/QueryParams.hs+85 src/Database/PostgreSQL/Simple/QueryResults.hs+219 src/Database/PostgreSQL/Simple/Range.hs−317 src/Database/PostgreSQL/Simple/Result.hs+252 src/Database/PostgreSQL/Simple/SqlQQ.hs−90 src/Database/PostgreSQL/Simple/Time.hs−246 src/Database/PostgreSQL/Simple/Time/Implementation.hs−176 src/Database/PostgreSQL/Simple/Time/Internal.hs−27 src/Database/PostgreSQL/Simple/Time/Internal/Parser.hs−204 src/Database/PostgreSQL/Simple/Time/Internal/Printer.hs−135 src/Database/PostgreSQL/Simple/ToField.hs−393 src/Database/PostgreSQL/Simple/ToField.hs-boot−30 src/Database/PostgreSQL/Simple/ToRow.hs−219 src/Database/PostgreSQL/Simple/ToRow.hs-boot−18 src/Database/PostgreSQL/Simple/Transaction.hs−264 src/Database/PostgreSQL/Simple/TypeInfo.hs−121 src/Database/PostgreSQL/Simple/TypeInfo/Macro.hs−53 src/Database/PostgreSQL/Simple/TypeInfo/Static.hs−1802 src/Database/PostgreSQL/Simple/TypeInfo/Types.hs−56 src/Database/PostgreSQL/Simple/Types.hs+37−187 src/Database/PostgreSQL/Simple/Vector.hs−45 src/Database/PostgreSQL/Simple/Vector/Unboxed.hs−44 test/Common.hs−32 test/Exception.hs−70 test/Inspection.hs−79 test/Interval.hs−176 test/Main.hs−648 test/Notify.hs−41 test/Serializable.hs−68 test/Time.hs−119 test/WRITING-TESTS−42 test/results/malformed-input.expected−4 test/results/unique-constraint-violation.expected−5 tools/GenBuiltinTypes.hs+185 tools/GenTypeInfo.hs−381 tools/StringsQQ.hs+1−12
.github/workflows/haskell-ci.ymldeleted−256
@@ -1,256 +0,0 @@
1−# This GitHub workflow config has been generated by a script via
2−#
3−# haskell-ci 'github' 'cabal.project'
4−#
5−# To regenerate the script (for example after adjusting tested-with) run
6−#
7−# haskell-ci regenerate
8−#
9−# For more information, see https://github.com/haskell-CI/haskell-ci
10−#
11−# version: 0.17.20231110
12−#
13−# REGENDATA ("0.17.20231110",["github","cabal.project"])
14−#
15−name: Haskell-CI
16−on:
17− push:
18− branches:
19− - master
20− pull_request:
21− branches:
22− - master
23−jobs:
24− linux:
25− name: Haskell-CI - Linux - ${{ matrix.compiler }}
26− runs-on: ubuntu-20.04
27− timeout-minutes:
28− 60
29− container:
30− image: buildpack-deps:bionic
31− services:
32− postgres:
33− image: postgres:14
34− env:
35− POSTGRES_PASSWORD: postgres
36− options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
37− continue-on-error: ${{ matrix.allow-failure }}
38− strategy:
39− matrix:
40− include:
41− - compiler: ghc-9.8.1
42− compilerKind: ghc
43− compilerVersion: 9.8.1
44− setup-method: ghcup
45− allow-failure: false
46− - compiler: ghc-9.6.3
47− compilerKind: ghc
48− compilerVersion: 9.6.3
49− setup-method: ghcup
50− allow-failure: false
51− - compiler: ghc-9.4.8
52− compilerKind: ghc
53− compilerVersion: 9.4.8
54− setup-method: ghcup
55− allow-failure: false
56− - compiler: ghc-9.2.8
57− compilerKind: ghc
58− compilerVersion: 9.2.8
59− setup-method: ghcup
60− allow-failure: false
61− - compiler: ghc-9.0.2
62− compilerKind: ghc
63− compilerVersion: 9.0.2
64− setup-method: ghcup
65− allow-failure: false
66− - compiler: ghc-8.10.7
67− compilerKind: ghc
68− compilerVersion: 8.10.7
69− setup-method: ghcup
70− allow-failure: false
71− - compiler: ghc-8.8.4
72− compilerKind: ghc
73− compilerVersion: 8.8.4
74− setup-method: hvr-ppa
75− allow-failure: false
76− - compiler: ghc-8.6.5
77− compilerKind: ghc
78− compilerVersion: 8.6.5
79− setup-method: hvr-ppa
80− allow-failure: false
81− fail-fast: false
82− steps:
83− - name: apt
84− run: |
85− apt-get update
86− apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5
87− if [ "${{ matrix.setup-method }}" = ghcup ]; then
88− mkdir -p "$HOME/.ghcup/bin"
89− curl -sL https://downloads.haskell.org/ghcup/0.1.20.0/x86_64-linux-ghcup-0.1.20.0 > "$HOME/.ghcup/bin/ghcup"
90− chmod a+x "$HOME/.ghcup/bin/ghcup"
91− "$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false)
92− "$HOME/.ghcup/bin/ghcup" install cabal 3.10.2.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
93− else
94− apt-add-repository -y 'ppa:hvr/ghc'
95− apt-get update
96− apt-get install -y "$HCNAME"
97− mkdir -p "$HOME/.ghcup/bin"
98− curl -sL https://downloads.haskell.org/ghcup/0.1.20.0/x86_64-linux-ghcup-0.1.20.0 > "$HOME/.ghcup/bin/ghcup"
99− chmod a+x "$HOME/.ghcup/bin/ghcup"
100− "$HOME/.ghcup/bin/ghcup" install cabal 3.10.2.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
101− fi
102− env:
103− HCKIND: ${{ matrix.compilerKind }}
104− HCNAME: ${{ matrix.compiler }}
105− HCVER: ${{ matrix.compilerVersion }}
106− - name: Set PATH and environment variables
107− run: |
108− echo "$HOME/.cabal/bin" >> $GITHUB_PATH
109− echo "LANG=C.UTF-8" >> "$GITHUB_ENV"
110− echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV"
111− echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV"
112− HCDIR=/opt/$HCKIND/$HCVER
113− if [ "${{ matrix.setup-method }}" = ghcup ]; then
114− HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER")
115− HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#')
116− HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#')
117− echo "HC=$HC" >> "$GITHUB_ENV"
118− echo "HCPKG=$HCPKG" >> "$GITHUB_ENV"
119− echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV"
120− echo "CABAL=$HOME/.ghcup/bin/cabal-3.10.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
121− else
122− HC=$HCDIR/bin/$HCKIND
123− echo "HC=$HC" >> "$GITHUB_ENV"
124− echo "HCPKG=$HCDIR/bin/$HCKIND-pkg" >> "$GITHUB_ENV"
125− echo "HADDOCK=$HCDIR/bin/haddock" >> "$GITHUB_ENV"
126− echo "CABAL=$HOME/.ghcup/bin/cabal-3.10.2.0 -vnormal+nowrap" >> "$GITHUB_ENV"
127− fi
128−
129− HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')
130− echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV"
131− echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV"
132− echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV"
133− echo "HEADHACKAGE=false" >> "$GITHUB_ENV"
134− echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV"
135− echo "GHCJSARITH=0" >> "$GITHUB_ENV"
136− env:
137− HCKIND: ${{ matrix.compilerKind }}
138− HCNAME: ${{ matrix.compiler }}
139− HCVER: ${{ matrix.compilerVersion }}
140− - name: env
141− run: |
142− env
143− - name: write cabal config
144− run: |
145− mkdir -p $CABAL_DIR
146− cat >> $CABAL_CONFIG <<EOF
147− remote-build-reporting: anonymous
148− write-ghc-environment-files: never
149− remote-repo-cache: $CABAL_DIR/packages
150− logs-dir: $CABAL_DIR/logs
151− world-file: $CABAL_DIR/world
152− extra-prog-path: $CABAL_DIR/bin
153− symlink-bindir: $CABAL_DIR/bin
154− installdir: $CABAL_DIR/bin
155− build-summary: $CABAL_DIR/logs/build.log
156− store-dir: $CABAL_DIR/store
157− install-dirs user
158− prefix: $CABAL_DIR
159− repository hackage.haskell.org
160− url: http://hackage.haskell.org/
161− EOF
162− cat >> $CABAL_CONFIG <<EOF
163− program-default-options
164− ghc-options: $GHCJOBS +RTS -M3G -RTS
165− EOF
166− cat $CABAL_CONFIG
167− - name: versions
168− run: |
169− $HC --version || true
170− $HC --print-project-git-commit-id || true
171− $CABAL --version || true
172− - name: update cabal index
173− run: |
174− $CABAL v2-update -v
175− - name: install cabal-plan
176− run: |
177− mkdir -p $HOME/.cabal/bin
178− curl -sL https://github.com/haskell-hvr/cabal-plan/releases/download/v0.7.3.0/cabal-plan-0.7.3.0-x86_64-linux.xz > cabal-plan.xz
179− echo 'f62ccb2971567a5f638f2005ad3173dba14693a45154c1508645c52289714cb2 cabal-plan.xz' | sha256sum -c -
180− xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan
181− rm -f cabal-plan.xz
182− chmod a+x $HOME/.cabal/bin/cabal-plan
183− cabal-plan --version
184− - name: checkout
185− uses: actions/checkout@v3
186− with:
187− path: source
188− - name: initial cabal.project for sdist
189− run: |
190− touch cabal.project
191− echo "packages: $GITHUB_WORKSPACE/source/." >> cabal.project
192− cat cabal.project
193− - name: sdist
194− run: |
195− mkdir -p sdist
196− $CABAL sdist all --output-dir $GITHUB_WORKSPACE/sdist
197− - name: unpack
198− run: |
199− mkdir -p unpacked
200− find sdist -maxdepth 1 -type f -name '*.tar.gz' -exec tar -C $GITHUB_WORKSPACE/unpacked -xzvf {} \;
201− - name: generate cabal.project
202− run: |
203− PKGDIR_postgresql_simple="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/postgresql-simple-[0-9.]*')"
204− echo "PKGDIR_postgresql_simple=${PKGDIR_postgresql_simple}" >> "$GITHUB_ENV"
205− rm -f cabal.project cabal.project.local
206− touch cabal.project
207− touch cabal.project.local
208− echo "packages: ${PKGDIR_postgresql_simple}" >> cabal.project
209− echo "package postgresql-simple" >> cabal.project
210− echo " ghc-options: -Werror=missing-methods" >> cabal.project
211− cat >> cabal.project <<EOF
212− allow-newer: cryptohash-md5-0.11.100.1:base
213− EOF
214− $HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: $_ installed\n" unless /^(postgresql-simple)$/; }' >> cabal.project.local
215− cat cabal.project
216− cat cabal.project.local
217− - name: dump install plan
218− run: |
219− $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all
220− cabal-plan
221− - name: restore cache
222− uses: actions/cache/restore@v3
223− with:
224− key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
225− path: ~/.cabal/store
226− restore-keys: ${{ runner.os }}-${{ matrix.compiler }}-
227− - name: install dependencies
228− run: |
229− $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --dependencies-only -j2 all
230− $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dependencies-only -j2 all
231− - name: build w/o tests
232− run: |
233− $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
234− - name: build
235− run: |
236− $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --write-ghc-environment-files=always
237− - name: tests
238− run: |
239− $CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct
240− - name: cabal check
241− run: |
242− cd ${PKGDIR_postgresql_simple} || false
243− ${CABAL} -vnormal check
244− - name: haddock
245− run: |
246− $CABAL v2-haddock --disable-documentation --haddock-all $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
247− - name: unconstrained build
248− run: |
249− rm -f cabal.project.local
250− $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
251− - name: save cache
252− uses: actions/cache/save@v3
253− if: always()
254− with:
255− key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
256− path: ~/.cabal/store
.github/workflows/simple.ymldeleted−58
@@ -1,58 +0,0 @@
1−name: Simple
2−on:
3− push:
4− branches:
5− - master
6− pull_request:
7− branches:
8− - master
9−
10−jobs:
11− native:
12− name: "Simple: GHC ${{ matrix.ghc }} on ${{ matrix.os }}"
13− runs-on: ${{ matrix.os }}
14− strategy:
15− matrix:
16− os: [macos-latest, windows-latest]
17− ghc: ['8.10','9.0','9.2','9.4.8','9.6.3','9.8.1']
18− fail-fast: false
19− timeout-minutes:
20− 60
21− steps:
22− - name: Set git to use LF
23− run: |
24− git config --global core.autocrlf false
25− git config --global core.eol lf
26−
27− - name: Set up Haskell
28− id: setup-haskell
29− uses: haskell/actions/setup@v2
30− with:
31− ghc-version: ${{ matrix.ghc }}
32− cabal-version: '3.10.2.0'
33−
34− - name: Set up PostgreSQL
35− uses: ikalnytskyi/action-setup-postgres@v3
36− id: postgres
37− with:
38− username: ci
39− password: sw0rdfish
40− database: test
41−
42− - name: Checkout
43− uses: actions/checkout@v3.0.2
44−
45− - name: Cache
46− uses: actions/cache@v2.1.3
47− with:
48− path: ${{ steps.setup-haskell.outputs.cabal-store }}
49− key: ${{ runner.os }}-${{ matrix.ghc }}-${{ github.sha }}
50− restore-keys: ${{ runner.os }}-${{ matrix.ghc }}-
51−
52− - name: Build
53− run: cabal build all --enable-tests
54−
55− - name: Test
56− run: cabal test all --enable-tests --test-show-details=direct
57− env:
58− DATABASE_CONNSTRING: ${{ steps.postgres.outputs.connection-uri }}
.gitignore+1−8
@@ -7,11 +7,4 @@ dist/
77*.o
88\#*\#
99.#*
10−/CHANGELOG.md
11−.cabal-sandbox/
12−.stack-work/
13−cabal.sandbox.config
14−/dist-newstyle
15−.ghc.environment.*
16−cabal.project.local
17−dump-core
10+postgresql-simple-*.tar.gz
CHANGES.mddeleted−746
@@ -1,746 +0,0 @@
1−### Version 0.7.0.0 (2023-07-31)
2−
3− * Remove Eq Null instance. (Future `base` may break it, we remove it profilacticly).
4− * Use `postgresql-libpq >=0.10.0.0`. It includes critical fixes,
5− by using `postgresql-simple >=0.7` you won't get older `postgresql-libpq`.
6− * Drop support for GHC prior 8.6
7− * Added a class for `postgresql-simple` exceptions: `SomePostgreSqlException`.
8− This allows to catch all `postgresql-simple` exceptions at once.
9− (c.f. `AsyncException` in `base).
10−
11−### Version 0.6.5.1 (2023-07-09)
12−
13− * Support `aeson-2.2.0.0`
14−
15−### Version 0.6.5 (2022-10-30)
16−
17− * Add `withConnect`
18−
19−### Version 0.6.4 (2021-01-06)
20−
21− * Add foldCopyData helper function
22− Thanks to Sebastián Estrella for the implementation
23− https://github.com/haskellari/postgresql-simple/pull/56
24− * Implement support for postgresql 'interval' type
25− Thanks to Andre Marques Lee for the implementation
26− https://github.com/haskellari/postgresql-simple/pull/60
27− * Depend on `time-compat` to provide uniform `time` related interface.
28−
29−### Version 0.6.3 (2020-11-15)
30−
31− * Add `fromFieldJSONByteString`
32− Thanks to tomjaguarpaw for the implementation
33− https://github.com/haskellari/postgresql-simple/pull/47
34− * Add `attoFieldParser`
35− Thanks to Victor Nawothnig for the implementation
36− https://github.com/haskellari/postgresql-simple/pull/45
37− * Add `Identity` and `Const` instance
38− Thanks to Cary Robbins for the implementation
39− https://github.com/haskellari/postgresql-simple/pull/46
40− * Add `withTransactionModeRetry'`, a variant of `withTransactionModeRetry`
41− for all exception types.
42− Thanks to Elliot Cameron for the implementation
43− https://github.com/haskellari/postgresql-simple/pull/42
44− * Fix spurious aborts when retrying transactions
45− Thanks to Elliot Cameron for the implementation
46− https://github.com/haskellari/postgresql-simple/pull/34
47− * Add `Database.PostgreSQL.Simple.Newtypes` module
48− with `Aeson` newtype.
49− https://github.com/haskellari/postgresql-simple/pull/55
50−
51−### Version 0.6.2 (2019-04-26)
52−
53− * Define `MonadFail Ok`.
54−
55−### Version 0.6.1 (2019-03-04)
56−
57− * Escape double '??' to a literal '?
58− Thanks to Felix Paulusma for the implementation.
59− https://github.com/phadej/postgresql-simple/pull/5
60−
61− * Mention GHC Generics support in the documentation.
62− Thanks to Gabriel Gonzalez for the implementation.
63− https://github.com/phadej/postgresql-simple/pull/6
64−
65− * Better error message for "Query resulted in a command response
66− Thanks to Max Amanshauser for the implementation.
67− https://github.com/phadej/postgresql-simple/pull/7
68−
69− * fromJSONField: Include JSONPath on JSON parse errors
70− Thanks to Simon Hengel for the implementation.
71− https://github.com/phadej/postgresql-simple/pull/2
72−
73− * No TH in implementation
74− https://github.com/phadej/postgresql-simple/pull/4
75−
76−### Version 0.6 (2018-10-16)
77−
78− * *Breaking change*: Use `Only` package's `Only` for a common 1-tuple.
79−
80− Consider a downstream library depending already both on
81− `Only` and `postgresql-simple` package. This library my define
82− a `MyClass` with instances for `Only.Only` and `PostgreSQL.Only`.
83− As now these types are the same, a library would break.
84− Therefore I consider "merging" types a breaking change.
85−
86− There are two ways for adopting this change in that scenario:
87−
88− - Either CPP-guard `PostgreSQL.Only` instance with
89−
90− ```haskell
91− #if !MIN_VERSION_postgresql_simple(0,6,0)
92− instance MyClass (PostgreSQL.Only a) where ...
93− #endif
94− ```
95−
96− - or simply remove it and add `postgresql-simple >=0.6` lower bound,
97− making sure that there's only single `Only`.
98−
99− * Add `ToField` instances for case-insensitive strict and lazy text.
100− Thanks to Max Tagher for the implementation.
101− https://github.com/lpsmith/postgresql-simple/pull/232
102−
103− * Add support to CockroachDB.
104− Thanks to Georte Steel.
105− https://github.com/lpsmith/postgresql-simple/pull/245
106−
107− * Add Generic ConnectInfo instance
108− Thanks to Dmitry Dzhus.
109− https://github.com/lpsmith/postgresql-simple/pull/235
110−
111− * Add `fromFieldRange :: Typeable a => FieldParser a -> FieldParser (PGRange a)`
112− https://github.com/lpsmith/postgresql-simple/pull/221
113−
114− * Add `fromFieldJSONByteString :: FieldParser ByteString`
115− https://github.com/lpsmith/postgresql-simple/pull/222/files
116−
117− * Fix off-by-one error in year builder.
118− Thanks to Nathan Ferris Hunter.
119− https://github.com/lpsmith/postgresql-simple/pull/230
120−
121− * Extend ToRow and FromRow to tuples of size 18
122− Thanks to Bardur Arantsson.
123− https://github.com/lpsmith/postgresql-simple/pull/229
124−
125− * Add `Vector` and `Vector.Unboxed` `query` variants.
126− These are more memory efficient
127− (especially, if you anyway will convert to some vector)
128− https://github.com/phadej/1
129−
130− * Documentation improvements
131− https://github.com/lpsmith/postgresql-simple/pull/227
132− https://github.com/lpsmith/postgresql-simple/pull/236
133−
134−### Version 0.5.4.0 (2018-05-23)
135− * Support GHC-8.4 (Semigroup/Monoid)
136−
137−### Version 0.5.3.0 (2017-05-15)
138− * Refactored some rudimentary cursor handling code out of the
139− implementation of the fold operators, into a new
140− `Database.PostgreSQL.Simple.Cursor` module, thanks to Bardur Arantsson.
141−
142− * Made the `FromField` instance for `Char` compatible with
143− postgresql's `bpchar` type. Thanks to Ivan Lazar Miljenovic for
144− reporting the issue.
145−
146− * Added `Show` and `Eq` instances for `Notification`, thanks to
147− Matvey Aksenov.
148−
149− * Fixed some example code, thanks to Matvey Aksenov.
150−
151− * Fixed a problem with using `conversionError` to throw exceptions
152− of type `SomeException`. Previously, the exception would be
153− wrapped in a second `SomeException` dynamic constructor which
154− would cause normal GHC typecase idioms over `SomeException` to fail.
155−
156−### Version 0.5.2.1 (2016-06-29)
157− * Bumped the lower bound for `base` to 4.6. Thanks to Herbert
158− Valerio Riedel for reporting the issue.
159−
160− * Added an `Eq` instance for `SqlError`, thanks to Chris Allen
161−
162− * Fixed a bug where a all-caps `"NULL"` text value inside a
163− postgresql array would get parsed as the SQL null value. Thanks
164− goes to Edgar Gomes and Silk for finding and fixing this mistake.
165−
166− * Modified `withTransaction` and friends to ignore `IOError`s when
167− attempting to roll back the transaction. This fixes a buggy
168− interaction between `withTransaction` and async exceptions (e.g.
169− `System.Timeout`) on unix platforms. Thanks goes to Erik
170− Hesselink and Silk for providing the test case that exposed this
171− issue.
172−
173− * Added the `testTimeout` regression test for the problem above.
174−
175−### Version 0.5.2.0 (2016-05-25)
176− * Significantly improved the error reporting from
177− `Copy.putCopyData`, thanks to Ben Gamari.
178−
179− * Moved the test suite to use `tasty`, with a big thanks
180− to Ben Gamari.
181−
182− * Added `FromField.optionalField`, and updated the documentation
183− of `FromField.fromJSONField`, as inspired by an email conversation
184− with Ian Wagner.
185−
186− * Updated all links in the haddocks to use https, and added a link
187− to the documentation of `connectPostgreSQL`.
188−
189− * Added a truncated changelog to the source distribution.
190−
191−### Version 0.5.1.3 (2016-04-30)
192− * Implemented the Monad of No Return proposal, future-proofing
193− postgresql-simple against future releases of GHC.
194−
195− * Fixed a rare and usually benign race condition where
196− `getNotification` could end up waiting on a newly reallocated
197− file descriptor index, potentially leading to deadlock if the
198− descriptor does not become readable promptly. This fix only
199− applies to GHC 7.8 or later, as it depends on `threadWaitReadSTM`.
200−
201− * Tweaked the time parsers to accept times of day of the form `hh:mm`,
202− omitting seconds, following changes made to aeson.
203−
204− * Updated the documentation of the `In` type to point out a gotcha
205− when using the SQL fragment `... NOT IN ?` with `In []`. Thanks
206− goes to Simon Michael and Dan Haraj for bringing this issue to
207− my attention.
208−
209−### Version 0.5.1.2 (2015-12-14)
210− * The syntax generated for empty arrays was changed so that
211− postgresql's type inference would work better, thanks to
212− Amit Levy.
213−
214− * Further revision and expansion of the new Time documentation.
215−
216−### Version 0.5.1.1 (2015-12-02)
217− * This is a documentation-only release
218−
219− * The documentation of the `Time` module has been completely
220− rewritten, and is far longer and more informative. It contains
221− a brief overview of civil time, the semantics of postgresql's
222− time types, and their relation to Haskell's time types via
223− postgresql-simple.
224−
225− * The documentation of `connectPostgreSQL` has been modified to
226− mention the effects of environment variables on the connection
227− string parameters.
228−
229− * The documentation of `HStore.Internal` has been unhidden from
230− haddock.
231−
232− * A typo in example code was fixed courtesy of Levi Notik.
233−
234−### Version 0.5.1.0 (2015-10-22)
235− * Optimized the implementation of the streaming operators to avoid
236− creating intermediate lists of rows, thanks to Timo von Holtz.
237−
238− * Added default instances for `ToRow` and `FromRow` that depend on
239− Generics, thanks to Alexey Khudyakov.
240−
241− * Fixed support for bytestring-0.9 and GHC 7.4.
242−
243−### Version 0.5.0.1 (2015-09-21)
244− * Fixed a bug when printing a `ZonedTime` with a negative offset
245− that is not a whole number of hours.
246−
247−### Version 0.5.0.0 (2015-09-19)
248− * Removed the deprecated `BuiltinTypes` module.
249−
250− * Modified the SQL quasiquoter so that it returns a `Query`, not
251− an overloaded string, and so that the `OverloadedStrings` language
252− extension is no longer necessary, thanks to Mike Ledger.
253−
254− * Moved away from `blaze-builder` in favor of `bytestring-builder`. This
255− shouldn't affect very many people, but does reduce the transitive
256− dependencies.
257−
258− * Rewrote the timestamp printers to use the new `Prim`
259− infrastructure in `bytestring-builder`. The new printers should
260− be a fair bit faster.
261−
262− * Added support for exclusion violations to the `ConstraintViolation`
263− type in the Errors module, thanks to João Cristóvão.
264−
265− * Moved away from the `uuid` package in favor of the `uuid-types` package,
266− thanks to Bardur Arantsson. This shouldn't affect anybody, but does
267− reduce the transitive dependencies.
268−
269− * Postgresql-simple now explicitly assumes the UTF8 character encoding
270− for communication between the client and server. All database encodings
271− support UTF8 except for Mule Internal Code, the Multilingual
272− Extensions for Emacs. An exception should be raised upon
273− connecting to a database by the backend if the backend cannot
274− accommodate this requirement.
275−
276− * Added `Eq` and `Typeable` instances for `Connection`.
277−
278− * Added the `foldWith`, `forEachWith`, and `returningWith` families
279− of functions courtesy of Travis Staton.
280−
281− * Support for Ranged types, with thanks to Leonid Onokhov for his
282− contributions.
283−
284− * The `FromField` instance for JSON now allows for top-level values that
285− are not objects or arrays, thanks to Sam Rijs.
286−
287− * The timestamp parsers have been replaced with those now in Aeson.
288− Janne Hellsten adapted the old parsers from postgresql-simple for
289− inclusion in Aeson; Bryan O'Sullivan rewrote those parsers to be
290− faster, with some tweaks contributed by myself. And now to
291− bring the effort full circle, the result has been brought back to
292− postgresql-simple, with some adaptations.
293−
294− * Fixed a bug in the typeinfo system where postgresql's `_record` type
295− was being reported as a basic type and not an array type. Thanks to
296− Nickolay Kolev for helping to expose this issue.
297−
298− * Fixed a bug with the `typeInfo` operator, thanks to Timmy Tofu. In the
299− case of parsing subfields of arrays and composites, it would fetch the
300− `TypeInfo` of the array or composite type and not the subtype.
301−
302−### Version 0.4.10.0 (2015-02-26)
303− * Added a blurb about SSL/TLS in the documentation for connectPostgreSQL
304−
305− * Moved some functions into the Internal module, courtesy of Aleksey
306− Uimanov.
307−
308−### Version 0.4.9.0 (2014-12-27)
309− * Made the fromField method for PGArray available as pgArrayFieldParser,
310− outside of the typeclass courtesy of Tom Ellis.
311−
312− * Fixed a missing OverloadedStrings pragma in the documentation of SqlQQ.
313−
314− * Fixed deprecation warnings, courtesy of Simon Hengel.
315−
316−### Version 0.4.8.0 (2014-11-24)
317− * Added support for postgresql's citext type extension via the
318− case-insensitive package.
319−
320− * Added the function parseHStoreList to the HStore module.
321−
322−### Version 0.4.7.0 (2014-10-27)
323− * Added support for very old timestamps to UTCTime. Depending on time
324− zone, very old timestamps can be returned with a offset from UTC that
325− contains seconds.
326−
327− All timezones in the TZ database moved to a time standard offset an
328− integer number of minutes from UTC before 1973, almost all locations
329− moved before 1938, and a solid majority moved before 1921.
330−
331− ZonedTime assumes offsets are a whole number of minutes, and thus the
332− conversion to ZonedTime does not support these timestamps and will
333− still throw a conversion error.
334−
335− Note that PostgreSQL's "timestamp with time zone" (or "timestamptz")
336− type is nearly universally misunderstood. For an explanation, see:
337−
338− https://github.com/lpsmith/postgresql-simple/issues/69
339−
340− Thanks to Michael Snoyman for his assistance with this issue.
341−
342−### Version 0.4.6.0 (2014-10-07)
343− * Added an instance ToField NominalDiffTime.
344−
345−### Version 0.4.5.0 (2014-09-26)
346− * Added support for retrieving NaN and ±Infinity floating point values
347− from postgres to the FromField instances for Float, Double, and Rational.
348− The instance for Scientific is unchanged due to the fact it has no
349− way of representing these special values. Thanks goes to Tom Nielsen
350− for reporting the issue.
351−
352−### Version 0.4.4.1 (2014-09-07)
353− * Fixed a rather serious bug that prevented the COPY module from working
354− at all on unix clients since version 0.4.3.0. Thanks goes to
355− Dmitry Dzhus for reporting the issue.
356−
357− * Added a regression test for the COPY module to the test suite.
358−
359−### Version 0.4.4.0 (2014-08-26)
360− * Added the jsonb type debuting in PostgreSQL 9.4 to the TypeInfo.Static
361− and Builtin tables, and extended the out-of-box json instances to
362− be compatible with the new type. Thanks to Tobias Florek for the
363− patch.
364−
365− * Ported some expanded documentation from mysql-simple, and fixed
366− a documentation typo.
367−
368−### Version 0.4.3.0 (2014-07-10)
369− * connect and exec now use libpq asynchronously on non-Windows platforms.
370− This means we are using threadWaitRead and threadWaitWrite to
371− have GHC's IO manager schedule non-blocking C calls to libpq,
372− instead of using blocking C calls and having the OS kernel do the
373− scheduling. Among other things, this now means that System.Timeout
374− will work with connect and exec on unix platforms.
375−
376− * connect and exec now throw IOErrors instead of SQLErrors in
377− some cases. The intention is for SQLErrors to represent
378− an error returned by the server, and to use IOErrors for errors
379− that originate from client-side code. However, this goal
380− isn't perfectly achieved as of yet.
381−
382−### Version 0.4.2.3 (2014-06-04)
383− * This is strictly a documentation release, with no code changes.
384−
385− * Fixed several documentation typos, thanks to Chris Allen and remdezx.
386−
387− * Expanded the documentation of connectPostgreSQL, including a short
388− overview of common use cases and two new links to the official Postgres
389− documentation about the authentication process.
390−
391− * De-emphasized connect and ConnectInfo in favor of connectPostgreSQL.
392−
393−### Version 0.4.2.2 (2014-05-15)
394− * Fixed compatibility with scientific-0.3.\*, thanks to Adam Bergmark
395−
396− * Improved documentation of the FromField module, as well as the fold,
397− foldWithOptions, executeMany, and returning operators.
398−
399−### Version 0.4.2.1 (2014-03-27)
400− * Fixed bug in Values syntax generation
401−
402− * Improved documentation, including examples of multi-row update,
403− a better example for Values, documenting the inaccuracies in reading
404− floating point numbers from the database, and the IsString instance
405− for QualifiedIdentifier.
406−
407−### Version 0.4.2.0 (2014-03-22)
408− * Added ToField and FromField instances for the scientific package
409−
410− * Changed the Identifier and QualifiedIdentifier to use Text in
411− order to avoid encoding errors. Technically this requires a
412− major version bump, but let's pretend 0.4.1.0 didn't happen.
413−
414− * Removed non-exhaustive cases in the ToField instance for Values,
415− and tweaked error messages.
416−
417−### Version 0.4.1.0 (2014-03-22)
418− * Fixed the parsing of arrays containing null values, courtesy of
419− Francesco Mazzoli
420−
421− * Added support for properly escaped identifiers, courtesy of
422− Tobias Florek. See the new Identifier and QualifiedIdentifier
423− types inside Database.PostgreSQL.Simple.Types.
424−
425− * Added support for parameterized VALUES expressions. This is
426− more general than executeMany and returning. See the
427− Database.PostgreSQL.Simple.Types.Values data type.
428−
429−### Version 0.4.0.2 (2014-01-12)
430− * Tweaked C preprocessor directive to be more portable
431−
432− * Tweaked testsuite for compatibility with aeson-0.7
433−
434−### Version 0.4.0.1 (2013-12-21)
435− * Relaxed dependency on aeson to >= 0.6
436−
437− * Update the documentation of `fromField`
438−
439−### Version 0.4.0.0 (2013-12-21)
440− * Changed the calling code of `fromField` so that it always sends
441− a copy of the raw data. This should be a small but significant
442− performance bump for most people most of the time; however it
443− may slow down retrieval of large values not converted directly
444− to ByteString, such as large json, hstore, and array values.
445− See commit 8635f8 for more information.
446−
447− * Added the PGArray type. Thanks to Joey Adams for the suggestion
448−
449− * Fixed JSON decoding, which was almost entirely broken up until now,
450− due to bugs in the version of aeson currently available on Hackage.
451− Thanks to Amit Levy for the report.
452−
453− * Added FromField instances for IORef, MVar, and IOVector.
454−
455−### Version 0.3.10.0 (2013-12-17)
456− * Added the queryWith function, courtesy of Leonid Onokhov
457−
458− * Added the Default type, for representing postgresql's default values
459−
460−### Version 0.3.9.1 (2013-10-28)
461− * Removed dependency on hashable
462−
463−### Version 0.3.9.0 (2013-10-27)
464− * Added FromField and ToField instances for the `uuid` package,
465− courtesy of Bas van Dijk.
466−
467− * Added instance FromRow (Maybe a) for most pre-packaged FromRow
468− instances. See issue #64 for some discussion.
469−
470− * Added the fromBinary, fromHStoreMap, and fromHStoreList newtype
471− unwrapper functions, courtesy of Bas van Dijk.
472−
473−### Version 0.3.8.0 (2013-10-11)
474− * Fixed the example code in `FromField`, thanks to Adam Bergmark.
475−
476− * Added `Notification.getBackendPID`.
477−
478−### Version 0.3.7.1 (2013-09-12)
479− * Relaxed the dependency on bytestring-0.10 back to bytestring-0.9,
480− courtesy of Michael Snoyman
481−
482−### Version 0.3.7.0 (2013-09-11)
483− * Added `aeson` as a dependency.
484−
485− * Added ToField and FromField instances for aeson's JSON.Value type,
486− courtesy of Bas van Dijk.
487−
488− * Added toJSONField and fromJSONField helper functions for declaring
489− FromField/ToField JSON instances to other Haskell types, courtesy
490− of Bas van Dijk.
491−
492− * Added a FromField instance for (), corresponding to postgresql's void
493− type.
494−
495− * Added liftConversion and liftRowParser functions to the Internal
496− module, for lifting IO actions into the respective monads.
497−
498− * The SqlError predicates available in the Transaction module are now
499− also exported from the Errors module.
500−
501− * Various documentation fixes.
502−
503−### Version 0.3.6.0 (2013-08-19)
504− * Added the json type to BuiltinTypes and TypeInfo.Static, courtesy of
505− Manuel Gómez.
506−
507− * Removed the remaining internal dependencies on BuiltinTypes from
508− FromField. Added the TypeInfo.Macro module as a result.
509−
510− * Deprecated the BuiltinTypes module, which will be removed several
511− months from now. Fixed the example code in FromField to reflect
512− this change.
513−
514−### Version 0.3.5.0 (2013-08-09)
515− * Added an FromRow instance for Vector, semantically identical to the
516− existing FromRow instance for [], courtesy of Doug Beardsley
517−
518− * Reworked the documentation for the Copy module, and tweaked the
519− documentation for the LargeObjects module.
520−
521−### Version 0.3.4.0 (2013-07-23)
522− * Added direct support for COPY IN and COPY OUT, without having
523− to use raw postgresql-libpq calls and postgresql-simple's Internal
524− module.
525−
526− * Changed `getNotification` so that it throws a IOError (resource vanished)
527− exception instead of an ErrorCall exception when it fails to fetch
528− the connection's file descriptor from libpq.
529−
530−### Version 0.3.3.2 (2013-06-18)
531− * Optimized the definition of `mconcat` in the Monoid instance for
532− the Query type, courtesy of Joey Adams.
533−
534−### Version 0.3.3.1 (2013-06-06)
535− * `getNotification` now works on Windows, albeit using a one-second
536− polling loop, courtesy of Joey Adams.
537−
538−### Version 0.3.3.0 (2013-05-29)
539− * Fixed two issues with the fold operator: fold would raise the wrong
540− exception, and gave the database cursor a static name preventing
541− folds from being nested. Thanks to Joey Adams for his
542− work on these issues.
543−
544−### Version 0.3.2.0 (2013-05-20)
545− * Added a savepoint abstraction to the Transaction module, courtesy
546− of Joey Adams
547−
548−### Version 0.3.1.2 (2013-04-29)
549− * Fixed hstore parser to not unnecessarily reverse the key-value pairs
550−
551−### Version 0.3.1.1 (2013-04-29)
552− * Fixed hstore parser to recognize empty hstores, courtesy of Simon
553− Meier
554−
555−### Version 0.3.1.0 (2013-04-26)
556− * Added support for Range and Composite types to the TypeInfo system.
557−
558− * Added support for hstore types in the Simple.HStore module.
559−
560− * Improved documentation of the FromField module.
561−
562−### Version 0.3.0.1 (2013-03-26)
563− * A large chunk of the documentation inside the FromField module had
564− silently failed to render in Haddock.
565−
566−### Version 0.3.0.0 (2013-03-25)
567− * Added support for PostgreSQL's Array Types. Thanks to Jason Dusek
568− for his work on this feature.
569−
570− * Added a brand new TypeInfo system that gives FromField instances
571− convenient and efficient access to the pg_type metatable. This
572− replaced the older typename cache, and was necessary to properly
573− support postgres array types. Thanks to Bas van Dijk for his
574− work on this feature.
575−
576− * Changed the type of the `fromField` and `fromRow` methods to allow
577− a restricted set of IO actions, and stopped pre-calculating the type
578− name of every column. As a result, the type of the `typename`
579− operator changed from `Field -> ByteString` to
580− `Field -> Conversion ByteString`, where Conversion is the new monad
581− that conversion computations run inside.
582−
583− * Improved the documentation of the FromField module.
584−
585− * Added the Database.PostgreSQL.Simple.Errors module, which offers
586− some predicates and functions for interpreting SqlError values, courtesy
587− of Leonid Onokhov.
588−
589− * Added a the name of a column and the associated table's object identifier
590− to ResultError exceptions, courtesy of Jeff Chu.
591−
592− * Moved most of the more detailed transaction operators into the
593− Database.PostgreSQL.Simple.Transaction module.
594−
595− * Changed withTransactionModeRetry to accept a predicate of which
596− SqlErrors to retry, due to the fact that serialization errors can
597− sometimes manifest themselves as constraint violations. Thanks
598− to Oliver Charles for pointing this out and implementing the change.
599−
600− * Added simple tests of the fold operator, thanks to Joey Adams.
601−
602− * Added simple tests of the array conversion code.
603−
604− * Added recognition of -- comments in the quasiquoter, which are
605− now stripped out.
606−
607−
608−### Version 0.2.4.1 (2012-08-29)
609− * Fixed the documentation of `In`. Thanks to rekado and dstcruz for
610− pointing this out.
611−
612−### Version 0.2.4.0 (2012-08-23)
613− * Added the `withTransactionSerializable` and `withTransactionModeRetry`
614− operators, thanks to Joey Adams.
615−
616−### Version 0.2.3.0 (2012-08-09)
617− * Added the `returning` operator, thanks to Jason Dusek
618−
619−### Version 0.2.2.0 (2012-07-26)
620− * Added a ToRow instance for the (:.) type, courtesy of Leonid Onokhov
621−
622− * Added the type oid for PostgreSQL's `uuid` type to BuiltinTypes
623−
624−### Version 0.2.1.0 (2012-07-23)
625− * Added the FromRow.fieldWith operator, thanks to Leonid Onokhov
626−
627− * Added a type synonym for FieldParser
628−
629−### Version 0.2.0.1 (2012-06-21)
630− * Fixed a compatibility problem with PostgreSQL 8.1, which does not allow
631− clients to set their own value for `standard_conforming_strings`. This
632− connection variable is still set to `on` for PostgreSQL 8.2 and later.
633−
634−### Version 0.2: (2012-06-19)
635− * Removed the conversion from `timestamp` to `UTCTime`. Some code will be
636− broken even though it will still compile.
637−
638− * Renamed a number of data constructors, mostly in the BuiltinTypes module.
639−
640− * Exported ToRow/FromRow from Database.PostgreSQL.Simple
641−
642−
643−### Version 0.1.4.3: (2012-06-10)
644− * Fix language extensions for compatibility with GHC 7.0
645−
646−### Version 0.1.4.2: (2012-06-10)
647− * Fix a wayward dependency on Text.
648−
649−### Version 0.1.4.1: (2012-06-10)
650− * Added support for timezones with minutes in their UTC offset.
651−
652−### Version 0.1.4: (2012-06-10)
653− * Removed pcre-light dependency, courtesy of Joey Adams.
654−
655− * Reworked support for the Time types.
656−
657− * The conversion from PostgreSQL's `timestamp` (without time zone) type
658− to Haskell's `UTCTime` type is deprecated and will be removed in 0.2.
659−
660− * `Data.Time.LocalTime` now has `FromField`/`ToField` instances. It is
661− now the preferred way of dealing with `timestamp` (without time zone).
662−
663− * `Database.PostgreSQL.Simple.Time` is a new module that offers types
664− that accommodate PostgreSQL's infinities.
665−
666− * All time-related `FromField`/`ToField` instances are now based on new,
667− higher-speed parsers and printers instead of those provided by the
668− time package included in GHC.
669−
670− * Planned breaking changes for 0.2:
671−
672− * Removing the conversion from `timestamp` to `UTCTime`.
673−
674− * Renaming some of the type names in `BuiltinTypes`.
675−
676−
677−### Version 0.1.3: (2012-05-30)
678− * Made ZonedTime an instance of FromField and ToField
679−
680− * Added getNotificationNonBlocking
681−
682−
683−### Version 0.1.2: (2012-05-09)
684− * Switched to libpq-based escaping for bytea types; Binary now works with
685− PostgreSQL 8 courtesy of Joey Adams.
686−
687− * postgresql-simple now sets standard_conforming_strings to "on". This
688− per-connection variable is initialized according to the server
689− configuration, which defaults to "off" for PostgreSQL < 9, and "on"
690− for PostgreSQL >= 9. You may need to adjust any string literals in
691− your SQL queries, or set the variable yourself.
692−
693− * Exported (:.) from Database.PostgreSQL.Simple
694−
695−
696−### Version 0.1.1: (2012-05-06)
697− * Added some preliminary documentation for the Ok, Notification, and
698− LargeObjects modules
699−
700− * Implemented the `fail` method for the monad instance for `Ok`.
701−
702− * Fixed a bug relating to handling the transaction level
703−
704−
705−### Version 0.1: (2012-05-04)
706− * Renamed several modules, typeclasses, and functions:
707−
708− QueryParams (renderParams) -> ToRow (toRow)
709− QueryResults (convertResults) -> FromRow (fromRow)
710− Param (render) -> ToField (toField)
711− Result (convert) -> FromField (fromField)
712−
713− * Added the `Database.PostgreSQL.Simple.Ok` module, a variation of
714− `Either SomeException` that has an instance for `Alternative` and also
715− uses a list of exceptions to track the ways it has failed.
716−
717− * Changed the return type of `fromField` and `fromRow` from
718− `Either SomeException` to `Ok`.
719−
720− * Thanks to suggestions from Ozgun Ataman, the `FromRow` typeclass has been
721− massively improved. The result is simpler definitions and better
722− compositionality. Also, user-defined instances need not be to be
723− concerned about forcing the converted results to WHNF. Here is an
724− example comparing the old to the new:
725−
726− instance (Result a, Result b) => QueryResults (a,b) where
727− convertResults [fa,fb] [va,vb] = do
728− !a <- convert fa va
729− !b <- convert fb vb
730− return (a,b)
731− convertResults fs vs = convertError fs vs 2
732−
733− instance (FromField a, FromField b) => FromRow (a,b) where
734− fromRow = (,) <$> field <*> field
735−
736− * Added `(:.)`, a pair that allows one to compose `FromRow` instances:
737−
738− instance (FromRow a, FromRow b) => FromRow (a :. b) where
739− fromRow = (:.) <$> fromRow <*> fromRow
740−
741− * Moved the contents `Field` module into the `FromField` module.
742−
743− * Removed the `RawResult` type.
744−
745− * Added `DefaultIsolationLevel` as a distinct `IsolationLevel` option
746− and `DefaultReadWriteMode` as a distinct `ReadWriteMode`.
CONTRIBUTING.mddeleted−13
@@ -1,13 +0,0 @@
1−Patches welcome!
2−
3−- If you are only going to bump bounds:
4− - 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.
5− - 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**:
6− - Amend `tested-with` to include that GHC
7− - Regenerate `.github/workflows/haskell-ci.yml` with `haskell-ci regenerate` (get the latest from [GitHub haskell-ci/haskell-ci](https://github.com/haskell-ci/haskell-ci))
8−
9−- 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.
10−- For the same reason, do not edit `version` or `x-revision`
11−
12−- I use [`stylish-haskell`](https://github.com/jaspervdj/stylish-haskell) to format imports. I encourage you to use it too, when contributing.
13−- General code style is 4 spaces, just look around how it looks, it's not so strict.
CONTRIBUTORSdeleted−36
@@ -1,36 +0,0 @@
1−Bryan O'Sullivan <bos@serpentine.com>
2−Leon P Smith <leon@melding-monads.com>
3−Felipe Lessa <felipe.lessa@gmail.com>
4−Ozgun Ataman <ozataman@gmail.com>
5−Joey Adams <joeyadams3.14159@gmail.com>
6−Rekado <rekado@elephly.net>
7−Leonid Onokhov <sopvop@gmail.com>
8−Bas van Dijk <v.dijk.bas@gmail.com>
9−Jason Dusek <jason.dusek@gmail.com>
10−Jeff Chu <jeff@kiteedu.com>
11−Oliver Charles <oliver.g.charles@gmail.com>
12−Simon Meier <iridcode@gmail.com>
13−Alexey Uimanov <s9gf4ult@gmail.com>
14−Doug Beardsley <mightybyte@gmail.com>
15−Manuel Gómez <targen@gmail.com>
16−Michael Snoyman <michael@snoyman.com>
17−Adam Bergmark <adam@edea.se>
18−Tobias Florek <tob@butter.sh>
19−Francesco Mazzoli <francesco.mazzoli@erudify.com>
20−Chris Allen <cma@bitemyapp.com>
21−Simon Hengel <sol@typeful.net>
22−Tom Ellis <tom-2013@purelyagile.com>
23−Mike Ledger <mike@quasimal.com>
24−João Cristóvão <jmacristovao@gmail.com>
25−Bardur Arantsson <bardur@scientician.net>
26−Travis Staton <hello@travisstaton.com>
27−Sam Rijs <srijs@airpost.net>
28−Janne Hellsten <jjhellst@gmail.com>
29−Timmy Tofu <timmytofu@users.noreply.github.com>
30−Alexey Khudyakov <alexey.skladnoy@gmail.com>
31−Timo von Holtz <tvh@anchor.com.au>
32−Amit Levy <amit@amitlevy.com>
33−Ben Gamari <ben@smart-cactus.org>
34−Edgar Gomes Araujo <talktoedgar@gmail.com>
35−Erik Hesselink <hesselink@gmail.com>
36−Matvey Aksenov <matvey.aksenov@gmail.com>
README.mddeleted−4
@@ -1,4 +0,0 @@
1−[![Hackage page (downloads and API reference)][hackage-png]][hackage]
2−
3− [hackage-png]: http://img.shields.io/hackage/v/postgresql-simple.svg
4− [hackage]: http://hackage.haskell.org/package/postgresql-simple
bench/Select.hsdeleted−32
@@ -1,32 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−module Main where
3−
4−import Database.PostgreSQL.Simple
5−import qualified Database.PostgreSQL.Simple.Vector as V
6−import qualified Database.PostgreSQL.Simple.Vector.Unboxed as VU
7−
8−import System.Environment (getArgs)
9−import Data.Foldable (Foldable, foldl')
10−import qualified Data.Vector.Unboxed as VU
11−
12−main :: IO ()
13−main = do
14− args <- getArgs
15− conn <- connectPostgreSQL ""
16− case args of
17− ("vector":_) -> do
18− result <- V.query_ conn "SELECT * FROM generate_series(1, 10000000);"
19− print (process result)
20− ("unboxed":_) -> do
21− -- dummy column
22− result <- VU.query_ conn "SELECT (NULL :: VOID), * FROM generate_series(1, 10000000);"
23− print (process' result)
24− _ -> do
25− result <- query_ conn "SELECT * FROM generate_series(1, 10000000);"
26− print (process result)
27−
28−process :: Foldable f => f (Only Int) -> Int
29−process = foldl' (\x (Only y) -> max x y) 0
30−
31−process' :: VU.Vector ((), Int) -> Int
32−process' = VU.foldl' (\x (_, y) -> max x y) 0
cabal.haskell-cideleted−5
@@ -1,5 +0,0 @@
1−branches: master
2−postgresql: True
3−
4−-- services aren't supported on Travis on macOS
5−-- osx: 8.4.4
cabal.projectdeleted−4
@@ -1,4 +0,0 @@
1−packages: .
2−tests: true
3−
4−allow-newer: cryptohash-md5-0.11.100.1:base
cabal.project.local.windeleted−4
@@ -1,4 +0,0 @@
1−constraints: these -assoc
2−
3−package postgresql-libpq
4− flags: -use-pkg-config
postgresql-simple.cabal+56−169
@@ -1,175 +1,62 @@
1−cabal-version: 1.12
2−name: postgresql-simple
3−version: 0.7.0.0
4−x-revision: 1
5−synopsis: Mid-Level PostgreSQL client library
6−description:
7− Mid-Level PostgreSQL client library, forked from mysql-simple.
8−
9−license: BSD3
10−license-file: LICENSE
11−author: Bryan O'Sullivan, Leon P Smith
12−maintainer: Oleg Grenrus <oleg.grenrus@iki.fi>
13−copyright:
14− (c) 2011 MailRank, Inc.
15− (c) 2011-2018 Leon P Smith
16− (c) 2018-2020 Oleg Grenrus
17−
18−category: Database
19−build-type: Simple
20−extra-source-files:
21− CHANGES.md
22− CONTRIBUTORS
23− test/results/malformed-input.expected
24− test/results/unique-constraint-violation.expected
25−
26−tested-with:
27− GHC ==8.6.5
28− || ==8.8.4
29− || ==8.10.7
30− || ==9.0.2
31− || ==9.2.8
32− || ==9.4.8
33− || ==9.6.3
34− || ==9.8.1
35−
36−library
37− default-language: Haskell2010
38− hs-source-dirs: src
39− exposed-modules:
40− Database.PostgreSQL.Simple
41− Database.PostgreSQL.Simple.Arrays
42− Database.PostgreSQL.Simple.Copy
43− Database.PostgreSQL.Simple.Cursor
44− Database.PostgreSQL.Simple.Errors
45− Database.PostgreSQL.Simple.FromField
46− Database.PostgreSQL.Simple.FromRow
47− Database.PostgreSQL.Simple.HStore
48− Database.PostgreSQL.Simple.HStore.Internal
49− Database.PostgreSQL.Simple.Internal
50− Database.PostgreSQL.Simple.LargeObjects
51− Database.PostgreSQL.Simple.Newtypes
52− Database.PostgreSQL.Simple.Notification
53− Database.PostgreSQL.Simple.Ok
54− Database.PostgreSQL.Simple.Range
55− Database.PostgreSQL.Simple.SqlQQ
56− Database.PostgreSQL.Simple.Time
57− Database.PostgreSQL.Simple.Time.Internal
58− Database.PostgreSQL.Simple.ToField
59− Database.PostgreSQL.Simple.ToRow
60− Database.PostgreSQL.Simple.Transaction
61− Database.PostgreSQL.Simple.TypeInfo
62− Database.PostgreSQL.Simple.TypeInfo.Macro
63− Database.PostgreSQL.Simple.TypeInfo.Static
64− Database.PostgreSQL.Simple.Types
65− Database.PostgreSQL.Simple.Vector
66− Database.PostgreSQL.Simple.Vector.Unboxed
67−
1+Name: postgresql-simple
2+Version: 0.0
3+Synopsis: Mid-Level PostgreSQL client library
4+License: BSD3
5+License-file: LICENSE
6+Author: Bryan O'Sullivan, Leon P Smith
7+Maintainer: Leon P Smith <leon@melding-monads.com>
8+Copyright: (c) 2011 MailRank, Inc.
9+ (c) 2011 Leon P Smith
10+Category: Database
11+Build-type: Simple
12+
13+-- Extra files to be distributed with the package, such as examples or
14+-- a README.
15+-- Extra-source-files:
16+
17+-- Constraint on the version of Cabal needed to build this package.
18+Cabal-version: >=1.2
19+
20+Library
21+ hs-source-dirs: src
22+ -- Modules exported by the library.
23+ Exposed-modules:
24+ Database.PostgreSQL.Simple
25+ Database.PostgreSQL.Simple.Param
26+ Database.PostgreSQL.Simple.QueryParams
27+ Database.PostgreSQL.Simple.Result
28+ Database.PostgreSQL.Simple.Field
29+ Database.PostgreSQL.Simple.QueryResults
30+ Database.PostgreSQL.Simple.BuiltinTypes
31+ Database.PostgreSQL.Simple.Types
32+ Other-modules:
33+ Database.PostgreSQL.Simple.Implementation
34+
35+ Build-depends:
36+ attoparsec >= 0.8.5.3,
37+ base < 5,
38+ base16-bytestring,
39+ blaze-builder,
40+ blaze-textual,
41+ bytestring >= 0.9,
42+ containers,
43+ postgresql-libpq,
44+ pcre-light,
45+ old-locale,
46+ text >= 0.11.0.2,
47+ time,
48+ transformers >= 0.2.0.0
49+ -- Modules not exported by this package.
6850 -- Other-modules:
69− other-modules:
70− Database.PostgreSQL.Simple.Compat
71− Database.PostgreSQL.Simple.HStore.Implementation
72− Database.PostgreSQL.Simple.Internal.PQResultUtils
73− Database.PostgreSQL.Simple.Time.Implementation
74− Database.PostgreSQL.Simple.Time.Internal.Parser
75− Database.PostgreSQL.Simple.Time.Internal.Printer
76− Database.PostgreSQL.Simple.TypeInfo.Types
77−
78− -- GHC bundled libs
79− build-depends:
80− base >=4.12.0.0 && <4.20
81− , bytestring >=0.10.8.2 && <0.13
82− , containers >=0.6.0.1 && <0.7
83− , template-haskell >=2.14.0.0 && <2.22
84− , text >=1.2.3.0 && <1.3 || >=2.0 && <2.2
85− , time-compat >=1.9.5 && <1.12
86− , transformers >=0.5.6.2 && <0.7
87−
88− -- Other dependencies
89− build-depends:
90− aeson >=2.1.2.1 && <2.3
91− , attoparsec >=0.14.4 && <0.15
92− , case-insensitive >=1.2.1.0 && <1.3
93− , hashable >=1.4.3.0 && <1.5
94− , Only >=0.1 && <0.1.1
95− , postgresql-libpq >=0.10.0.0 && <0.11
96− , scientific >=0.3.7.0 && <0.4
97− , uuid-types >=1.0.5 && <1.1
98− , vector >=0.13.0.0 && <0.14
9951
100− default-extensions:
101− BangPatterns
102− DoAndIfThenElse
103− OverloadedStrings
104− TypeOperators
105− ViewPatterns
106−
107− ghc-options: -Wall -fno-warn-name-shadowing
52+ -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.
53+ -- Build-tools:
10854
10955source-repository head
11056 type: git
111− location: http://github.com/haskellari/postgresql-simple
112−
113−test-suite inspection
114− default-language: Haskell2010
115− type: exitcode-stdio-1.0
116− hs-source-dirs: test
117− main-is: Inspection.hs
118− build-depends:
119− base
120− , inspection-testing >=0.4.1.1 && <0.6
121− , postgresql-libpq
122− , postgresql-simple
123− , tasty
124− , tasty-hunit
125−
126−test-suite test
127− default-language: Haskell2010
128− type: exitcode-stdio-1.0
129− hs-source-dirs: test
130− main-is: Main.hs
131− other-modules:
132− Common
133− Exception
134− Interval
135− Notify
136− Serializable
137− Time
57+ location: http://github.com/lpsmith/postgresql-simple
13858
139− ghc-options: -threaded
140− ghc-options: -Wall -fno-warn-name-shadowing -fno-warn-unused-do-bind
141− default-extensions:
142− NamedFieldPuns
143− OverloadedStrings
144− PatternGuards
145− Rank2Types
146− RecordWildCards
147− ScopedTypeVariables
148−
149− build-depends:
150− aeson
151− , base
152− , base16-bytestring
153− , bytestring
154− , case-insensitive
155− , containers
156− , cryptohash-md5 >=0.11.100.1 && <0.12
157− , filepath
158− , HUnit
159− , postgresql-simple
160− , tasty
161− , tasty-golden
162− , tasty-hunit
163− , text
164− , time-compat
165− , vector
166−
167−benchmark select
168− default-language: Haskell2010
169− type: exitcode-stdio-1.0
170− hs-source-dirs: bench
171− main-is: Select.hs
172− build-depends:
173− base
174− , postgresql-simple
175− , vector
59+source-repository this
60+ type: git
61+ location: http://github.com/lpsmith/postgresql-simple
62+ tag: v0.0
sdist.shdeleted−25
@@ -1,25 +0,0 @@
1−#!/bin/bash
2−
3−echo "Generating CHANGELOG.md"
4−echo
5−
6−cat <<HEADER > CHANGELOG.md
7−For the full changelog, see
8−<https://github.com/lpsmith/postgresql-simple/blob/master/CHANGES.md>
9−
10−HEADER
11−
12−perl -ne '$n++ if /^###/; exit if $n==2; print' CHANGES.md >> CHANGELOG.md
13−
14−cat CHANGELOG.md
15−rm -f ./dist/*.tar
16−rm -f ./dist/*.tar.gz
17−cabal sdist
18−cd dist
19−wc *.tar.gz
20−gunzip *.gz
21−wc *.tar
22−zopfli *.tar
23−wc *.tar.gz
24−cd ..
25−rm -f ./dist/*.tar
src/Database/PostgreSQL/Simple.hs+330−495
@@ -1,23 +1,16 @@
1−{-# LANGUAGE DeriveDataTypeable #-}
2−{-# LANGUAGE NamedFieldPuns #-}
3−{-# LANGUAGE OverloadedStrings #-}
4−{-# LANGUAGE PatternGuards #-}
5−{-# LANGUAGE RecordWildCards #-}
6−{-# LANGUAGE ViewPatterns #-}
7−
8−------------------------------------------------------------------------------
1+{-# LANGUAGE BangPatterns, DeriveDataTypeable, OverloadedStrings, DoAndIfThenElse, RecordWildCards, NamedFieldPuns #-}
2+
93-- |
104-- Module: Database.PostgreSQL.Simple
115-- Copyright: (c) 2011 MailRank, Inc.
12−-- (c) 2011-2012 Leon P Smith
6+-- (c) 2011 Leon P Smith
137-- License: BSD3
148-- Maintainer: Leon P Smith <leon@melding-monads.com>
159-- Stability: experimental
10+-- Portability: portable
1611--
17−-- A mid-level client library for the PostgreSQL database, aimed at ease of
12+-- A mid-level client library for the MySQL database, aimed at ease of
1813-- use and high performance.
19−--
20−------------------------------------------------------------------------------
2114
2215module Database.PostgreSQL.Simple
2316 (
@@ -42,9 +35,6 @@ module Database.PostgreSQL.Simple
4235 -- ** Modifying multiple rows at once
4336 -- $many
4437
45− -- ** @RETURNING@: modifications that return results
46− -- $returning
47−
4838 -- * Extracting results
4939 -- $result
5040
@@ -55,54 +45,32 @@ module Database.PostgreSQL.Simple
5545 -- $types
5646
5747 -- * Types
58− Connection
48+ Base.ConnectInfo(..)
49+ , Connection
5950 , Query
60− , ToRow
61− , FromRow
6251 , In(..)
6352 , Binary(..)
6453 , Only(..)
65− , (:.)(..)
6654 -- ** Exceptions
67− , SomePostgreSqlException(..)
68− , SqlError(..)
69− , PQ.ExecStatus(..)
70− , FormatError(..)
71− , QueryError(..)
72− , ResultError(..)
55+ , FormatError(fmtMessage, fmtQuery, fmtParams)
56+ , QueryError(qeMessage, qeQuery)
57+ , ResultError(errSQLType, errHaskellType, errMessage)
7358 -- * Connection management
74− , Base.connectPostgreSQL
75− , Base.close
7659 , Base.connect
77− , Base.withConnect
78− , Base.ConnectInfo(..)
79− , Base.defaultConnectInfo
60+ , Base.connectPostgreSQL
8061 , Base.postgreSQLConnectionString
62+ , Base.defaultConnectInfo
63+ , Base.close
8164 -- * Queries that return results
8265 , query
8366 , query_
84− -- ** Queries taking parser as argument
85− , queryWith
86− , queryWith_
67+{--
8768 -- * Queries that stream results
88− , FoldOptions(..)
89− , FetchQuantity(..)
90− , defaultFoldOptions
9169 , fold
92− , foldWithOptions
9370 , fold_
94− , foldWithOptions_
9571 , forEach
9672 , forEach_
97− , returning
98− -- ** Queries that stream results taking a parser as an argument
99− , foldWith
100− , foldWithOptionsAndParser
101− , foldWith_
102− , foldWithOptionsAndParser_
103− , forEachWith
104− , forEachWith_
105− , returningWith
73+--}
10674 -- * Statements that do not return results
10775 , execute
10876 , execute_
@@ -110,7 +78,6 @@ module Database.PostgreSQL.Simple
11078-- , Base.insertID
11179 -- * Transaction handling
11280 , withTransaction
113− , withSavepoint
11481-- , Base.autocommit
11582 , begin
11683 , commit
@@ -120,27 +87,55 @@ module Database.PostgreSQL.Simple
12087 , formatQuery
12188 ) where
12289
123−import Data.ByteString.Builder (Builder, byteString, char8)
124−import Control.Applicative ((<$>))
125−import Control.Exception as E
126−import Data.ByteString (ByteString)
127−import Data.Int (Int64)
128−import Data.List (intersperse)
129−import Data.Monoid (mconcat)
130−import Database.PostgreSQL.Simple.Compat ((<>), toByteString)
131−import Database.PostgreSQL.Simple.Cursor
132−import Database.PostgreSQL.Simple.FromField (ResultError(..))
133−import Database.PostgreSQL.Simple.FromRow (FromRow(..))
134−import Database.PostgreSQL.Simple.ToField (Action(..))
135−import Database.PostgreSQL.Simple.ToRow (ToRow(..))
136−import Database.PostgreSQL.Simple.Types
137− ( Binary(..), In(..), Only(..), Query(..), (:.)(..) )
138−import Database.PostgreSQL.Simple.Internal as Base
139−import Database.PostgreSQL.Simple.Internal.PQResultUtils
140−import Database.PostgreSQL.Simple.Transaction
90+import Blaze.ByteString.Builder (Builder, fromByteString, toByteString)
91+import Blaze.ByteString.Builder.Char8 (fromChar)
92+import Control.Applicative ((<$>), pure)
93+import Control.Concurrent.MVar
94+import Control.Exception (Exception, bracket, onException, throw, throwIO)
95+import Control.Monad (forM)
96+import Control.Monad.Fix (fix)
97+import Data.ByteString (ByteString)
98+-- import qualified Data.ByteString as B (unpack)
99+import Data.Char(ord)
100+import Data.Int (Int64)
101+import qualified Data.IntMap as IntMap
102+import Data.List (intersperse)
103+import Data.Monoid (mappend, mconcat)
104+import Data.Typeable (Typeable)
105+--import Database.MySQL.Base (Connection, Result)
106+--import Database.MySQL.Base.Types (Field)
107+import Database.PostgreSQL.Simple.BuiltinTypes (oid2builtin, builtin2typname)
108+import Database.PostgreSQL.Simple.Param (Action(..), inQuotes)
109+import Database.PostgreSQL.Simple.QueryParams (QueryParams(..))
110+import Database.PostgreSQL.Simple.Result (ResultError(..))
111+import Database.PostgreSQL.Simple.QueryResults (QueryResults(..))
112+-- import Database.PostgreSQL.Simple.Result (ResultError(..))
113+import Database.PostgreSQL.Simple.Types (Binary(..), In(..), Only(..), Query(..))
114+import Database.PostgreSQL.Simple.Implementation as Base
141115import qualified Database.PostgreSQL.LibPQ as PQ
116+import Text.Regex.PCRE.Light (compile, caseless, match)
142117import qualified Data.ByteString.Char8 as B
118+--import qualified Database.MySQL.Base as Base
119+
120+-- | Exception thrown if a 'Query' could not be formatted correctly.
121+-- This may occur if the number of \'@?@\' characters in the query
122+-- string does not match the number of parameters provided.
123+data FormatError = FormatError {
124+ fmtMessage :: String
125+ , fmtQuery :: Query
126+ , fmtParams :: [ByteString]
127+ } deriving (Eq, Show, Typeable)
143128
129+instance Exception FormatError
130+
131+-- | Exception thrown if 'query' is used to perform an @INSERT@-like
132+-- operation, or 'execute' is used to perform a @SELECT@-like operation.
133+data QueryError = QueryError {
134+ qeMessage :: String
135+ , qeQuery :: Query
136+ } deriving (Eq, Show, Typeable)
137+
138+instance Exception QueryError
144139
145140-- | Format a query string.
146141--
@@ -152,11 +147,11 @@ import qualified Data.ByteString.Char8 as B
152147--
153148-- Throws 'FormatError' if the query string could not be formatted
154149-- correctly.
155−formatQuery :: ToRow q => Connection -> Query -> q -> IO ByteString
150+formatQuery :: QueryParams q => Connection -> Query -> q -> IO ByteString
156151formatQuery conn q@(Query template) qs
157152 | null xs && '?' `B.notElem` template = return template
158153 | otherwise = toByteString <$> buildQuery conn q template xs
159− where xs = toRow qs
154+ where xs = renderParams qs
160155
161156-- | Format a query string with a variable number of rows.
162157--
@@ -171,208 +166,106 @@ formatQuery conn q@(Query template) qs
171166--
172167-- Throws 'FormatError' if the query string could not be formatted
173168-- correctly.
174−formatMany :: (ToRow q) => Connection -> Query -> [q] -> IO ByteString
169+formatMany :: (QueryParams q) => Connection -> Query -> [q] -> IO ByteString
175170formatMany _ q [] = fmtError "no rows supplied" q []
176171formatMany conn q@(Query template) qs = do
177− case parseTemplate template of
178− Just (before, qbits, after) -> do
179− bs <- mapM (buildQuery conn q qbits . toRow) qs
180− return . toByteString . mconcat $ byteString before :
181− intersperse (char8 ',') bs ++
182− [byteString after]
183− Nothing -> fmtError "syntax error in multi-row template" q []
184−
185−-- Split the input string into three pieces, @before@, @qbits@, and @after@,
186−-- following this grammar:
187−--
188−-- start: ^ before qbits after $
189−-- before: ([^?]* [^?\w])? 'VALUES' \s*
190−-- qbits: '(' \s* '?' \s* (',' \s* '?' \s*)* ')'
191−-- after: [^?]*
192−--
193−-- \s: [ \t\n\r\f]
194−-- \w: [A-Z] | [a-z] | [\x80-\xFF] | '_' | '$' | [0-9]
195−--
196−-- This would be much more concise with some sort of regex engine.
197−-- 'formatMany' used to use pcre-light instead of this hand-written parser,
198−-- but pcre is a hassle to install on Windows.
199−parseTemplate :: ByteString -> Maybe (ByteString, ByteString, ByteString)
200−parseTemplate template =
201− -- Convert input string to uppercase, to facilitate searching.
202− search $ B.map toUpper_ascii template
172+ case match re template [] of
173+ Just [_,before,qbits,after] -> do
174+ bs <- mapM (buildQuery conn q qbits . renderParams) qs
175+ return . toByteString . mconcat $ fromByteString before :
176+ intersperse (fromChar ',') bs ++
177+ [fromByteString after]
178+ _ -> error "foo"
203179 where
204− -- Search for the next occurrence of "VALUES"
205− search bs =
206− case B.breakSubstring "VALUES" bs of
207− (x, y)
208− -- If "VALUES" is not present in the string, or any '?' characters
209− -- were encountered prior to it, fail.
210− | B.null y || ('?' `B.elem` x)
211− -> Nothing
212−
213− -- If "VALUES" is preceded by an identifier character (a.k.a. \w),
214− -- try the next occurrence.
215− | not (B.null x) && isIdent (B.last x)
216− -> search $ B.drop 6 y
217−
218− -- Otherwise, we have a legitimate "VALUES" token.
219− | otherwise
220− -> parseQueryBits $ skipSpace $ B.drop 6 y
221−
222− -- Parse '(' \s* '?' \s* . If this doesn't match
223− -- (and we don't consume a '?'), look for another "VALUES".
224− --
225− -- qb points to the open paren (if present), meaning it points to the
226− -- beginning of the "qbits" production described above. This is why we
227− -- pass it down to finishQueryBits.
228− parseQueryBits qb
229− | Just ('(', skipSpace -> bs1) <- B.uncons qb
230− , Just ('?', skipSpace -> bs2) <- B.uncons bs1
231− = finishQueryBits qb bs2
232− | otherwise
233− = search qb
234−
235− -- Parse (',' \s* '?' \s*)* ')' [^?]* .
236− --
237− -- Since we've already consumed at least one '?', there's no turning back.
238− -- The parse has to succeed here, or the whole thing fails
239− -- (because we don't allow '?' to appear outside of the VALUES list).
240− finishQueryBits qb bs0
241− | Just (')', bs1) <- B.uncons bs0
242− = if '?' `B.elem` bs1
243− then Nothing
244− else Just $ slice3 template qb bs1
245− | Just (',', skipSpace -> bs1) <- B.uncons bs0
246− , Just ('?', skipSpace -> bs2) <- B.uncons bs1
247− = finishQueryBits qb bs2
248− | otherwise
249− = Nothing
250−
251− -- Slice a string into three pieces, given the start offset of the second
252− -- and third pieces. Each "offset" is actually a tail of the uppercase
253− -- version of the template string. Its length is used to infer the offset.
254− --
255− -- It is important to note that we only slice the original template.
256− -- We don't want our all-caps trick messing up the actual query string.
257− slice3 source p1 p2 =
258− (s1, s2, source'')
259− where
260− (s1, source') = B.splitAt (B.length source - B.length p1) source
261− (s2, source'') = B.splitAt (B.length p1 - B.length p2) source'
262−
263− toUpper_ascii c | c >= 'a' && c <= 'z' = toEnum (fromEnum c - 32)
264− | otherwise = c
265−
266− -- Based on the definition of {ident_cont} in src/backend/parser/scan.l
267− -- in the PostgreSQL source. No need to check [a-z], since we converted
268− -- the whole string to uppercase.
269− isIdent c = (c >= '0' && c <= '9')
270− || (c >= 'A' && c <= 'Z')
271− || (c >= '\x80' && c <= '\xFF')
272− || c == '_'
273− || c == '$'
274−
275− -- Based on {space} in scan.l
276− isSpace_ascii c = (c == ' ') || (c >= '\t' && c <= '\r')
277−
278− skipSpace = B.dropWhile isSpace_ascii
180+ re = compile "^([^?]+\\bvalues\\s*)\
181+ \(\\(\\s*[?](?:\\s*,\\s*[?])*\\s*\\))\
182+ \([^?]*)$"
183+ [caseless]
279184
185+escapeStringConn conn s = withConnection conn $ \c -> do
186+ PQ.escapeStringConn c s
280187
281188buildQuery :: Connection -> Query -> ByteString -> [Action] -> IO Builder
282−buildQuery conn q template xs =
283− zipParams (split template) <$> mapM (buildAction conn q xs) xs
284− where split s =
285− -- This part escapes double '??'s to make literal '?'s possible
286− -- in PostgreSQL queries using the JSON operators: @?@, @?|@ and @?&@
287− let (h,t) = breakOnSingleQuestionMark s
288− in byteString h
289− : if B.null t
290− then []
291− else split (B.tail t)
292− zipParams (t:ts) (p:ps) = t <> p <> zipParams ts ps
189+buildQuery conn q template xs = zipParams (split template) <$> mapM sub xs
190+ where quote = inQuotes . fromByteString . maybe undefined id
191+ sub (Plain b) = pure b
192+ sub (Escape s) = quote <$> escapeStringConn conn s
193+ sub (Many ys) = mconcat <$> mapM sub ys
194+ split s = fromByteString h : if B.null t then [] else split (B.tail t)
195+ where (h,t) = B.break (=='?') s
196+ zipParams (t:ts) (p:ps) = t `mappend` p `mappend` zipParams ts ps
293197 zipParams [t] [] = t
294− zipParams _ _ = fmtError (show countSingleQs ++
295− " single '?' characters, but " ++
198+ zipParams _ _ = fmtError (show (B.count '?' template) ++
199+ " '?' characters, but " ++
296200 show (length xs) ++ " parameters") q xs
297− countSingleQs = go 0 template
298− where go i "" = (i :: Int)
299− go i bs = case qms of
300− ("?","?") -> go i nextQMBS
301− ("?",_) -> go (i+1) nextQMBS
302− _ -> i
303− where qms = B.splitAt 1 qmBS
304− (qmBS,nextQMBS) = B.splitAt 2 qmBS'
305− qmBS' = B.dropWhile (/= '?') bs
306201
307202-- | Execute an @INSERT@, @UPDATE@, or other SQL query that is not
308203-- expected to return results.
309204--
310205-- Returns the number of rows affected.
311206--
312−-- Throws 'FormatError' if the query could not be formatted correctly, or
313−-- a 'SqlError' exception if the backend returns an error.
314−execute :: (ToRow q) => Connection -> Query -> q -> IO Int64
207+-- Throws 'FormatError' if the query could not be formatted correctly.
208+execute :: (QueryParams q) => Connection -> Query -> q -> IO Int64
315209execute conn template qs = do
316210 result <- exec conn =<< formatQuery conn template qs
317211 finishExecute conn template result
318212
213+-- | A version of 'execute' that does not perform query substitution.
214+execute_ :: Connection -> Query -> IO Int64
215+execute_ conn q@(Query stmt) = do
216+ result <- exec conn stmt
217+ finishExecute conn q result
218+
319219-- | Execute a multi-row @INSERT@, @UPDATE@, or other SQL query that is not
320220-- expected to return results.
321221--
322−-- Returns the number of rows affected. If the list of parameters is empty,
323−-- this function will simply return 0 without issuing the query to the backend.
324−-- If this is not desired, consider using the 'Values' constructor instead.
325−--
326−-- Throws 'FormatError' if the query could not be formatted correctly, or
327−-- a 'SqlError' exception if the backend returns an error.
328−--
329−-- For example, here's a command that inserts two rows into a table
330−-- with two columns:
331−--
332−-- @
333−-- executeMany c [sql|
334−-- INSERT INTO sometable VALUES (?,?)
335−-- |] [(1, \"hello\"),(2, \"world\")]
336−-- @
337−--
338−-- Here's an canonical example of a multi-row update command:
222+-- Returns the number of rows affected.
339223--
340−-- @
341−-- executeMany c [sql|
342−-- UPDATE sometable
343−-- SET y = upd.y
344−-- FROM (VALUES (?,?)) as upd(x,y)
345−-- WHERE sometable.x = upd.x
346−-- |] [(1, \"hello\"),(2, \"world\")]
347−-- @
348−
349−executeMany :: (ToRow q) => Connection -> Query -> [q] -> IO Int64
224+-- Throws 'FormatError' if the query could not be formatted correctly.
225+executeMany :: (QueryParams q) => Connection -> Query -> [q] -> IO Int64
350226executeMany _ _ [] = return 0
351227executeMany conn q qs = do
352228 result <- exec conn =<< formatMany conn q qs
353229 finishExecute conn q result
354230
355−-- | Execute @INSERT ... RETURNING@, @UPDATE ... RETURNING@, or other SQL
356−-- query that accepts multi-row input and is expected to return results.
357−-- Note that it is possible to write
358−-- @'query' conn "INSERT ... RETURNING ..." ...@
359−-- in cases where you are only inserting a single row, and do not need
360−-- functionality analogous to 'executeMany'.
361−--
362−-- If the list of parameters is empty, this function will simply return @[]@
363−-- without issuing the query to the backend. If this is not desired,
364−-- consider using the 'Values' constructor instead.
365−--
366−-- Throws 'FormatError' if the query could not be formatted correctly.
367−returning :: (ToRow q, FromRow r) => Connection -> Query -> [q] -> IO [r]
368−returning = returningWith fromRow
231+finishExecute :: Connection -> Query -> PQ.Result -> IO Int64
232+finishExecute conn q result = do
233+ status <- PQ.resultStatus result
234+ case status of
235+ PQ.EmptyQuery -> fail "empty query" -- FIXME: error message
236+ PQ.CommandOk -> do
237+ ncols <- PQ.nfields result
238+ if ncols /= 0
239+ then throwIO $ QueryError ("execute resulted in " ++ show ncols ++
240+ "-column result") q
241+ else do
242+ nstr <- PQ.cmdTuples result
243+ return $ case nstr of
244+ Nothing -> 0 -- is this appropriate?
245+ Just str -> toInteger str
246+ PQ.TuplesOk -> do
247+ ncols <- PQ.nfields result
248+ throwIO $ QueryError ("execute resulted in " ++ show ncols ++
249+ "-column result") q
250+ PQ.CopyIn -> fail "FIXME: postgresql-simple does not currently support COPY IN"
251+ PQ.CopyOut -> fail "FIXME: postgresql-simple does not currently support COPY OUT"
252+ _ -> do
253+ errormsg <- maybe "" id <$> PQ.resultErrorMessage result
254+ statusmsg <- PQ.resStatus status
255+ state <- maybe "" id <$> PQ.resultErrorField result PQ.DiagSqlstate
256+ throw $ SqlError { sqlState = state
257+ , sqlNativeError = 0 -- FIXME
258+ , sqlErrorMsg = B.concat [ "execute: ", statusmsg
259+ , ": ", errormsg ]}
260+ where
261+ toInteger str = B.foldl' delta 0 str
262+ where
263+ delta acc c =
264+ if '0' <= c && c <= '9'
265+ then 10 * acc + fromIntegral (ord c - ord '0')
266+ else error ("finishExecute: not an int: " ++ B.unpack str)
267+
369268
370−-- | A version of 'returning' taking parser as argument
371−returningWith :: (ToRow q) => RowParser r -> Connection -> Query -> [q] -> IO [r]
372−returningWith _ _ _ [] = return []
373−returningWith parser conn q qs = do
374− result <- exec conn =<< formatMany conn q qs
375− finishQueryWith parser conn q result
376269
377270-- | Perform a @SELECT@ or other SQL query that is expected to return
378271-- results. All results are retrieved and converted before this
@@ -389,44 +282,33 @@ returningWith parser conn q qs = do
389282-- using 'execute' instead of 'query').
390283--
391284-- * 'ResultError': result conversion failed.
392−--
393−-- * 'SqlError': the postgresql backend returned an error, e.g.
394−-- a syntax or type error, or an incorrect table or column name.
395−query :: (ToRow q, FromRow r) => Connection -> Query -> q -> IO [r]
396−query = queryWith fromRow
397−
398−-- | A version of 'query' that does not perform query substitution.
399−query_ :: (FromRow r) => Connection -> Query -> IO [r]
400−query_ = queryWith_ fromRow
401−
402−-- | A version of 'query' taking parser as argument
403−queryWith :: ToRow q => RowParser r -> Connection -> Query -> q -> IO [r]
404−queryWith parser conn template qs = do
285+query :: (QueryParams q, QueryResults r)
286+ => Connection -> Query -> q -> IO [r]
287+query conn template qs = do
405288 result <- exec conn =<< formatQuery conn template qs
406− finishQueryWith parser conn template result
289+ finishQuery conn template result
407290
408−-- | A version of 'query_' taking parser as argument
409−queryWith_ :: RowParser r -> Connection -> Query -> IO [r]
410−queryWith_ parser conn q@(Query que) = do
291+-- | A version of 'query' that does not perform query substitution.
292+query_ :: (QueryResults r) => Connection -> Query -> IO [r]
293+query_ conn q@(Query que) = do
411294 result <- exec conn que
412− finishQueryWith parser conn q result
413−
295+ finishQuery conn q result
296+{--
414297-- | Perform a @SELECT@ or other SQL query that is expected to return
415298-- results. Results are streamed incrementally from the server, and
416299-- consumed via a left fold.
417300--
301+-- The result consumer must be carefully written to execute
302+-- quickly. If the consumer is slow, server resources will be tied up,
303+-- and other clients may not be able to update the tables from which
304+-- the results are being streamed.
305+--
418306-- When dealing with small results, it may be simpler (and perhaps
419307-- faster) to use 'query' instead.
420308--
421309-- This fold is /not/ strict. The stream consumer is responsible for
422310-- forcing the evaluation of its result to avoid space leaks.
423311--
424−-- This is implemented using a database cursor. As such, this requires
425−-- a transaction. This function will detect whether or not there is a
426−-- transaction in progress, and will create a 'ReadCommitted' 'ReadOnly'
427−-- transaction if needed. The cursor is given a unique temporary name,
428−-- so the consumer may itself call fold.
429−--
430312-- Exceptions that may be thrown:
431313--
432314-- * 'FormatError': the query string could not be formatted correctly.
@@ -435,202 +317,164 @@ queryWith_ parser conn q@(Query que) = do
435317-- using 'execute' instead of 'query').
436318--
437319-- * 'ResultError': result conversion failed.
438−--
439−-- * 'SqlError': the postgresql backend returned an error, e.g.
440−-- a syntax or type error, or an incorrect table or column name.
441−fold :: ( FromRow row, ToRow params )
442− => Connection
443− -> Query
444− -> params
445− -> a
446− -> (a -> row -> IO a)
447− -> IO a
448−fold = foldWithOptions defaultFoldOptions
449−
450−-- | A version of 'fold' taking a parser as an argument
451−foldWith :: ( ToRow params )
452− => RowParser row
453− -> Connection
454− -> Query
455− -> params
456− -> a
457− -> (a -> row -> IO a)
458− -> IO a
459−foldWith = foldWithOptionsAndParser defaultFoldOptions
460−
461−-- | Number of rows to fetch at a time. 'Automatic' currently defaults
462−-- to 256 rows, although it might be nice to make this more intelligent
463−-- based on e.g. the average size of the rows.
464−data FetchQuantity
465− = Automatic
466− | Fixed !Int
467−
468−data FoldOptions
469− = FoldOptions {
470− fetchQuantity :: !FetchQuantity,
471− transactionMode :: !TransactionMode
472− }
473−
474−-- | defaults to 'Automatic', and 'TransactionMode' 'ReadCommitted' 'ReadOnly'
475−defaultFoldOptions :: FoldOptions
476−defaultFoldOptions = FoldOptions {
477− fetchQuantity = Automatic,
478− transactionMode = TransactionMode ReadCommitted ReadOnly
479− }
480−
481−-- | The same as 'fold', but this provides a bit more control over
482−-- lower-level details. Currently, the number of rows fetched per
483−-- round-trip to the server and the transaction mode may be adjusted
484−-- accordingly. If the connection is already in a transaction,
485−-- then the existing transaction is used and thus the 'transactionMode'
486−-- option is ignored.
487−foldWithOptions :: ( FromRow row, ToRow params )
488− => FoldOptions
489− -> Connection
490− -> Query
491− -> params
492− -> a
493− -> (a -> row -> IO a)
494− -> IO a
495−foldWithOptions opts = foldWithOptionsAndParser opts fromRow
496−
497−-- | A version of 'foldWithOptions' taking a parser as an argument
498−foldWithOptionsAndParser :: (ToRow params)
499− => FoldOptions
500− -> RowParser row
501− -> Connection
502− -> Query
503− -> params
504− -> a
505− -> (a -> row -> IO a)
506− -> IO a
507−foldWithOptionsAndParser opts parser conn template qs a f = do
508− q <- formatQuery conn template qs
509− doFold opts parser conn template (Query q) a f
510−
320+fold :: (QueryParams q, QueryResults r) =>
321+ Connection
322+ -> Query -- ^ Query template.
323+ -> q -- ^ Query parameters.
324+ -> a -- ^ Initial state for result consumer.
325+ -> (a -> r -> IO a) -- ^ Result consumer.
326+ -> IO a
327+fold conn template qs z f = do
328+ Base.query conn =<< formatQuery conn template qs
329+ finishFold conn template z f
330+--}
331+{--
511332-- | A version of 'fold' that does not perform query substitution.
512−fold_ :: (FromRow r) =>
333+fold_ :: (QueryResults r) =>
513334 Connection
514335 -> Query -- ^ Query.
515336 -> a -- ^ Initial state for result consumer.
516337 -> (a -> r -> IO a) -- ^ Result consumer.
517338 -> IO a
518−fold_ = foldWithOptions_ defaultFoldOptions
519−
520−-- | A version of 'fold_' taking a parser as an argument
521−foldWith_ :: RowParser r
522− -> Connection
523− -> Query
524− -> a
525− -> (a -> r -> IO a)
526− -> IO a
527−foldWith_ = foldWithOptionsAndParser_ defaultFoldOptions
528−
529−foldWithOptions_ :: (FromRow r) =>
530− FoldOptions
531− -> Connection
532− -> Query -- ^ Query.
533− -> a -- ^ Initial state for result consumer.
534− -> (a -> r -> IO a) -- ^ Result consumer.
535− -> IO a
536−foldWithOptions_ opts conn query' a f = doFold opts fromRow conn query' query' a f
537−
538−-- | A version of 'foldWithOptions_' taking a parser as an argument
539−foldWithOptionsAndParser_ :: FoldOptions
540− -> RowParser r
541− -> Connection
542− -> Query -- ^ Query.
543− -> a -- ^ Initial state for result consumer.
544− -> (a -> r -> IO a) -- ^ Result consumer.
545− -> IO a
546−foldWithOptionsAndParser_ opts parser conn query' a f = doFold opts parser conn query' query' a f
547−
548−doFold :: FoldOptions
549− -> RowParser row
550− -> Connection
551− -> Query
552− -> Query
553− -> a
554− -> (a -> row -> IO a)
555− -> IO a
556−doFold FoldOptions{..} parser conn _template q a0 f = do
557− stat <- withConnection conn PQ.transactionStatus
558− case stat of
559− PQ.TransIdle -> withTransactionMode transactionMode conn go
560− PQ.TransInTrans -> go
561− PQ.TransActive -> fail "foldWithOpts FIXME: PQ.TransActive"
562− -- This _shouldn't_ occur in the current incarnation of
563− -- the library, as we aren't using libpq asynchronously.
564− -- However, it could occur in future incarnations of
565− -- this library or if client code uses the Internal module
566− -- to use raw libpq commands on postgresql-simple connections.
567− PQ.TransInError -> fail "foldWithOpts FIXME: PQ.TransInError"
568− -- This should be turned into a better error message.
569− -- It is probably a bad idea to automatically roll
570− -- back the transaction and start another.
571− PQ.TransUnknown -> fail "foldWithOpts FIXME: PQ.TransUnknown"
572− -- Not sure what this means.
573− where
574− declare =
575− declareCursor conn q
576− fetch cursor a =
577− foldForwardWithParser cursor parser chunkSize f a
578−
579− go = bracket declare closeCursor $ \cursor ->
580− let loop a = fetch cursor a >>=
581− \r -> case r of
582− Left x -> return x
583− Right x -> loop x
584− in loop a0
585−
586−-- FIXME: choose the Automatic chunkSize more intelligently
587−-- One possibility is to use the type of the results, although this
588−-- still isn't a perfect solution, given that common types (e.g. text)
589−-- are of highly variable size.
590−-- A refinement of this technique is to pick this number adaptively
591−-- as results are read in from the database.
592− chunkSize = case fetchQuantity of
593− Automatic -> 256
594− Fixed n -> n
595−
339+fold_ conn q@(Query que) z f = do
340+ Base.query conn que
341+ finishFold conn q z f
342+--}
343+{--
596344-- | A version of 'fold' that does not transform a state value.
597−forEach :: (ToRow q, FromRow r) =>
345+forEach :: (QueryParams q, QueryResults r) =>
598346 Connection
599347 -> Query -- ^ Query template.
600348 -> q -- ^ Query parameters.
601349 -> (r -> IO ()) -- ^ Result consumer.
602350 -> IO ()
603−forEach = forEachWith fromRow
351+forEach conn template qs = fold conn template qs () . const
604352{-# INLINE forEach #-}
605353
606−-- | A version of 'forEach' taking a parser as an argument
607−forEachWith :: ( ToRow q )
608− => RowParser r
609− -> Connection
610− -> Query
611− -> q
612− -> (r -> IO ())
613− -> IO ()
614−forEachWith parser conn template qs = foldWith parser conn template qs () . const
615−{-# INLINE forEachWith #-}
616−
617354-- | A version of 'forEach' that does not perform query substitution.
618−forEach_ :: (FromRow r) =>
355+forEach_ :: (QueryResults r) =>
619356 Connection
620357 -> Query -- ^ Query template.
621358 -> (r -> IO ()) -- ^ Result consumer.
622359 -> IO ()
623−forEach_ = forEachWith_ fromRow
360+forEach_ conn template = fold_ conn template () . const
624361{-# INLINE forEach_ #-}
362+--}
625363
626−forEachWith_ :: RowParser r
627− -> Connection
628− -> Query
629− -> (r -> IO ())
630− -> IO ()
631−forEachWith_ parser conn template = foldWith_ parser conn template () . const
632−{-# INLINE forEachWith_ #-}
633−
364+forM' :: (Ord n, Num n) => n -> n -> (n -> IO a) -> IO [a]
365+forM' lo hi m = loop hi []
366+ where
367+ loop !n !as
368+ | n < lo = return as
369+ | otherwise = do
370+ a <- m n
371+ loop (n-1) (a:as)
372+
373+finishQuery :: (QueryResults r) => Connection -> Query -> PQ.Result -> IO [r]
374+finishQuery conn q result = do
375+ status <- PQ.resultStatus result
376+ case status of
377+ PQ.EmptyQuery -> fail "empty query" -- FIXME: error message
378+ PQ.CommandOk -> do
379+ throwIO $ QueryError "query resulted in a command response" q
380+ PQ.TuplesOk -> do
381+ ncols <- PQ.nfields result
382+ fields <- forM' 0 (ncols-1) $ \column -> do
383+ type_oid <- PQ.ftype result column
384+ typename <- getTypename conn type_oid
385+ return Field{..}
386+ nrows <- PQ.ntuples result
387+ forM' 0 (nrows-1) $ \row -> do
388+ values <- forM' 0 (ncols-1) (\col -> PQ.getvalue' result row col)
389+ case convertResults fields values of
390+ Fail err -> throwIO err
391+ Success a -> return a
392+ PQ.CopyIn -> fail "FIXME: postgresql-simple does not currently support COPY IN"
393+ PQ.CopyOut -> fail "FIXME: postgresql-simple does not currently support COPY OUT"
394+ _ -> do
395+ errormsg <- maybe "" id <$> PQ.resultErrorMessage result
396+ statusmsg <- PQ.resStatus status
397+ state <- maybe "" id <$> PQ.resultErrorField result PQ.DiagSqlstate
398+ throw $ SqlError { sqlState = state
399+ , sqlNativeError = 0 -- FIXME
400+ , sqlErrorMsg = B.concat [ "execute: ", statusmsg
401+ , ": ", errormsg ]}
402+
403+{--
404+withResult (Base.storeResult conn) q $ \r fs -> do
405+ status <-
406+/ flip fix [] $ \loop acc -> do
407+ row <- Base.fetchRow r
408+ case row of
409+ [] -> return (reverse acc)
410+ _ -> let !c = convertResults fs row
411+ in loop (c:acc)
412+--}
413+{--
414+finishFold :: (QueryResults r) =>
415+ Connection -> Query -> a -> (a -> r -> IO a) -> IO a
416+finishFold conn q z0 f = withResult (Base.useResult conn) q $ \r fs ->
417+ flip fix z0 $ \loop z -> do
418+ row <- Base.fetchRow r
419+ case row of
420+ [] -> return z
421+ _ -> (f z $! convertResults fs row) >>= loop
422+--}
423+{--
424+withResult :: (IO Result) -> Query -> (Result -> [Field] -> IO a) -> IO a
425+withResult fetchResult q act = bracket fetchResult Base.freeResult $ \r -> do
426+ ncols <- Base.fieldCount (Right r)
427+ if ncols == 0
428+ then throwIO $ QueryError "query resulted in zero-column result" q
429+ else act r =<< Base.fetchFields r
430+--}
431+-- | Execute an action inside a SQL transaction.
432+--
433+-- This function initiates a transaction with a \"@begin
434+-- transaction@\" statement, then executes the supplied action. If
435+-- the action succeeds, the transaction will be completed with
436+-- 'Base.commit' before this function returns.
437+--
438+-- If the action throws /any/ kind of exception (not just a
439+-- MySQL-related exception), the transaction will be rolled back using
440+-- 'Base.rollback', then the exception will be rethrown.
441+withTransaction :: Connection -> IO a -> IO a
442+withTransaction conn act = do
443+ _ <- begin conn
444+ r <- act `onException` rollback conn
445+ commit conn
446+ return r
447+
448+-- | Rollback a transaction.
449+-- rollback :: (MonadCatchIO m,MonadIO m) => Connection -> m ()
450+rollback :: Connection -> IO ()
451+rollback conn = do
452+ _ <- execute_ conn "ABORT;"
453+ return ()
454+
455+-- | Commit a transaction.
456+-- commit :: (MonadCatchIO m,MonadIO m) => Connection -> m ()
457+commit :: Connection -> IO ()
458+commit conn = do
459+ _ <- execute_ conn "COMMIT;"
460+ return ()
461+
462+-- | Begin a transaction.
463+-- begin :: (MonadCatchIO m,MonadIO m) => Connection -> m ()
464+begin :: Connection -> IO ()
465+begin conn = do
466+ _ <- execute_ conn "BEGIN;"
467+ return ()
468+
469+fmtError :: String -> Query -> [Action] -> a
470+fmtError msg q xs = throw FormatError {
471+ fmtMessage = msg
472+ , fmtQuery = q
473+ , fmtParams = map twiddle xs
474+ }
475+ where twiddle (Plain b) = toByteString b
476+ twiddle (Escape s) = s
477+ twiddle (Many ys) = B.concat (map twiddle ys)
634478
635479-- $use
636480--
@@ -656,13 +500,11 @@ forEachWith_ parser conn template = foldWith_ parser conn template () . const
656500--
657501-- > {-# LANGUAGE OverloadedStrings #-}
658502-- >
659−-- > import Database.PostgreSQL.Simple
503+-- > import Database.MySQL.Simple
660504-- >
661−-- > hello :: IO Int
662505-- > hello = do
663−-- > conn <- connectPostgreSQL ""
664−-- > [Only i] <- query_ conn "select 2 + 2"
665−-- > return i
506+-- > conn <- connect defaultConnectInfo
507+-- > query conn "select 2 + 2"
666508--
667509-- A 'Query' value does not represent the actual query that will be
668510-- executed, but is a template for constructing the final query.
@@ -709,7 +551,7 @@ forEachWith_ parser conn template = foldWith_ parser conn template () . const
709551-- Automated type inference means that you will often be able to avoid
710552-- supplying explicit type signatures for the elements of a tuple.
711553-- However, sometimes the compiler will not be able to infer your
712−-- types. Consider a case where you write a numeric literal in a
554+-- types. Consider a care where you write a numeric literal in a
713555-- parameter tuple:
714556--
715557-- > query conn "select ? + ?" (40,2)
@@ -723,30 +565,15 @@ forEachWith_ parser conn template = foldWith_ parser conn template () . const
723565-- The same kind of problem can arise with string literals if you have
724566-- the @OverloadedStrings@ language extension enabled. Again, just
725567-- use an explicit type signature if this happens.
726−--
727−-- Finally, remember that the compiler must be able to infer the type
728−-- of a query's /results/ as well as its parameters. We might like
729−-- the following example to work:
730−--
731−-- > print =<< query_ conn "select 2 + 2"
732−--
733−-- Unfortunately, while a quick glance tells us that the result type
734−-- should be a single row containing a single numeric column, the
735−-- compiler has no way to infer what the types are. We can easily fix
736−-- this by providing an explicit type annotation:
737−--
738−-- > xs <- query_ conn "select 2 + 2"
739−-- > print (xs :: [Only Int])
740568
741569-- $only_param
742570--
743571-- Haskell lacks a single-element tuple type, so if you have just one
744−-- value you want substituted into a query or a single-column result,
745−-- what should you do?
572+-- value you want substituted into a query, what should you do?
746573--
747574-- The obvious approach would appear to be something like this:
748575--
749−-- > instance (ToField a) => ToRow a where
576+-- > instance (Param a) => QueryParam a where
750577-- > ...
751578--
752579-- Unfortunately, this wreaks havoc with type inference, so we take a
@@ -757,9 +584,6 @@ forEachWith_ parser conn template = foldWith_ parser conn template () . const
757584--
758585-- > execute conn "insert into users (first_name) values (?)"
759586-- > ["Nuala"]
760−--
761−-- A row of /n/ query results is represented using an /n/-tuple, so
762−-- you should use 'Only' to represent a single-column result.
763587
764588-- $in
765589--
@@ -777,7 +601,7 @@ forEachWith_ parser conn template = foldWith_ parser conn template () . const
777601-- Here's an example:
778602--
779603-- > query conn "select * from users where first_name in ?" $
780−-- > Only $ In ["Anna", "Boris", "Carla"]
604+-- > In ["Anna", "Boris", "Carla"]
781605--
782606-- If your 'In'-wrapped list is empty, the string @\"(null)\"@ will be
783607-- substituted instead, to ensure that your clause remains
@@ -805,7 +629,7 @@ forEachWith_ parser conn template = foldWith_ parser conn template () . const
805629-- * There must be no other \"@?@\" characters anywhere in your
806630-- template.
807631--
808−-- * There must be one or more \"@?@\" in the parentheses.
632+-- * There must one or more \"@?@\" in the parentheses.
809633--
810634-- * Extra white space is fine.
811635--
@@ -826,26 +650,10 @@ forEachWith_ parser conn template = foldWith_ parser conn template () . const
826650-- > insert into users (first_name,last_name) values
827651-- > ('Boris','Karloff'),('Ed','Wood')
828652
829−-- $returning
830−--
831−-- PostgreSQL supports returning values from data manipulation statements
832−-- such as @INSERT@ and @UPDATE@. You can use these statements by
833−-- using 'query' instead of 'execute'. For multi-tuple inserts,
834−-- use 'returning' instead of 'executeMany'.
835−--
836−-- For example, were there an auto-incrementing @id@ column and
837−-- timestamp column @t@ that defaulted to the present time for the
838−-- @sales@ table, then the following query would insert two new
839−-- sales records and also return their new @id@s and timestamps.
840−--
841−-- > let q = "insert into sales (amount, label) values (?,?) returning id, t"
842−-- > xs :: [(Int, UTCTime)] <- query conn q (15,"Sawdust")
843−-- > ys :: [(Int, UTCTime)] <- returning conn q [(20,"Chips"),(300,"Wood")]
844−
845653-- $result
846654--
847655-- The 'query' and 'query_' functions return a list of values in the
848−-- 'FromRow' typeclass. This class performs automatic extraction
656+-- 'QueryResults' typeclass. This class performs automatic extraction
849657-- and type conversion of rows from a query result.
850658--
851659-- Here is a simple example of how to extract results:
@@ -876,7 +684,7 @@ forEachWith_ parser conn template = foldWith_ parser conn template () . const
876684--
877685-- Although SQL can accommodate @NULL@ as a value for any of these
878686-- types, Haskell cannot. If your result contains columns that may be
879−-- @NULL@, be sure that you use 'Maybe' in those positions of your
687+-- @NULL@, be sure that you use 'Maybe' in those positions of of your
880688-- tuple.
881689--
882690-- > (Text, Maybe Int, Int)
@@ -899,15 +707,16 @@ forEachWith_ parser conn template = foldWith_ parser conn template () . const
899707-- permissive. Here are the rules.
900708--
901709-- * For numeric types, any Haskell type that can accurately represent
902−-- all values of the given PostgreSQL type is considered \"compatible\".
903−-- For instance, you can always extract a PostgreSQL 16-bit @SMALLINT@
904−-- column to a Haskell 'Int'. The Haskell 'Float' type can accurately
905−-- represent a @SMALLINT@, so it is considered compatible with those types.
710+-- all values of the given MySQL type is considered \"compatible\".
711+-- For instance, you can always extract a MySQL @TINYINT@ column to
712+-- a Haskell 'Int'. The Haskell 'Float' type can accurately
713+-- represent MySQL integer types of size up to @INT24@, so it is
714+-- considered compatble with those types.
906715--
907716-- * A numeric compatibility check is based only on the type of a
908−-- column, /not/ on its values. For instance, a PostgreSQL 64-bit
909−-- @BIGINT@ column will be considered incompatible with a Haskell
910−-- 'Int16', even if it contains the value @1@.
717+-- column, /not/ on its values. For instance, a MySQL @LONG_LONG@
718+-- column will be considered incompatible with a Haskell 'Int8',
719+-- even if it contains the value @1@.
911720--
912721-- * If a numeric incompatibility is found, 'query' will throw a
913722-- 'ResultError'.
@@ -916,3 +725,29 @@ forEachWith_ parser conn template = foldWith_ parser conn template () . const
916725-- UTF-8. If you use some other encoding, decoding may fail or give
917726-- wrong results. In such cases, write a @newtype@ wrapper and a
918727-- custom 'Result' instance to handle your encoding.
728+
729+getTypename :: Connection -> PQ.Oid -> IO ByteString
730+getTypename conn@Connection{..} oid =
731+ case oid2builtin oid of
732+ Just builtin -> return $! builtin2typname builtin
733+ Nothing -> do
734+ oidmap <- takeMVar connectionObjects
735+ case IntMap.lookup (oid2int oid) oidmap of
736+ Just name -> putMVar connectionObjects oidmap >> return name
737+ Nothing -> do
738+ names <- query conn "SELECT typname FROM pg_type WHERE oid=?"
739+ (Only oid)
740+ name <- case names of
741+ [] -> return $ throw SqlError {
742+ sqlNativeError = -1,
743+ sqlErrorMsg = "invalid type oid",
744+ sqlState = ""
745+ }
746+ [Only x] -> return x
747+ _ -> fail "typename query returned more than one result"
748+ -- oid is a primary key, so the query should
749+ -- never return more than one result
750+ putMVar connectionObjects (IntMap.insert (oid2int oid) name oidmap)
751+ return name
752+ `onException` do
753+ putMVar connectionObjects oidmap
src/Database/PostgreSQL/Simple.hs-bootdeleted−23
@@ -1,23 +0,0 @@
1−module Database.PostgreSQL.Simple
2− ( Connection
3− , Query
4− , query
5− , query_
6− , execute
7− , execute_
8− , executeMany
9− ) where
10−
11−import Data.Int(Int64)
12−import Database.PostgreSQL.Simple.Internal
13−import Database.PostgreSQL.Simple.Types
14−import {-# SOURCE #-} Database.PostgreSQL.Simple.FromRow
15−import {-# SOURCE #-} Database.PostgreSQL.Simple.ToRow
16−
17−query :: (ToRow q, FromRow r) => Connection -> Query -> q -> IO [r]
18−
19−query_ :: FromRow r => Connection -> Query -> IO [r]
20−
21−execute :: ToRow q => Connection -> Query -> q -> IO Int64
22−
23−executeMany :: ToRow q => Connection -> Query -> [q] -> IO Int64
src/Database/PostgreSQL/Simple/Arrays.hsdeleted−92
@@ -1,92 +0,0 @@
1−{-# LANGUAGE PatternGuards #-}
2−
3−------------------------------------------------------------------------------
4−-- |
5−-- Module: Database.PostgreSQL.Simple.Arrays
6−-- Copyright: (c) 2012 Leon P Smith
7−-- License: BSD3
8−-- Maintainer: Leon P Smith <leon@melding-monads.com>
9−-- Stability: experimental
10−--
11−-- A Postgres array parser and pretty-printer.
12−------------------------------------------------------------------------------
13−
14−module Database.PostgreSQL.Simple.Arrays where
15−
16−import Control.Applicative (Applicative(..), Alternative(..), (<$>))
17−import Data.ByteString.Char8 (ByteString)
18−import qualified Data.ByteString.Char8 as B
19−import Data.Monoid
20−import Data.Attoparsec.ByteString.Char8
21−
22−
23−-- | Parse one of three primitive field formats: array, quoted and plain.
24−arrayFormat :: Char -> Parser ArrayFormat
25−arrayFormat delim = Array <$> array delim
26− <|> Plain <$> plain delim
27− <|> Quoted <$> quoted
28−
29−data ArrayFormat = Array [ArrayFormat]
30− | Plain ByteString
31− | Quoted ByteString
32− deriving (Eq, Show, Ord)
33−
34−array :: Char -> Parser [ArrayFormat]
35−array delim = char '{' *> option [] (arrays <|> strings) <* char '}'
36− where
37− strings = sepBy1 (Quoted <$> quoted <|> Plain <$> plain delim) (char delim)
38− arrays = sepBy1 (Array <$> array delim) (char ',')
39− -- NB: Arrays seem to always be delimited by commas.
40−
41−-- | Recognizes a quoted string.
42−quoted :: Parser ByteString
43−quoted = char '"' *> option "" contents <* char '"'
44− where
45− esc' = char '\\' *> (char '\\' <|> char '"')
46− unQ = takeWhile1 (notInClass "\"\\")
47− contents = mconcat <$> many (unQ <|> B.singleton <$> esc')
48−
49−-- | Recognizes a plain string literal, not containing quotes or brackets and
50−-- not containing the delimiter character.
51−plain :: Char -> Parser ByteString
52−plain delim = takeWhile1 (notInClass (delim:"\"{}"))
53−
54−-- Mutually recursive 'fmt' and 'delimit' separate out value formatting
55−-- from the subtleties of delimiting.
56−
57−-- | Format an array format item, using the delimiter character if the item is
58−-- itself an array.
59−fmt :: Char -> ArrayFormat -> ByteString
60−fmt = fmt' False
61−
62−-- | Format a list of array format items, inserting the appropriate delimiter
63−-- between them. When the items are arrays, they will be delimited with
64−-- commas; otherwise, they are delimited with the passed-in-delimiter.
65−delimit :: Char -> [ArrayFormat] -> ByteString
66−delimit _ [] = ""
67−delimit c [x] = fmt' True c x
68−delimit c (x:y:z) = (fmt' True c x `B.snoc` c') `mappend` delimit c (y:z)
69− where
70− c' | Array _ <- x = ','
71− | otherwise = c
72−
73−-- | Format an array format item, using the delimiter character if the item is
74−-- itself an array, optionally applying quoting rules. Creates copies for
75−-- safety when used in 'FromField' instances.
76−fmt' :: Bool -> Char -> ArrayFormat -> ByteString
77−fmt' quoting c x =
78− case x of
79− Array items -> '{' `B.cons` (delimit c items `B.snoc` '}')
80− Plain bytes -> B.copy bytes
81− Quoted q | quoting -> '"' `B.cons` (esc q `B.snoc` '"')
82− | otherwise -> B.copy q
83− -- NB: The 'snoc' and 'cons' functions always copy.
84−
85−-- | Escape a string according to Postgres double-quoted string format.
86−esc :: ByteString -> ByteString
87−esc = B.concatMap f
88− where
89− f '"' = "\\\""
90− f '\\' = "\\\\"
91− f c = B.singleton c
92− -- TODO: Implement easy performance improvements with unfoldr.
src/Database/PostgreSQL/Simple/BuiltinTypes.hsnew file+403
@@ -0,0 +1,403 @@
1+{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}
2+
3+------------------------------------------------------------------------------
4+-- |
5+-- Module: Database.PostgreSQL.Simple.BuiltinTypes
6+-- Copyright: (c) 2011 Leon P Smith
7+-- License: BSD3
8+-- Maintainer: Leon P Smith <leon@melding-monads.com>
9+-- Stability: experimental
10+--
11+------------------------------------------------------------------------------
12+
13+-- Note that this file is generated by tools/GenBuiltinTypes.hs, and should
14+-- not be edited directly
15+
16+module Database.PostgreSQL.Simple.BuiltinTypes
17+ ( BuiltinType (..)
18+ , builtin2oid
19+ , oid2builtin
20+ , builtin2typname
21+ , oid2typname
22+ ) where
23+
24+import Data.Typeable
25+import Data.ByteString (ByteString)
26+import qualified Database.PostgreSQL.LibPQ as PQ
27+
28+data BuiltinType
29+ = Bool
30+ | Bytea
31+ | Char
32+ | Name
33+ | Int8
34+ | Int2
35+ | Int4
36+ | Regproc
37+ | Text
38+ | Oid
39+ | Tid
40+ | Xid
41+ | Cid
42+ | Xml
43+ | Point
44+ | Lseg
45+ | Path
46+ | Box
47+ | Polygon
48+ | Line
49+ | Cidr
50+ | Float4
51+ | Float8
52+ | Abstime
53+ | Reltime
54+ | Tinterval
55+ | Unknown
56+ | Circle
57+ | Money
58+ | Macaddr
59+ | Inet
60+ | Bpchar
61+ | Varchar
62+ | Date
63+ | Time
64+ | Timestamp
65+ | TimestampWithTimeZone
66+ | Interval
67+ | TimeWithTimeZone
68+ | Bit
69+ | Varbit
70+ | Numeric
71+ | Refcursor
72+ | Record
73+ | Void
74+ deriving (Eq, Ord, Enum, Bounded, Read, Show, Typeable)
75+
76+builtin2oid :: BuiltinType -> PQ.Oid
77+builtin2oid typ = PQ.Oid $ case typ of
78+ Bool -> 16
79+ Bytea -> 17
80+ Char -> 18
81+ Name -> 19
82+ Int8 -> 20
83+ Int2 -> 21
84+ Int4 -> 23
85+ Regproc -> 24
86+ Text -> 25
87+ Oid -> 26
88+ Tid -> 27
89+ Xid -> 28
90+ Cid -> 29
91+ Xml -> 142
92+ Point -> 600
93+ Lseg -> 601
94+ Path -> 602
95+ Box -> 603
96+ Polygon -> 604
97+ Line -> 628
98+ Cidr -> 650
99+ Float4 -> 700
100+ Float8 -> 701
101+ Abstime -> 702
102+ Reltime -> 703
103+ Tinterval -> 704
104+ Unknown -> 705
105+ Circle -> 718
106+ Money -> 790
107+ Macaddr -> 829
108+ Inet -> 869
109+ Bpchar -> 1042
110+ Varchar -> 1043
111+ Date -> 1082
112+ Time -> 1083
113+ Timestamp -> 1114
114+ TimestampWithTimeZone -> 1184
115+ Interval -> 1186
116+ TimeWithTimeZone -> 1266
117+ Bit -> 1560
118+ Varbit -> 1562
119+ Numeric -> 1700
120+ Refcursor -> 1790
121+ Record -> 2249
122+ Void -> 2278
123+
124+oid2builtin :: PQ.Oid -> Maybe BuiltinType
125+oid2builtin (PQ.Oid x) = case x of
126+ 16 -> Just Bool
127+ 17 -> Just Bytea
128+ 18 -> Just Char
129+ 19 -> Just Name
130+ 20 -> Just Int8
131+ 21 -> Just Int2
132+ 23 -> Just Int4
133+ 24 -> Just Regproc
134+ 25 -> Just Text
135+ 26 -> Just Oid
136+ 27 -> Just Tid
137+ 28 -> Just Xid
138+ 29 -> Just Cid
139+ 142 -> Just Xml
140+ 600 -> Just Point
141+ 601 -> Just Lseg
142+ 602 -> Just Path
143+ 603 -> Just Box
144+ 604 -> Just Polygon
145+ 628 -> Just Line
146+ 650 -> Just Cidr
147+ 700 -> Just Float4
148+ 701 -> Just Float8
149+ 702 -> Just Abstime
150+ 703 -> Just Reltime
151+ 704 -> Just Tinterval
152+ 705 -> Just Unknown
153+ 718 -> Just Circle
154+ 790 -> Just Money
155+ 829 -> Just Macaddr
156+ 869 -> Just Inet
157+ 1042 -> Just Bpchar
158+ 1043 -> Just Varchar
159+ 1082 -> Just Date
160+ 1083 -> Just Time
161+ 1114 -> Just Timestamp
162+ 1184 -> Just TimestampWithTimeZone
163+ 1186 -> Just Interval
164+ 1266 -> Just TimeWithTimeZone
165+ 1560 -> Just Bit
166+ 1562 -> Just Varbit
167+ 1700 -> Just Numeric
168+ 1790 -> Just Refcursor
169+ 2249 -> Just Record
170+ 2278 -> Just Void
171+ _ -> Nothing
172+
173+builtin2typname :: BuiltinType -> ByteString
174+builtin2typname typ = case typ of
175+ Bool -> bool
176+ Bytea -> bytea
177+ Char -> char
178+ Name -> name
179+ Int8 -> int8
180+ Int2 -> int2
181+ Int4 -> int4
182+ Regproc -> regproc
183+ Text -> text
184+ Oid -> oid
185+ Tid -> tid
186+ Xid -> xid
187+ Cid -> cid
188+ Xml -> xml
189+ Point -> point
190+ Lseg -> lseg
191+ Path -> path
192+ Box -> box
193+ Polygon -> polygon
194+ Line -> line
195+ Cidr -> cidr
196+ Float4 -> float4
197+ Float8 -> float8
198+ Abstime -> abstime
199+ Reltime -> reltime
200+ Tinterval -> tinterval
201+ Unknown -> unknown
202+ Circle -> circle
203+ Money -> money
204+ Macaddr -> macaddr
205+ Inet -> inet
206+ Bpchar -> bpchar
207+ Varchar -> varchar
208+ Date -> date
209+ Time -> time
210+ Timestamp -> timestamp
211+ TimestampWithTimeZone -> timestamptz
212+ Interval -> interval
213+ TimeWithTimeZone -> timetz
214+ Bit -> bit
215+ Varbit -> varbit
216+ Numeric -> numeric
217+ Refcursor -> refcursor
218+ Record -> record
219+ Void -> void
220+
221+oid2typname :: PQ.Oid -> Maybe ByteString
222+oid2typname (PQ.Oid x) = case x of
223+ 16 -> Just bool
224+ 17 -> Just bytea
225+ 18 -> Just char
226+ 19 -> Just name
227+ 20 -> Just int8
228+ 21 -> Just int2
229+ 23 -> Just int4
230+ 24 -> Just regproc
231+ 25 -> Just text
232+ 26 -> Just oid
233+ 27 -> Just tid
234+ 28 -> Just xid
235+ 29 -> Just cid
236+ 142 -> Just xml
237+ 600 -> Just point
238+ 601 -> Just lseg
239+ 602 -> Just path
240+ 603 -> Just box
241+ 604 -> Just polygon
242+ 628 -> Just line
243+ 650 -> Just cidr
244+ 700 -> Just float4
245+ 701 -> Just float8
246+ 702 -> Just abstime
247+ 703 -> Just reltime
248+ 704 -> Just tinterval
249+ 705 -> Just unknown
250+ 718 -> Just circle
251+ 790 -> Just money
252+ 829 -> Just macaddr
253+ 869 -> Just inet
254+ 1042 -> Just bpchar
255+ 1043 -> Just varchar
256+ 1082 -> Just date
257+ 1083 -> Just time
258+ 1114 -> Just timestamp
259+ 1184 -> Just timestamptz
260+ 1186 -> Just interval
261+ 1266 -> Just timetz
262+ 1560 -> Just bit
263+ 1562 -> Just varbit
264+ 1700 -> Just numeric
265+ 1790 -> Just refcursor
266+ 2249 -> Just record
267+ 2278 -> Just void
268+ _ -> Nothing
269+
270+bool :: ByteString
271+bool = "bool"
272+
273+bytea :: ByteString
274+bytea = "bytea"
275+
276+char :: ByteString
277+char = "char"
278+
279+name :: ByteString
280+name = "name"
281+
282+int8 :: ByteString
283+int8 = "int8"
284+
285+int2 :: ByteString
286+int2 = "int2"
287+
288+int4 :: ByteString
289+int4 = "int4"
290+
291+regproc :: ByteString
292+regproc = "regproc"
293+
294+text :: ByteString
295+text = "text"
296+
297+oid :: ByteString
298+oid = "oid"
299+
300+tid :: ByteString
301+tid = "tid"
302+
303+xid :: ByteString
304+xid = "xid"
305+
306+cid :: ByteString
307+cid = "cid"
308+
309+xml :: ByteString
310+xml = "xml"
311+
312+point :: ByteString
313+point = "point"
314+
315+lseg :: ByteString
316+lseg = "lseg"
317+
318+path :: ByteString
319+path = "path"
320+
321+box :: ByteString
322+box = "box"
323+
324+polygon :: ByteString
325+polygon = "polygon"
326+
327+line :: ByteString
328+line = "line"
329+
330+cidr :: ByteString
331+cidr = "cidr"
332+
333+float4 :: ByteString
334+float4 = "float4"
335+
336+float8 :: ByteString
337+float8 = "float8"
338+
339+abstime :: ByteString
340+abstime = "abstime"
341+
342+reltime :: ByteString
343+reltime = "reltime"
344+
345+tinterval :: ByteString
346+tinterval = "tinterval"
347+
348+unknown :: ByteString
349+unknown = "unknown"
350+
351+circle :: ByteString
352+circle = "circle"
353+
354+money :: ByteString
355+money = "money"
356+
357+macaddr :: ByteString
358+macaddr = "macaddr"
359+
360+inet :: ByteString
361+inet = "inet"
362+
363+bpchar :: ByteString
364+bpchar = "bpchar"
365+
366+varchar :: ByteString
367+varchar = "varchar"
368+
369+date :: ByteString
370+date = "date"
371+
372+time :: ByteString
373+time = "time"
374+
375+timestamp :: ByteString
376+timestamp = "timestamp"
377+
378+timestamptz :: ByteString
379+timestamptz = "timestamptz"
380+
381+interval :: ByteString
382+interval = "interval"
383+
384+timetz :: ByteString
385+timetz = "timetz"
386+
387+bit :: ByteString
388+bit = "bit"
389+
390+varbit :: ByteString
391+varbit = "varbit"
392+
393+numeric :: ByteString
394+numeric = "numeric"
395+
396+refcursor :: ByteString
397+refcursor = "refcursor"
398+
399+record :: ByteString
400+record = "record"
401+
402+void :: ByteString
403+void = "void"
src/Database/PostgreSQL/Simple/Compat.hsdeleted−41
@@ -1,41 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−-- | This is a module of its own, partly because it uses the CPP extension,
3−-- which doesn't play well with backslash-broken string literals.
4−module Database.PostgreSQL.Simple.Compat
5− ( mask
6− , (<>)
7− , unsafeDupablePerformIO
8− , toByteString
9− , scientificBuilder
10− , toPico
11− , fromPico
12− ) where
13−
14−import qualified Control.Exception as E
15−import Data.ByteString (ByteString)
16−import Data.ByteString.Lazy (toStrict)
17−import Data.ByteString.Builder (Builder, toLazyByteString)
18−import Data.Text.Lazy.Builder.Scientific (scientificBuilder)
19−import System.IO.Unsafe (unsafeDupablePerformIO)
20−import Data.Fixed (Pico)
21−import Data.Fixed (Fixed(MkFixed))
22−
23−-- | Like 'E.mask', but backported to base before version 4.3.0.
24−--
25−-- Note that the restore callback is monomorphic, unlike in 'E.mask'. This
26−-- could be fixed by changing the type signature, but it would require us to
27−-- enable the RankNTypes extension (since 'E.mask' has a rank-3 type). The
28−-- 'withTransactionMode' function calls the restore callback only once, so we
29−-- don't need that polymorphism.
30−mask :: ((IO a -> IO a) -> IO b) -> IO b
31−mask io = E.mask $ \restore -> io restore
32−{-# INLINE mask #-}
33−
34−toByteString :: Builder -> ByteString
35−toByteString x = toStrict (toLazyByteString x)
36−
37−toPico :: Integer -> Pico
38−toPico = MkFixed
39−
40−fromPico :: Pico -> Integer
41−fromPico (MkFixed i) = i
src/Database/PostgreSQL/Simple/Copy.hsdeleted−269
@@ -1,269 +0,0 @@
1−{-# LANGUAGE CPP, DeriveDataTypeable #-}
2−
3−------------------------------------------------------------------------------
4−-- |
5−-- Module: Database.PostgreSQL.Simple.Copy
6−-- Copyright: (c) 2013 Leon P Smith
7−-- License: BSD3
8−-- Maintainer: Leon P Smith <leon@melding-monads.com>
9−-- Stability: experimental
10−--
11−-- mid-level support for COPY IN and COPY OUT. See
12−-- <https://www.postgresql.org/docs/9.5/static/sql-copy.html> for
13−-- more information.
14−--
15−-- To use this binding, first call 'copy' with an appropriate
16−-- query as documented in the link above. Then, in the case of a
17−-- @COPY TO STDOUT@ query, call 'getCopyData' repeatedly until it
18−-- returns 'CopyOutDone'. In the case of a @COPY FROM STDIN@
19−-- query, call 'putCopyData' repeatedly and then finish by calling
20−-- either 'putCopyEnd' to proceed or 'putCopyError' to abort.
21−--
22−-- You cannot issue another query on the same connection while a copy
23−-- is ongoing; this will result in an exception. It is harmless to
24−-- concurrently call @getNotification@ on a connection while it is in
25−-- a @CopyIn@ or @CopyOut@ state, however be aware that current versions
26−-- of the PostgreSQL backend will not deliver notifications to a client
27−-- while a transaction is ongoing.
28−--
29−------------------------------------------------------------------------------
30−
31−module Database.PostgreSQL.Simple.Copy
32− ( copy
33− , copy_
34− , CopyOutResult(..)
35− , foldCopyData
36− , getCopyData
37− , putCopyData
38− , putCopyEnd
39− , putCopyError
40− ) where
41−
42−import Control.Applicative
43−import Control.Concurrent
44−import Control.Exception ( throwIO )
45−import qualified Data.Attoparsec.ByteString.Char8 as P
46−import Data.Typeable(Typeable)
47−import Data.Int(Int64)
48−import qualified Data.ByteString.Char8 as B
49−import qualified Database.PostgreSQL.LibPQ as PQ
50−import Database.PostgreSQL.Simple
51−import Database.PostgreSQL.Simple.Types
52−import Database.PostgreSQL.Simple.Internal hiding (result, row)
53−
54−
55−-- | Issue a @COPY FROM STDIN@ or @COPY TO STDOUT@ query. In the former
56−-- case, the connection's state will change to @CopyIn@; in the latter,
57−-- @CopyOut@. The connection must be in the ready state in order
58−-- to call this function. Performs parameter substitution.
59−
60−copy :: ( ToRow params ) => Connection -> Query -> params -> IO ()
61−copy conn template qs = do
62− q <- formatQuery conn template qs
63− doCopy "Database.PostgreSQL.Simple.Copy.copy" conn template q
64−
65−
66−-- | Issue a @COPY FROM STDIN@ or @COPY TO STDOUT@ query. In the former
67−-- case, the connection's state will change to @CopyIn@; in the latter,
68−-- @CopyOut@. The connection must be in the ready state in order
69−-- to call this function. Does not perform parameter subsitution.
70−
71−copy_ :: Connection -> Query -> IO ()
72−copy_ conn (Query q) = do
73− doCopy "Database.PostgreSQL.Simple.Copy.copy_" conn (Query q) q
74−
75−doCopy :: B.ByteString -> Connection -> Query -> B.ByteString -> IO ()
76−doCopy funcName conn template q = do
77− result <- exec conn q
78− status <- PQ.resultStatus result
79− let errMsg msg = throwIO $ QueryError
80− (B.unpack funcName ++ " " ++ msg)
81− template
82− let err = errMsg $ show status
83− case status of
84− PQ.EmptyQuery -> err
85− PQ.CommandOk -> err
86− PQ.TuplesOk -> err
87− PQ.CopyOut -> return ()
88− PQ.CopyIn -> return ()
89− PQ.CopyBoth -> errMsg "COPY BOTH is not supported"
90− PQ.SingleTuple -> errMsg "single-row mode is not supported"
91− PQ.BadResponse -> throwResultError funcName result status
92− PQ.NonfatalError -> throwResultError funcName result status
93− PQ.FatalError -> throwResultError funcName result status
94−
95−data CopyOutResult
96− = CopyOutRow !B.ByteString -- ^ Data representing either exactly
97− -- one row of the result, or header
98− -- or footer data depending on format.
99− | CopyOutDone {-# UNPACK #-} !Int64 -- ^ No more rows, and a count of the
100− -- number of rows returned.
101− deriving (Eq, Typeable, Show)
102−
103−
104−-- | Fold over @COPY TO STDOUT@ query passing each copied row to an accumulator
105−-- and calling a post-process at the end. A connection must be in the
106−-- @CopyOut@ state in order to call this function.
107−--
108−-- __Example__
109−--
110−-- > (acc, count) <- foldCopyData conn
111−-- > (\acc row -> return (row:acc))
112−-- > (\acc count -> return (acc, count))
113−-- > []
114−
115−foldCopyData
116− :: Connection -- ^ Database connection
117− -> (a -> B.ByteString -> IO a) -- ^ Accumulate one row of the result
118− -> (a -> Int64 -> IO b) -- ^ Post-process accumulator with a count of rows
119− -> a -- ^ Initial accumulator
120− -> IO b -- ^ Result
121−foldCopyData conn f g !acc = do
122− result <- getCopyData conn
123− case result of
124− CopyOutRow row -> f acc row >>= foldCopyData conn f g
125− CopyOutDone count -> g acc count
126−
127−
128−-- | Retrieve some data from a @COPY TO STDOUT@ query. A connection
129−-- must be in the @CopyOut@ state in order to call this function. If this
130−-- returns a 'CopyOutRow', the connection remains in the @CopyOut@ state,
131−-- if it returns 'CopyOutDone', then the connection has reverted to the
132−-- ready state.
133−
134−getCopyData :: Connection -> IO CopyOutResult
135−getCopyData conn = withConnection conn loop
136− where
137− funcName = "Database.PostgreSQL.Simple.Copy.getCopyData"
138− loop pqconn = do
139−#if defined(mingw32_HOST_OS)
140− row <- PQ.getCopyData pqconn False
141−#else
142− row <- PQ.getCopyData pqconn True
143−#endif
144− case row of
145− PQ.CopyOutRow rowdata -> return $! CopyOutRow rowdata
146− PQ.CopyOutDone -> CopyOutDone <$> getCopyCommandTag funcName pqconn
147−#if defined(mingw32_HOST_OS)
148− PQ.CopyOutWouldBlock -> do
149− fail (B.unpack funcName ++ ": the impossible happened")
150−#else
151− PQ.CopyOutWouldBlock -> do
152− mfd <- PQ.socket pqconn
153− case mfd of
154− Nothing -> throwIO (fdError funcName)
155− Just fd -> do
156− threadWaitRead fd
157− _ <- PQ.consumeInput pqconn
158− loop pqconn
159−#endif
160− PQ.CopyOutError -> do
161− mmsg <- PQ.errorMessage pqconn
162− throwIO SqlError {
163− sqlState = "",
164− sqlExecStatus = FatalError,
165− sqlErrorMsg = maybe "" id mmsg,
166− sqlErrorDetail = "",
167− sqlErrorHint = funcName
168− }
169−
170−
171−-- | Feed some data to a @COPY FROM STDIN@ query. Note that
172−-- the data does not need to represent a single row, or even an
173−-- integral number of rows. The net result of
174−-- @putCopyData conn a >> putCopyData conn b@
175−-- is the same as @putCopyData conn c@ whenever @c == BS.append a b@.
176−--
177−-- A connection must be in the @CopyIn@ state in order to call this
178−-- function, otherwise a 'SqlError' exception will result. The
179−-- connection remains in the @CopyIn@ state after this function
180−-- is called.
181−
182−putCopyData :: Connection -> B.ByteString -> IO ()
183−putCopyData conn dat = withConnection conn $ \pqconn -> do
184− doCopyIn funcName (\c -> PQ.putCopyData c dat) pqconn
185− where
186− funcName = "Database.PostgreSQL.Simple.Copy.putCopyData"
187−
188−
189−-- | Completes a @COPY FROM STDIN@ query. Returns the number of rows
190−-- processed.
191−--
192−-- A connection must be in the @CopyIn@ state in order to call this
193−-- function, otherwise a 'SqlError' exception will result. The
194−-- connection's state changes back to ready after this function
195−-- is called.
196−
197−putCopyEnd :: Connection -> IO Int64
198−putCopyEnd conn = withConnection conn $ \pqconn -> do
199− doCopyIn funcName (\c -> PQ.putCopyEnd c Nothing) pqconn
200− getCopyCommandTag funcName pqconn
201− where
202− funcName = "Database.PostgreSQL.Simple.Copy.putCopyEnd"
203−
204−
205−-- | Aborts a @COPY FROM STDIN@ query. The string parameter is simply
206−-- an arbitrary error message that may show up in the PostgreSQL
207−-- server's log.
208−--
209−-- A connection must be in the @CopyIn@ state in order to call this
210−-- function, otherwise a 'SqlError' exception will result. The
211−-- connection's state changes back to ready after this function
212−-- is called.
213−
214−putCopyError :: Connection -> B.ByteString -> IO ()
215−putCopyError conn err = withConnection conn $ \pqconn -> do
216− doCopyIn funcName (\c -> PQ.putCopyEnd c (Just err)) pqconn
217− consumeResults pqconn
218− where
219− funcName = "Database.PostgreSQL.Simple.Copy.putCopyError"
220−
221−
222−doCopyIn :: B.ByteString -> (PQ.Connection -> IO PQ.CopyInResult)
223− -> PQ.Connection -> IO ()
224−doCopyIn funcName action = loop
225− where
226− loop pqconn = do
227− stat <- action pqconn
228− case stat of
229− PQ.CopyInOk -> return ()
230− PQ.CopyInError -> do
231− mmsg <- PQ.errorMessage pqconn
232− throwIO SqlError {
233− sqlState = "",
234− sqlExecStatus = FatalError,
235− sqlErrorMsg = maybe "" id mmsg,
236− sqlErrorDetail = "",
237− sqlErrorHint = funcName
238− }
239− PQ.CopyInWouldBlock -> do
240− mfd <- PQ.socket pqconn
241− case mfd of
242− Nothing -> throwIO (fdError funcName)
243− Just fd -> do
244− threadWaitWrite fd
245− loop pqconn
246−{-# INLINE doCopyIn #-}
247−
248−getCopyCommandTag :: B.ByteString -> PQ.Connection -> IO Int64
249−getCopyCommandTag funcName pqconn = do
250− result <- maybe (fail errCmdStatus) return =<< PQ.getResult pqconn
251− cmdStat <- maybe (fail errCmdStatus) return =<< PQ.cmdStatus result
252− consumeResults pqconn
253− let rowCount = P.string "COPY " *> (P.decimal <* P.endOfInput)
254− case P.parseOnly rowCount cmdStat of
255− Left _ -> do mmsg <- PQ.errorMessage pqconn
256− fail $ errCmdStatusFmt
257− ++ maybe "" (\msg -> "\nConnection error: "++B.unpack msg) mmsg
258− Right n -> return $! n
259− where
260− errCmdStatus = B.unpack funcName ++ ": failed to fetch command status"
261− errCmdStatusFmt = B.unpack funcName ++ ": failed to parse command status"
262−
263−
264−consumeResults :: PQ.Connection -> IO ()
265−consumeResults pqconn = do
266− mres <- PQ.getResult pqconn
267− case mres of
268− Nothing -> return ()
269− Just _ -> consumeResults pqconn
src/Database/PostgreSQL/Simple/Cursor.hsdeleted−98
@@ -1,98 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−
3−------------------------------------------------------------------------------
4−-- |
5−-- Module: Database.PostgreSQL.Simple.Cursor
6−-- Copyright: (c) 2011-2012 Leon P Smith
7−-- (c) 2017 Bardur Arantsson
8−-- License: BSD3
9−-- Maintainer: Leon P Smith <leon@melding-monads.com>
10−--
11−------------------------------------------------------------------------------
12−
13−module Database.PostgreSQL.Simple.Cursor
14− (
15− -- * Types
16− Cursor
17− -- * Cursor management
18− , declareCursor
19− , closeCursor
20− -- * Folding over rows from a cursor
21− , foldForward
22− , foldForwardWithParser
23− ) where
24−
25−import Data.ByteString.Builder (intDec)
26−import Control.Applicative ((<$>))
27−import Control.Exception as E
28−import Control.Monad (unless, void)
29−import Data.Monoid (mconcat)
30−import Database.PostgreSQL.Simple.Compat ((<>), toByteString)
31−import Database.PostgreSQL.Simple.FromRow (FromRow(..))
32−import Database.PostgreSQL.Simple.Types (Query(..))
33−import Database.PostgreSQL.Simple.Internal as Base hiding (result, row)
34−import Database.PostgreSQL.Simple.Internal.PQResultUtils
35−import Database.PostgreSQL.Simple.Transaction
36−import qualified Database.PostgreSQL.LibPQ as PQ
37−
38−-- | Cursor within a transaction.
39−data Cursor = Cursor !Query !Connection
40−
41−-- | Declare a temporary cursor. The cursor is given a
42−-- unique name for the given connection.
43−declareCursor :: Connection -> Query -> IO Cursor
44−declareCursor conn q = do
45− name <- newTempName conn
46− void $ execute_ conn $ mconcat ["DECLARE ", name, " NO SCROLL CURSOR FOR ", q]
47− return $ Cursor name conn
48−
49−-- | Close the given cursor.
50−closeCursor :: Cursor -> IO ()
51−closeCursor (Cursor name conn) =
52− (void $ execute_ conn ("CLOSE " <> name)) `E.catch` \ex ->
53− -- Don't throw exception if CLOSE failed because the transaction is
54− -- aborted. Otherwise, it will throw away the original error.
55− unless (isFailedTransactionError ex) $ throwIO ex
56−
57−-- | Fold over a chunk of rows from the given cursor, calling the
58−-- supplied fold-like function on each row as it is received. In case
59−-- the cursor is exhausted, a 'Left' value is returned, otherwise a
60−-- 'Right' value is returned.
61−foldForwardWithParser :: Cursor -> RowParser r -> Int -> (a -> r -> IO a) -> a -> IO (Either a a)
62−foldForwardWithParser (Cursor name conn) parser chunkSize f a0 = do
63− let q = "FETCH FORWARD "
64− <> (toByteString $ intDec chunkSize)
65− <> " FROM "
66− <> fromQuery name
67− result <- exec conn q
68− status <- PQ.resultStatus result
69− case status of
70− PQ.TuplesOk -> do
71− nrows <- PQ.ntuples result
72− ncols <- PQ.nfields result
73− if nrows > 0
74− then do
75− let inner a row = do
76− x <- getRowWith parser row ncols conn result
77− f a x
78− Right <$> foldM' inner a0 0 (nrows - 1)
79− else
80− return $ Left a0
81− _ -> throwResultError "foldForwardWithParser" result status
82−
83−-- | Fold over a chunk of rows, calling the supplied fold-like function
84−-- on each row as it is received. In case the cursor is exhausted,
85−-- a 'Left' value is returned, otherwise a 'Right' value is returned.
86−foldForward :: FromRow r => Cursor -> Int -> (a -> r -> IO a) -> a -> IO (Either a a)
87−foldForward cursor = foldForwardWithParser cursor fromRow
88−
89−
90−foldM' :: (Ord n, Num n) => (a -> n -> IO a) -> a -> n -> n -> IO a
91−foldM' f a lo hi = loop a lo
92− where
93− loop x !n
94− | n > hi = return x
95− | otherwise = do
96− x' <- f x n
97− loop x' (n+1)
98−{-# INLINE foldM' #-}
src/Database/PostgreSQL/Simple/Errors.hsdeleted−140
@@ -1,140 +0,0 @@
1−{-# LANGUAGE DeriveDataTypeable #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE RecordWildCards #-}
4−------------------------------------------------------------------------------
5−-- |
6−-- Module: Database.PostgreSQL.Simple.Errors
7−-- Copyright: (c) 2012-2013 Leonid Onokhov, Joey Adams
8−-- License: BSD3
9−-- Maintainer: Leon P Smith <leon@melding-monads.com>
10−-- Stability: experimental
11−--
12−-- | Module for parsing errors from postgresql error messages.
13−-- Currently only parses integrity violation errors (class 23).
14−--
15−-- /Note: Success of parsing may depend on language settings./
16−----------------------------------------------------------
17−module Database.PostgreSQL.Simple.Errors
18− ( ConstraintViolation(..)
19− , constraintViolation
20− , constraintViolationE
21− , catchViolation
22− , isSerializationError
23− , isNoActiveTransactionError
24− , isFailedTransactionError
25− )
26− where
27−
28−import Control.Exception as E
29−
30−import Data.Attoparsec.ByteString.Char8
31−import Data.ByteString (ByteString)
32−import Data.Typeable
33−
34−import Database.PostgreSQL.Simple.Internal
35−
36−-- Examples of parsed error messages
37−--
38−-- `ERROR: new row for relation "users" violates check
39−-- constraint "user_kind_check"`
40−--
41−-- `ERROR: insert or update on table "user_group_map" violates foreign key
42−-- constraint "user_id"`
43−--
44−-- `ERROR: null value in column "login" violates not-null constraint`
45−--
46−-- `ERROR: duplicate key value violates unique constraint "users_login_key"`
47−
48−data ConstraintViolation
49− = NotNullViolation ByteString
50− -- ^ The field is a column name
51− | ForeignKeyViolation ByteString ByteString
52− -- ^ Table name and name of violated constraint
53− | UniqueViolation ByteString
54− -- ^ Name of violated constraint
55− | CheckViolation ByteString ByteString
56− -- ^ Relation name (usually table), constraint name
57− | ExclusionViolation ByteString
58− -- ^ Name of the exclusion violation constraint
59− deriving (Show, Eq, Ord, Typeable)
60−
61−-- Default instance should be enough
62−instance Exception ConstraintViolation where
63− toException = postgresqlExceptionToException
64− fromException = postgresqlExceptionFromException
65−
66−
67−-- | Tries to convert 'SqlError' to 'ConstrainViolation', checks sqlState and
68−-- succeeds only if able to parse sqlErrorMsg.
69−--
70−-- > createUser = handleJust constraintViolation handler $ execute conn ...
71−-- > where
72−-- > handler (UniqueViolation "user_login_key") = ...
73−-- > handler _ = ...
74−constraintViolation :: SqlError -> Maybe ConstraintViolation
75−constraintViolation e =
76− case sqlState e of
77− "23502" -> NotNullViolation <$> parseMaybe parseQ1 msg
78− "23503" -> uncurry ForeignKeyViolation <$> parseMaybe parseQ2 msg
79− "23505" -> UniqueViolation <$> parseMaybe parseQ1 msg
80− "23514" -> uncurry CheckViolation <$> parseMaybe parseQ2 msg
81− "23P01" -> ExclusionViolation <$> parseMaybe parseQ1 msg
82− _ -> Nothing
83− where msg = sqlErrorMsg e
84−
85−
86−-- | Like constraintViolation, but also packs original SqlError.
87−--
88−-- > createUser = handleJust constraintViolationE handler $ execute conn ...
89−-- > where
90−-- > handler (_, UniqueViolation "user_login_key") = ...
91−-- > handler (e, _) = throwIO e
92−--
93−constraintViolationE :: SqlError -> Maybe (SqlError, ConstraintViolation)
94−constraintViolationE e = fmap ((,) e) $ constraintViolation e
95−
96−-- | Catches SqlError, tries to convert to ConstraintViolation, re-throws
97−-- on fail. Provides alternative interface to 'E.handleJust'
98−--
99−-- > createUser = catchViolation catcher $ execute conn ...
100−-- > where
101−-- > catcher _ (UniqueViolation "user_login_key") = ...
102−-- > catcher e _ = throwIO e
103−catchViolation :: (SqlError -> ConstraintViolation -> IO a) -> IO a -> IO a
104−catchViolation f m = E.catch m
105− (\e -> maybe (throwIO e) (f e) $ constraintViolation e)
106−
107−-- Parsers just try to extract quoted strings from error messages, number
108−-- of quoted strings depend on error type.
109−scanTillQuote :: Parser ByteString
110−scanTillQuote = scan False go
111− where go True _ = Just False -- escaped character
112− go False '"' = Nothing -- end parse
113− go False '\\' = Just True -- next one is escaped
114− go _ _ = Just False
115−
116−parseQ1 :: Parser ByteString
117−parseQ1 = scanTillQuote *> char '"' *> scanTillQuote <* char '"'
118−
119−parseQ2 :: Parser (ByteString, ByteString)
120−parseQ2 = (,) <$> parseQ1 <*> parseQ1
121−
122−parseMaybe :: Parser a -> ByteString -> Maybe a
123−parseMaybe p b = either (const Nothing) Just $ parseOnly p b
124−
125−------------------------------------------------------------------------
126−-- Error predicates
127−--
128−-- https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html
129−
130−isSerializationError :: SqlError -> Bool
131−isSerializationError = isSqlState "40001"
132−
133−isNoActiveTransactionError :: SqlError -> Bool
134−isNoActiveTransactionError = isSqlState "25P01"
135−
136−isFailedTransactionError :: SqlError -> Bool
137−isFailedTransactionError = isSqlState "25P02"
138−
139−isSqlState :: ByteString -> SqlError -> Bool
140−isSqlState s SqlError{..} = sqlState == s
src/Database/PostgreSQL/Simple/Field.hsnew file+15
@@ -0,0 +1,15 @@
1+module Database.PostgreSQL.Simple.Field
2+ ( Field
3+ , typename
4+ , name
5+ , tableOid
6+ , tableColumn
7+ , format
8+ , typeOid
9+ , Oid
10+ , Format(..)
11+ , RawResult(..)
12+ ) where
13+
14+import Database.PostgreSQL.Simple.Implementation
15+import Database.PostgreSQL.LibPQ (Format(..), Oid)
src/Database/PostgreSQL/Simple/FromField.hsdeleted−708
@@ -1,708 +0,0 @@
1−{-# LANGUAGE CPP, DeriveDataTypeable, DeriveFunctor #-}
2−{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
3−{-# LANGUAGE PatternGuards, ScopedTypeVariables #-}
4−{-# LANGUAGE RecordWildCards #-}
5−{-# LANGUAGE PolyKinds #-}
6−
7−{- |
8−Module: Database.PostgreSQL.Simple.FromField
9−Copyright: (c) 2011 MailRank, Inc.
10− (c) 2011-2013 Leon P Smith
11−License: BSD3
12−Maintainer: Leon P Smith <leon@melding-monads.com>
13−Stability: experimental
14−
15−The 'FromField' typeclass, for converting a single value in a row
16−returned by a SQL query into a more useful Haskell representation.
17−Note that each instance of 'FromField' is documented by a list of
18−compatible postgresql types.
19−
20−A Haskell numeric type is considered to be compatible with all
21−PostgreSQL numeric types that are less accurate than it. For instance,
22−the Haskell 'Double' type is compatible with the PostgreSQL's 32-bit
23−@int@ type because it can represent a @int@ exactly. On the other hand,
24−since a 'Double' might lose precision if representing PostgreSQL's 64-bit
25−@bigint@, the two are /not/ considered compatible.
26−
27−Note that the 'Float' and 'Double' instances use attoparsec's 'double'
28−conversion routine, which sacrifices some accuracy for speed. If you
29−need accuracy, consider first converting data to a 'Scientific' or 'Rational'
30−type, and then converting to a floating-point type. If you are defining
31−your own 'Database.PostgreSQL.Simple.FromRow.FromRow' instances, this can be
32−achieved simply by
33−@'fromRational' '<$>' 'Database.PostgreSQL.Simple.FromRow.field'@, although
34−this idiom is additionally compatible with PostgreSQL's @int8@ and @numeric@
35−types. If this is unacceptable, you may find
36−'Database.PostgreSQL.Simple.FromRow.fieldWith' useful.
37−
38−Also note that while converting to a 'Double' through the 'Scientific' type
39−is likely somewhat faster than converting through the 'Rational' type,
40−the 'Scientific' type has no way to represent @NaN@ and @±Infinity@ values.
41−Thus, if you need precision conversion of regular floating point values
42−and the possibility of receiving these special values from the backend,
43−stick with 'Rational'.
44−
45−Because 'FromField' is a typeclass, one may provide conversions to
46−additional Haskell types without modifying postgresql-simple. This is
47−particularly useful for supporting PostgreSQL types that postgresql-simple
48−does not support out-of-box. Here's an example of what such an instance
49−might look like for a UUID type that implements the @Read@ class:
50−
51−@
52−import Data.UUID ( UUID )
53−import Database.PostgreSQL.Simple.FromField
54− ( FromField (fromField) , typeOid, returnError, ResultError (..) )
55−import Database.PostgreSQL.Simple.TypeInfo.Static (typoid, uuid)
56−import qualified Data.ByteString.Char8 as B
57−
58−instance FromField UUID where
59− fromField f mdata =
60− if typeOid f /= typoid uuid
61− then returnError Incompatible f \"\"
62− else case B.unpack \`fmap\` mdata of
63− Nothing -> returnError UnexpectedNull f \"\"
64− Just dat ->
65− case [ x | (x,t) <- reads dat, (\"\",\"\") <- lex t ] of
66− [x] -> return x
67− _ -> returnError ConversionFailed f dat
68−@
69−
70−Note that because PostgreSQL's @uuid@ type is built into postgres and is
71−not provided by an extension, the 'typeOid' of @uuid@ does not change and
72−thus we can examine it directly. One could hard-code the type oid, or
73−obtain it by other means, but in this case we simply pull it out of the
74−static table provided by postgresql-simple.
75−
76−On the other hand if the type is provided by an extension, such as
77−@PostGIS@ or @hstore@, then the 'typeOid' is not stable and can vary from
78−database to database. In this case it is recommended that FromField
79−instances use 'typename' instead.
80−
81−-}
82−
83−
84−module Database.PostgreSQL.Simple.FromField
85− (
86− FromField(..)
87− , FieldParser
88− , Conversion()
89−
90− , runConversion
91− , conversionMap
92− , conversionError
93− , ResultError(..)
94− , returnError
95−
96− , Field
97− , typename
98− , TypeInfo(..)
99− , Attribute(..)
100− , typeInfo
101− , typeInfoByOid
102− , name
103− , tableOid
104− , tableColumn
105− , format
106− , typeOid
107− , PQ.Oid(..)
108− , PQ.Format(..)
109− , pgArrayFieldParser
110− , attoFieldParser
111−
112− , optionalField
113− , fromJSONField
114− , fromFieldJSONByteString
115− ) where
116−
117−#include "MachDeps.h"
118−
119−import Control.Applicative ( Const(Const), (<|>), (<$>), pure, (*>), (<*) )
120−import Control.Concurrent.MVar (MVar, newMVar)
121−import Control.Exception (Exception (toException, fromException))
122−import qualified Data.Aeson as JSON
123−import Data.Attoparsec.ByteString.Char8 hiding (Result)
124−import Data.ByteString (ByteString)
125−import qualified Data.ByteString.Char8 as B
126−import Data.Functor.Identity (Identity(Identity))
127−import Data.Int (Int16, Int32, Int64)
128−import Data.IORef (IORef, newIORef)
129−import Data.Ratio (Ratio)
130−import Data.Time.Compat ( UTCTime, ZonedTime, LocalTime, Day, TimeOfDay, CalendarDiffTime )
131−import Data.Typeable (Typeable, typeOf)
132−import Data.Vector (Vector)
133−import Data.Vector.Mutable (IOVector)
134−import qualified Data.Vector as V
135−import Database.PostgreSQL.Simple.Internal
136−import Database.PostgreSQL.Simple.Compat
137−import Database.PostgreSQL.Simple.Ok
138−import Database.PostgreSQL.Simple.Types
139−import Database.PostgreSQL.Simple.TypeInfo as TI
140−import qualified Database.PostgreSQL.Simple.TypeInfo.Static as TI
141−import Database.PostgreSQL.Simple.Time
142−import Database.PostgreSQL.Simple.Arrays as Arrays
143−import qualified Database.PostgreSQL.LibPQ as PQ
144−import qualified Data.ByteString as SB
145−import qualified Data.ByteString.Char8 as B8
146−import qualified Data.ByteString.Lazy as LB
147−import qualified Data.Text as ST
148−import qualified Data.Text.Encoding as ST
149−import qualified Data.Text.Lazy as LT
150−import Data.CaseInsensitive (CI)
151−import qualified Data.CaseInsensitive as CI
152−import Data.UUID.Types (UUID)
153−import qualified Data.UUID.Types as UUID
154−import Data.Scientific (Scientific)
155−import GHC.Real (infinity, notANumber)
156−
157−import qualified Data.Aeson.Types as JSON
158−
159−-- | Exception thrown if conversion from a SQL value to a Haskell
160−-- value fails.
161−data ResultError = Incompatible { errSQLType :: String
162− , errSQLTableOid :: Maybe PQ.Oid
163− , errSQLField :: String
164− , errHaskellType :: String
165− , errMessage :: String }
166− -- ^ The SQL and Haskell types are not compatible.
167− | UnexpectedNull { errSQLType :: String
168− , errSQLTableOid :: Maybe PQ.Oid
169− , errSQLField :: String
170− , errHaskellType :: String
171− , errMessage :: String }
172− -- ^ A SQL @NULL@ was encountered when the Haskell
173− -- type did not permit it.
174− | ConversionFailed { errSQLType :: String
175− , errSQLTableOid :: Maybe PQ.Oid
176− , errSQLField :: String
177− , errHaskellType :: String
178− , errMessage :: String }
179− -- ^ The SQL value could not be parsed, or could not
180− -- be represented as a valid Haskell value, or an
181− -- unexpected low-level error occurred (e.g. mismatch
182− -- between metadata and actual data in a row).
183− deriving (Eq, Show, Typeable)
184−
185−instance Exception ResultError where
186− toException = postgresqlExceptionToException
187− fromException = postgresqlExceptionFromException
188−
189−left :: Exception a => a -> Conversion b
190−left = conversionError
191−
192−type FieldParser a = Field -> Maybe ByteString -> Conversion a
193−
194−-- | A type that may be converted from a SQL type.
195−class FromField a where
196− fromField :: FieldParser a
197− -- ^ Convert a SQL value to a Haskell value.
198− --
199− -- Returns a list of exceptions if the conversion fails. In the case of
200− -- library instances, this will usually be a single 'ResultError', but
201− -- may be a 'UnicodeException'.
202− --
203− -- Note that retaining any reference to the 'Field' argument causes
204− -- the entire @LibPQ.'PQ.Result'@ to be retained. Thus, implementations
205− -- of 'fromField' should return results that do not refer to this value
206− -- after the result have been evaluated to WHNF.
207− --
208− -- Note that as of @postgresql-simple-0.4.0.0@, the 'ByteString' value
209− -- has already been copied out of the @LibPQ.'PQ.Result'@ before it has
210− -- been passed to 'fromField'. This is because for short strings, it's
211− -- cheaper to copy the string than to set up a finalizer.
212−
213−-- | Returns the data type name. This is the preferred way of identifying
214−-- types that do not have a stable type oid, such as types provided by
215−-- extensions to PostgreSQL.
216−--
217−-- More concretely, it returns the @typname@ column associated with the
218−-- type oid in the @pg_type@ table. First, postgresql-simple will check
219−-- the built-in, static table. If the type oid is not there,
220−-- postgresql-simple will check a per-connection cache, and then
221−-- finally query the database's meta-schema.
222−
223−typename :: Field -> Conversion ByteString
224−typename field = typname <$> typeInfo field
225−
226−typeInfo :: Field -> Conversion TypeInfo
227−typeInfo Field{..} = Conversion $ \conn -> do
228− Ok <$> (getTypeInfo conn typeOid)
229−
230−typeInfoByOid :: PQ.Oid -> Conversion TypeInfo
231−typeInfoByOid oid = Conversion $ \conn -> do
232− Ok <$> (getTypeInfo conn oid)
233−
234−-- | Returns the name of the column. This is often determined by a table
235−-- definition, but it can be set using an @as@ clause.
236−
237−name :: Field -> Maybe ByteString
238−name Field{..} = unsafeDupablePerformIO (PQ.fname result column)
239−
240−-- | Returns the name of the object id of the @table@ associated with the
241−-- column, if any. Returns 'Nothing' when there is no such table;
242−-- for example a computed column does not have a table associated with it.
243−-- Analogous to libpq's @PQftable@.
244−
245−tableOid :: Field -> Maybe PQ.Oid
246−tableOid Field{..} = toMaybeOid (unsafeDupablePerformIO (PQ.ftable result column))
247− where
248− toMaybeOid x
249− = if x == PQ.invalidOid
250− then Nothing
251− else Just x
252−
253−-- | If the column has a table associated with it, this returns the
254−-- number of the associated table column. Table columns have
255−-- nonzero numbers. Zero is returned if the specified column is not
256−-- a simple reference to a table column, or when using pre-3.0
257−-- protocol. Analogous to libpq's @PQftablecol@.
258−
259−tableColumn :: Field -> Int
260−tableColumn Field{..} = fromCol (unsafeDupablePerformIO (PQ.ftablecol result column))
261− where
262− fromCol (PQ.Col x) = fromIntegral x
263−
264−-- | This returns whether the data was returned in a binary or textual format.
265−-- Analogous to libpq's @PQfformat@.
266−
267−format :: Field -> PQ.Format
268−format Field{..} = unsafeDupablePerformIO (PQ.fformat result column)
269−
270−-- | void
271−instance FromField () where
272− fromField f _bs
273− | typeOid f /= TI.voidOid = returnError Incompatible f ""
274− | otherwise = pure ()
275−
276−instance (FromField a) => FromField (Const a b) where
277− fromField f bs = Const <$> fromField f bs
278−
279−instance (FromField a) => FromField (Identity a) where
280− fromField f bs = Identity <$> fromField f bs
281−
282−-- | For dealing with null values. Compatible with any postgresql type
283−-- compatible with type @a@. Note that the type is not checked if
284−-- the value is null, although it is inadvisable to rely on this
285−-- behavior.
286−instance FromField a => FromField (Maybe a) where
287− fromField = optionalField fromField
288−
289−-- | For dealing with SQL @null@ values outside of the 'FromField' class.
290−-- Alternatively, one could use 'Control.Applicative.optional', but that
291−-- also turns type and conversion errors into 'Nothing', whereas this is
292−-- more specific and turns only @null@ values into 'Nothing'.
293−
294−optionalField :: FieldParser a -> FieldParser (Maybe a)
295−optionalField p f mv =
296− case mv of
297− Nothing -> pure Nothing
298− Just _ -> Just <$> p f mv
299−{-# INLINE optionalField #-}
300−
301−-- | compatible with any data type, but the value must be null
302−instance FromField Null where
303− fromField _ Nothing = pure Null
304− fromField f (Just _) = returnError ConversionFailed f "data is not null"
305−
306−-- | bool
307−instance FromField Bool where
308− fromField f bs
309− | typeOid f /= TI.boolOid = returnError Incompatible f ""
310− | bs == Nothing = returnError UnexpectedNull f ""
311− | bs == Just "t" = pure True
312− | bs == Just "f" = pure False
313− | otherwise = returnError ConversionFailed f ""
314−
315−-- | \"char\", bpchar
316−instance FromField Char where
317− fromField f bs0 =
318− if (eq TI.charOid \/ eq TI.bpcharOid) (typeOid f)
319− then case bs0 of
320− Nothing -> returnError UnexpectedNull f ""
321− Just bs -> if B.length bs /= 1
322− then returnError ConversionFailed f "length not 1"
323− else return $! (B.head bs)
324− else returnError Incompatible f ""
325−
326−-- | int2
327−instance FromField Int16 where
328− fromField = attoFieldParser ok16 $ signed decimal
329−
330−-- | int2, int4
331−instance FromField Int32 where
332− fromField = attoFieldParser ok32 $ signed decimal
333−
334−#if WORD_SIZE_IN_BITS < 64
335−-- | int2, int4, and if compiled as 64-bit code, int8 as well.
336−-- This library was compiled as 32-bit code.
337−#else
338−-- | int2, int4, and if compiled as 64-bit code, int8 as well.
339−-- This library was compiled as 64-bit code.
340−#endif
341−instance FromField Int where
342− fromField = attoFieldParser okInt $ signed decimal
343−
344−-- | int2, int4, int8
345−instance FromField Int64 where
346− fromField = attoFieldParser ok64 $ signed decimal
347−
348−-- | int2, int4, int8
349−instance FromField Integer where
350− fromField = attoFieldParser ok64 $ signed decimal
351−
352−-- | int2, float4 (Uses attoparsec's 'double' routine, for
353−-- better accuracy convert to 'Scientific' or 'Rational' first)
354−instance FromField Float where
355− fromField = attoFieldParser ok (realToFrac <$> pg_double)
356− where ok = eq TI.float4Oid \/ eq TI.int2Oid
357−
358−-- | int2, int4, float4, float8 (Uses attoparsec's 'double' routine, for
359−-- better accuracy convert to 'Scientific' or 'Rational' first)
360−instance FromField Double where
361− fromField = attoFieldParser ok pg_double
362− where ok = eq TI.float4Oid \/ eq TI.float8Oid \/ eq TI.int2Oid \/ eq TI.int4Oid
363−
364−-- | int2, int4, int8, float4, float8, numeric
365−instance FromField (Ratio Integer) where
366− fromField = attoFieldParser ok pg_rational
367− where ok = eq TI.float4Oid \/ eq TI.float8Oid \/ eq TI.int2Oid \/ eq TI.int4Oid \/ eq TI.int8Oid \/ eq TI.numericOid
368−
369−-- | int2, int4, int8, float4, float8, numeric
370−instance FromField Scientific where
371− fromField = attoFieldParser ok rational
372− where ok = eq TI.float4Oid \/ eq TI.float8Oid \/ eq TI.int2Oid \/ eq TI.int4Oid \/ eq TI.int8Oid \/ eq TI.numericOid
373−
374−unBinary :: Binary t -> t
375−unBinary (Binary x) = x
376−
377−pg_double :: Parser Double
378−pg_double
379− = (string "NaN" *> pure ( 0 / 0))
380− <|> (string "Infinity" *> pure ( 1 / 0))
381− <|> (string "-Infinity" *> pure (-1 / 0))
382− <|> double
383−
384−pg_rational :: Parser Rational
385−pg_rational
386− = (string "NaN" *> pure notANumber )
387− <|> (string "Infinity" *> pure infinity )
388− <|> (string "-Infinity" *> pure (-infinity))
389− <|> rational
390−
391−-- | bytea, name, text, \"char\", bpchar, varchar, unknown
392−instance FromField SB.ByteString where
393− fromField f dat = if typeOid f == TI.byteaOid
394− then unBinary <$> fromField f dat
395− else doFromField f okText' pure dat
396−
397−-- | oid
398−instance FromField PQ.Oid where
399− fromField f dat = PQ.Oid <$> attoFieldParser (== TI.oidOid) decimal f dat
400−
401−-- | bytea, name, text, \"char\", bpchar, varchar, unknown
402−instance FromField LB.ByteString where
403− fromField f dat = LB.fromChunks . (:[]) <$> fromField f dat
404−
405−unescapeBytea :: Field -> SB.ByteString
406− -> Conversion (Binary SB.ByteString)
407−unescapeBytea f str' = case unsafeDupablePerformIO (PQ.unescapeBytea str') of
408− Nothing -> returnError ConversionFailed f "unescapeBytea failed"
409− Just str -> pure (Binary str)
410−
411−-- | bytea
412−instance FromField (Binary SB.ByteString) where
413− fromField f dat = case format f of
414− PQ.Text -> doFromField f okBinary (unescapeBytea f) dat
415− PQ.Binary -> doFromField f okBinary (pure . Binary) dat
416−
417−-- | bytea
418−instance FromField (Binary LB.ByteString) where
419− fromField f dat = Binary . LB.fromChunks . (:[]) . unBinary <$> fromField f dat
420−
421−-- | name, text, \"char\", bpchar, varchar
422−instance FromField ST.Text where
423− fromField f = doFromField f okText $ (either left pure . ST.decodeUtf8')
424− -- FIXME: check character encoding
425−
426−-- | name, text, \"char\", bpchar, varchar
427−instance FromField LT.Text where
428− fromField f dat = LT.fromStrict <$> fromField f dat
429−
430−-- | citext
431−instance FromField (CI ST.Text) where
432− fromField f mdat = do
433− typ <- typename f
434− if typ /= "citext"
435− then returnError Incompatible f ""
436− else case mdat of
437− Nothing -> returnError UnexpectedNull f ""
438− Just dat -> either left (pure . CI.mk)
439− (ST.decodeUtf8' dat)
440−
441−-- | citext
442−instance FromField (CI LT.Text) where
443− fromField f mdat = do
444− typ <- typename f
445− if typ /= "citext"
446− then returnError Incompatible f ""
447− else case mdat of
448− Nothing -> returnError UnexpectedNull f ""
449− Just dat -> either left (pure . CI.mk . LT.fromStrict)
450− (ST.decodeUtf8' dat)
451−
452−-- | name, text, \"char\", bpchar, varchar
453−instance FromField [Char] where
454− fromField f dat = ST.unpack <$> fromField f dat
455−
456−-- | timestamptz
457−instance FromField UTCTime where
458− fromField = ff TI.timestamptzOid "UTCTime" parseUTCTime
459−
460−-- | timestamptz
461−instance FromField ZonedTime where
462− fromField = ff TI.timestamptzOid "ZonedTime" parseZonedTime
463−
464−-- | timestamp
465−instance FromField LocalTime where
466− fromField = ff TI.timestampOid "LocalTime" parseLocalTime
467−
468−-- | date
469−instance FromField Day where
470− fromField = ff TI.dateOid "Day" parseDay
471−
472−-- | time
473−instance FromField TimeOfDay where
474− fromField = ff TI.timeOid "TimeOfDay" parseTimeOfDay
475−
476−-- | timestamptz
477−instance FromField UTCTimestamp where
478− fromField = ff TI.timestamptzOid "UTCTimestamp" parseUTCTimestamp
479−
480−-- | timestamptz
481−instance FromField ZonedTimestamp where
482− fromField = ff TI.timestamptzOid "ZonedTimestamp" parseZonedTimestamp
483−
484−-- | timestamp
485−instance FromField LocalTimestamp where
486− fromField = ff TI.timestampOid "LocalTimestamp" parseLocalTimestamp
487−
488−-- | date
489−instance FromField Date where
490− fromField = ff TI.dateOid "Date" parseDate
491−
492−-- | interval. Requires you to configure intervalstyle as @iso_8601@.
493−--
494−-- You can configure intervalstyle on every connection with a @SET@ command,
495−-- but for better performance you may want to configure it permanently in the
496−-- file found with @SHOW config_file;@ .
497−--
498−instance FromField CalendarDiffTime where
499− fromField = ff TI.intervalOid "CalendarDiffTime" parseCalendarDiffTime
500−
501−ff :: PQ.Oid -> String -> (B8.ByteString -> Either String a)
502− -> Field -> Maybe B8.ByteString -> Conversion a
503−ff compatOid hsType parseBS f mstr =
504− if typeOid f /= compatOid
505− then err Incompatible ""
506− else case mstr of
507− Nothing -> err UnexpectedNull ""
508− Just str -> case parseBS str of
509− Left msg -> err ConversionFailed msg
510− Right val -> return val
511− where
512− err errC msg = do
513− typnam <- typename f
514− left $ errC (B8.unpack typnam)
515− (tableOid f)
516− (maybe "" B8.unpack (name f))
517− hsType
518− msg
519−{-# INLINE ff #-}
520−
521−-- | Compatible with both types. Conversions to type @b@ are
522−-- preferred, the conversion to type @a@ will be tried after
523−-- the 'Right' conversion fails.
524−instance (FromField a, FromField b) => FromField (Either a b) where
525− fromField f dat = (Right <$> fromField f dat)
526− <|> (Left <$> fromField f dat)
527−
528−-- | any postgresql array whose elements are compatible with type @a@
529−instance (FromField a, Typeable a) => FromField (PGArray a) where
530− fromField = pgArrayFieldParser fromField
531−
532−pgArrayFieldParser :: Typeable a => FieldParser a -> FieldParser (PGArray a)
533−pgArrayFieldParser fieldParser f mdat = do
534− info <- typeInfo f
535− case info of
536− TI.Array{} ->
537− case mdat of
538− Nothing -> returnError UnexpectedNull f ""
539− Just dat -> do
540− case parseOnly (fromArray fieldParser info f) dat of
541− Left err -> returnError ConversionFailed f err
542− Right conv -> PGArray <$> conv
543− _ -> returnError Incompatible f ""
544−
545−fromArray :: FieldParser a -> TypeInfo -> Field -> Parser (Conversion [a])
546−fromArray fieldParser typInfo f = sequence . (parseIt <$>) <$> array delim
547− where
548− delim = typdelim (typelem typInfo)
549− fElem = f{ typeOid = typoid (typelem typInfo) }
550−
551− parseIt item =
552− fieldParser f' $ if item == Arrays.Plain "NULL" then Nothing else Just item'
553− where
554− item' = fmt delim item
555− f' | Arrays.Array _ <- item = f
556− | otherwise = fElem
557−
558−instance (FromField a, Typeable a) => FromField (Vector a) where
559− fromField f v = V.fromList . fromPGArray <$> fromField f v
560−
561−instance (FromField a, Typeable a) => FromField (IOVector a) where
562− fromField f v = liftConversion . V.unsafeThaw =<< fromField f v
563−
564−-- | uuid
565−instance FromField UUID where
566− fromField f mbs =
567− if typeOid f /= TI.uuidOid
568− then returnError Incompatible f ""
569− else case mbs of
570− Nothing -> returnError UnexpectedNull f ""
571− Just bs ->
572− case UUID.fromASCIIBytes bs of
573− Nothing -> returnError ConversionFailed f "Invalid UUID"
574− Just uuid -> pure uuid
575−
576−-- | json, jsonb
577−instance FromField JSON.Value where
578− fromField f mbs = parseBS =<< fromFieldJSONByteString f mbs
579− where parseBS bs = case JSON.eitherDecodeStrict' bs of
580− Left err -> returnError ConversionFailed f err
581− Right val -> pure val
582−
583−-- | Return the JSON ByteString directly
584−--
585−-- @since 0.6.3
586−fromFieldJSONByteString :: Field -> Maybe ByteString -> Conversion ByteString
587−fromFieldJSONByteString f mbs =
588− if typeOid f /= TI.jsonOid && typeOid f /= TI.jsonbOid
589− then returnError Incompatible f ""
590− else case mbs of
591− Nothing -> returnError UnexpectedNull f ""
592− Just bs -> pure bs
593−
594−-- | Parse a field to a JSON 'JSON.Value' and convert that into a
595−-- Haskell value using the 'JSON.FromJSON' instance.
596−--
597−-- This can be used as the default implementation for the 'fromField'
598−-- method for Haskell types that have a JSON representation in
599−-- PostgreSQL.
600−--
601−-- The 'Typeable' constraint is required to show more informative
602−-- error messages when parsing fails.
603−--
604−-- Note that @fromJSONField :: FieldParser ('Maybe' Foo)@ will return
605−-- @'Nothing'@ on the json @null@ value, and return an exception on SQL @null@
606−-- value. Alternatively, one could write @'optionalField' fromJSONField@
607−-- that will return @Nothing@ on SQL @null@, and otherwise will call
608−-- @fromJSONField :: FieldParser Foo@ and then return @'Just'@ the
609−-- result value, or return its exception. If one would
610−-- like to return @Nothing@ on both the SQL @null@ and json @null@ values,
611−-- one way to do it would be to write
612−-- @\\f mv -> 'Control.Monad.join' '<$>' optionalField fromJSONField f mv@
613−fromJSONField :: (JSON.FromJSON a, Typeable a) => FieldParser a
614−fromJSONField f mbBs = do
615− value <- fromField f mbBs
616− case JSON.ifromJSON value of
617− JSON.IError path err -> returnError ConversionFailed f $
618− "JSON decoding error: " ++ (JSON.formatError path err)
619− JSON.ISuccess x -> pure x
620−
621−-- | Compatible with the same set of types as @a@. Note that
622−-- modifying the 'IORef' does not have any effects outside
623−-- the local process on the local machine.
624−instance FromField a => FromField (IORef a) where
625− fromField f v = liftConversion . newIORef =<< fromField f v
626−
627−-- | Compatible with the same set of types as @a@. Note that
628−-- modifying the 'MVar' does not have any effects outside
629−-- the local process on the local machine.
630−instance FromField a => FromField (MVar a) where
631− fromField f v = liftConversion . newMVar =<< fromField f v
632−
633−type Compat = PQ.Oid -> Bool
634−
635−okText, okText', okBinary, ok16, ok32, ok64, okInt :: Compat
636−okText = eq TI.nameOid \/ eq TI.textOid \/ eq TI.charOid
637− \/ eq TI.bpcharOid \/ eq TI.varcharOid
638−okText' = eq TI.nameOid \/ eq TI.textOid \/ eq TI.charOid
639− \/ eq TI.bpcharOid \/ eq TI.varcharOid \/ eq TI.unknownOid
640−okBinary = eq TI.byteaOid
641−ok16 = eq TI.int2Oid
642−ok32 = eq TI.int2Oid \/ eq TI.int4Oid
643−ok64 = eq TI.int2Oid \/ eq TI.int4Oid \/ eq TI.int8Oid
644−#if WORD_SIZE_IN_BITS < 64
645−okInt = ok32
646−#else
647−okInt = ok64
648−#endif
649−
650−-- | eq and \/ are used to imlement what Macro stuff did,
651−-- i.e. mkCompats and inlineTypoid
652−eq :: PQ.Oid -> PQ.Oid -> Bool
653−eq = (==)
654−{-# INLINE eq #-}
655−
656−infixr 2 \/
657−(\/) :: (PQ.Oid -> Bool)
658− -> (PQ.Oid -> Bool)
659− -> (PQ.Oid -> Bool)
660−f \/ g = \x -> f x || g x
661−{-# INLINE (\/) #-}
662−
663−doFromField :: forall a . (Typeable a)
664− => Field -> Compat -> (ByteString -> Conversion a)
665− -> Maybe ByteString -> Conversion a
666−doFromField f isCompat cvt (Just bs)
667− | isCompat (typeOid f) = cvt bs
668− | otherwise = returnError Incompatible f "types incompatible"
669−doFromField f _ _ _ = returnError UnexpectedNull f ""
670−
671−
672−-- | Given one of the constructors from 'ResultError', the field,
673−-- and an 'errMessage', this fills in the other fields in the
674−-- exception value and returns it in a 'Left . SomeException'
675−-- constructor.
676−returnError :: forall a err . (Typeable a, Exception err)
677− => (String -> Maybe PQ.Oid -> String -> String -> String -> err)
678− -> Field -> String -> Conversion a
679−returnError mkErr f msg = do
680− typnam <- typename f
681− left $ mkErr (B.unpack typnam)
682− (tableOid f)
683− (maybe "" B.unpack (name f))
684− (show (typeOf (undefined :: a)))
685− msg
686−
687−-- | Construct a field parser from an attoparsec parser. An 'Incompatible' error is thrown if the
688−-- PostgreSQL oid does not match the specified predicate.
689−--
690−-- @
691−-- instance FromField Int16 where
692−-- fromField = attoFieldParser ok16 (signed decimal)
693−-- @
694−--
695−-- @since 0.6.3
696−attoFieldParser :: forall a. (Typeable a)
697− => (PQ.Oid -> Bool)
698− -- ^ Predicate for whether the postgresql type oid is compatible with this parser
699− -> Parser a
700− -- ^ An attoparsec parser.
701− -> FieldParser a
702−attoFieldParser types p0 f dat = doFromField f types (go p0) dat
703− where
704− go :: Parser a -> ByteString -> Conversion a
705− go p s =
706− case parseOnly p s of
707− Left err -> returnError ConversionFailed f err
708− Right v -> pure v
src/Database/PostgreSQL/Simple/FromField.hs-bootdeleted−11
@@ -1,11 +0,0 @@
1−module Database.PostgreSQL.Simple.FromField where
2−
3−import Data.ByteString(ByteString)
4−import Database.PostgreSQL.Simple.Types
5−
6−class FromField a
7−
8−instance FromField Oid
9−instance FromField Char
10−instance FromField ByteString
11−instance FromField a => FromField (Maybe a)
src/Database/PostgreSQL/Simple/FromRow.hsdeleted−487
@@ -1,487 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE RecordWildCards, FlexibleInstances, DefaultSignatures #-}
3−
4−
5−------------------------------------------------------------------------------
6−-- |
7−-- Module: Database.PostgreSQL.Simple.FromRow
8−-- Copyright: (c) 2012 Leon P Smith
9−-- License: BSD3
10−-- Maintainer: Leon P Smith <leon@melding-monads.com>
11−-- Stability: experimental
12−--
13−-- The 'FromRow' typeclass, for converting a row of results
14−-- returned by a SQL query into a more useful Haskell representation.
15−--
16−-- Predefined instances are provided for tuples containing up to ten
17−-- elements. The instances for 'Maybe' types return 'Nothing' if all
18−-- the columns that would have been otherwise consumed are null, otherwise
19−-- it attempts a regular conversion.
20−--
21−------------------------------------------------------------------------------
22−
23−module Database.PostgreSQL.Simple.FromRow
24− ( FromRow(..)
25− , RowParser
26− , field
27− , fieldWith
28− , numFieldsRemaining
29− ) where
30−
31−import Prelude hiding (null)
32−import Control.Applicative (Applicative(..), (<$>), (<|>), (*>), liftA2)
33−import Control.Monad (replicateM, replicateM_)
34−import Control.Monad.Trans.State.Strict
35−import Control.Monad.Trans.Reader
36−import Control.Monad.Trans.Class
37−import Data.ByteString (ByteString)
38−import qualified Data.ByteString.Char8 as B
39−import Data.Vector (Vector)
40−import qualified Data.Vector as V
41−import Database.PostgreSQL.Simple.Types (Only(..))
42−import qualified Database.PostgreSQL.LibPQ as PQ
43−import Database.PostgreSQL.Simple.Internal
44−import Database.PostgreSQL.Simple.Compat
45−import Database.PostgreSQL.Simple.FromField
46−import Database.PostgreSQL.Simple.Ok
47−import Database.PostgreSQL.Simple.Types ((:.)(..), Null)
48−import Database.PostgreSQL.Simple.TypeInfo
49−
50−import GHC.Generics
51−
52−
53−-- | A collection type that can be converted from a sequence of fields.
54−-- Instances are provided for tuples up to 10 elements and lists of any length.
55−--
56−-- Note that instances can be defined outside of postgresql-simple, which is
57−-- often useful. For example, here's an instance for a user-defined pair:
58−--
59−-- @
60−-- data User = User { name :: String, fileQuota :: Int }
61−--
62−-- instance 'FromRow' User where
63−-- fromRow = User \<$\> 'field' \<*\> 'field'
64−-- @
65−--
66−-- The number of calls to 'field' must match the number of fields returned
67−-- in a single row of the query result. Otherwise, a 'ConversionFailed'
68−-- exception will be thrown.
69−--
70−-- You can also derive 'FromRow' for your data type using GHC generics, like
71−-- this:
72−--
73−-- @
74−-- \{-# LANGUAGE DeriveAnyClass \#-}
75−-- \{-# LANGUAGE DeriveGeneric \#-}
76−--
77−-- import "GHC.Generics" ('GHC.Generics.Generic')
78−-- import "Database.PostgreSQL.Simple" ('FromRow')
79−--
80−-- data User = User { name :: String, fileQuota :: Int }
81−-- deriving ('GHC.Generics.Generic', 'FromRow')
82−-- @
83−--
84−-- Note that this only works for product types (e.g. records) and does not
85−-- support sum types or recursive types.
86−--
87−-- Note that 'field' evaluates its result to WHNF, so the caveats listed in
88−-- mysql-simple and very early versions of postgresql-simple no longer apply.
89−-- Instead, look at the caveats associated with user-defined implementations
90−-- of 'fromField'.
91−
92−class FromRow a where
93− fromRow :: RowParser a
94− default fromRow :: (Generic a, GFromRow (Rep a)) => RowParser a
95− fromRow = to <$> gfromRow
96−
97−getvalue :: PQ.Result -> PQ.Row -> PQ.Column -> Maybe ByteString
98−getvalue result row col = unsafeDupablePerformIO (PQ.getvalue' result row col)
99−
100−nfields :: PQ.Result -> PQ.Column
101−nfields result = unsafeDupablePerformIO (PQ.nfields result)
102−
103−getTypeInfoByCol :: Row -> PQ.Column -> Conversion TypeInfo
104−getTypeInfoByCol Row{..} col =
105− Conversion $ \conn -> do
106− oid <- PQ.ftype rowresult col
107− Ok <$> getTypeInfo conn oid
108−
109−getTypenameByCol :: Row -> PQ.Column -> Conversion ByteString
110−getTypenameByCol row col = typname <$> getTypeInfoByCol row col
111−
112−fieldWith :: FieldParser a -> RowParser a
113−fieldWith fieldP = RP $ do
114− let unCol (PQ.Col x) = fromIntegral x :: Int
115− r@Row{..} <- ask
116− column <- lift get
117− lift (put (column + 1))
118− let ncols = nfields rowresult
119− if (column >= ncols)
120− then lift $ lift $ do
121− vals <- mapM (getTypenameByCol r) [0..ncols-1]
122− let err = ConversionFailed
123− (show (unCol ncols) ++ " values: " ++ show (map ellipsis vals))
124− Nothing
125− ""
126− ("at least " ++ show (unCol column + 1)
127− ++ " slots in target type")
128− "mismatch between number of columns to \
129− \convert and number in target type"
130− conversionError err
131− else do
132− let !result = rowresult
133− !typeOid = unsafeDupablePerformIO (PQ.ftype result column)
134− !field' = Field{..}
135− lift (lift (fieldP field' (getvalue result row column)))
136−
137−field :: FromField a => RowParser a
138−field = fieldWith fromField
139−
140−ellipsis :: ByteString -> ByteString
141−ellipsis bs
142− | B.length bs > 15 = B.take 10 bs `B.append` "[...]"
143− | otherwise = bs
144−
145−numFieldsRemaining :: RowParser Int
146−numFieldsRemaining = RP $ do
147− Row{..} <- ask
148− column <- lift get
149− return $! (\(PQ.Col x) -> fromIntegral x) (nfields rowresult - column)
150−
151−null :: RowParser Null
152−null = field
153−
154−instance (FromField a) => FromRow (Only a) where
155− fromRow = Only <$> field
156−
157−instance (FromField a) => FromRow (Maybe (Only a)) where
158− fromRow = (null *> pure Nothing)
159− <|> (Just <$> fromRow)
160−
161−instance (FromField a, FromField b) => FromRow (a,b) where
162− fromRow = (,) <$> field <*> field
163−
164−instance (FromField a, FromField b) => FromRow (Maybe (a,b)) where
165− fromRow = (null *> null *> pure Nothing)
166− <|> (Just <$> fromRow)
167−
168−instance (FromField a, FromField b, FromField c) => FromRow (a,b,c) where
169− fromRow = (,,) <$> field <*> field <*> field
170−
171−instance (FromField a, FromField b, FromField c) => FromRow (Maybe (a,b,c)) where
172− fromRow = (null *> null *> null *> pure Nothing)
173− <|> (Just <$> fromRow)
174−
175−instance (FromField a, FromField b, FromField c, FromField d) =>
176− FromRow (a,b,c,d) where
177− fromRow = (,,,) <$> field <*> field <*> field <*> field
178−
179−instance (FromField a, FromField b, FromField c, FromField d) =>
180− FromRow (Maybe (a,b,c,d)) where
181− fromRow = (null *> null *> null *> null *> pure Nothing)
182− <|> (Just <$> fromRow)
183−
184−instance (FromField a, FromField b, FromField c, FromField d, FromField e) =>
185− FromRow (a,b,c,d,e) where
186− fromRow = (,,,,) <$> field <*> field <*> field <*> field <*> field
187−
188−instance (FromField a, FromField b, FromField c, FromField d, FromField e) =>
189− FromRow (Maybe (a,b,c,d,e)) where
190− fromRow = (null *> null *> null *> null *> null *> pure Nothing)
191− <|> (Just <$> fromRow)
192−
193−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
194− FromField f) =>
195− FromRow (a,b,c,d,e,f) where
196− fromRow = (,,,,,) <$> field <*> field <*> field <*> field <*> field
197− <*> field
198−
199−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
200− FromField f) =>
201− FromRow (Maybe (a,b,c,d,e,f)) where
202− fromRow = (null *> null *> null *> null *> null *>
203− null *> pure Nothing)
204− <|> (Just <$> fromRow)
205−
206−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
207− FromField f, FromField g) =>
208− FromRow (a,b,c,d,e,f,g) where
209− fromRow = (,,,,,,) <$> field <*> field <*> field <*> field <*> field
210− <*> field <*> field
211−
212−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
213− FromField f, FromField g) =>
214− FromRow (Maybe (a,b,c,d,e,f,g)) where
215− fromRow = (null *> null *> null *> null *> null *>
216− null *> null *> pure Nothing)
217− <|> (Just <$> fromRow)
218−
219−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
220− FromField f, FromField g, FromField h) =>
221− FromRow (a,b,c,d,e,f,g,h) where
222− fromRow = (,,,,,,,) <$> field <*> field <*> field <*> field <*> field
223− <*> field <*> field <*> field
224−
225−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
226− FromField f, FromField g, FromField h) =>
227− FromRow (Maybe (a,b,c,d,e,f,g,h)) where
228− fromRow = (null *> null *> null *> null *> null *>
229− null *> null *> null *> pure Nothing)
230− <|> (Just <$> fromRow)
231−
232−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
233− FromField f, FromField g, FromField h, FromField i) =>
234− FromRow (a,b,c,d,e,f,g,h,i) where
235− fromRow = (,,,,,,,,) <$> field <*> field <*> field <*> field <*> field
236− <*> field <*> field <*> field <*> field
237−
238−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
239− FromField f, FromField g, FromField h, FromField i) =>
240− FromRow (Maybe (a,b,c,d,e,f,g,h,i)) where
241− fromRow = (null *> null *> null *> null *> null *>
242− null *> null *> null *> null *> pure Nothing)
243− <|> (Just <$> fromRow)
244−
245−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
246− FromField f, FromField g, FromField h, FromField i, FromField j) =>
247− FromRow (a,b,c,d,e,f,g,h,i,j) where
248− fromRow = (,,,,,,,,,) <$> field <*> field <*> field <*> field <*> field
249− <*> field <*> field <*> field <*> field <*> field
250−
251−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
252− FromField f, FromField g, FromField h, FromField i, FromField j) =>
253− FromRow (Maybe (a,b,c,d,e,f,g,h,i,j)) where
254− fromRow = (null *> null *> null *> null *> null *>
255− null *> null *> null *> null *> null *> pure Nothing)
256− <|> (Just <$> fromRow)
257−
258−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
259− FromField f, FromField g, FromField h, FromField i, FromField j,
260− FromField k) =>
261− FromRow (a,b,c,d,e,f,g,h,i,j,k) where
262− fromRow = (,,,,,,,,,,) <$> field <*> field <*> field <*> field <*> field
263− <*> field <*> field <*> field <*> field <*> field
264− <*> field
265−
266−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
267− FromField f, FromField g, FromField h, FromField i, FromField j,
268− FromField k) =>
269− FromRow (Maybe (a,b,c,d,e,f,g,h,i,j,k)) where
270− fromRow = (null *> null *> null *> null *> null *>
271− null *> null *> null *> null *> null *>
272− null *> pure Nothing)
273− <|> (Just <$> fromRow)
274−
275−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
276− FromField f, FromField g, FromField h, FromField i, FromField j,
277− FromField k, FromField l) =>
278− FromRow (a,b,c,d,e,f,g,h,i,j,k,l) where
279− fromRow = (,,,,,,,,,,,) <$> field <*> field <*> field <*> field <*> field
280− <*> field <*> field <*> field <*> field <*> field
281− <*> field <*> field
282−
283−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
284− FromField f, FromField g, FromField h, FromField i, FromField j,
285− FromField k, FromField l) =>
286− FromRow (Maybe (a,b,c,d,e,f,g,h,i,j,k,l)) where
287− fromRow = (null *> null *> null *> null *> null *>
288− null *> null *> null *> null *> null *>
289− null *> null *> pure Nothing)
290− <|> (Just <$> fromRow)
291−
292−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
293− FromField f, FromField g, FromField h, FromField i, FromField j,
294− FromField k, FromField l, FromField m) =>
295− FromRow (a,b,c,d,e,f,g,h,i,j,k,l,m) where
296− fromRow = (,,,,,,,,,,,,) <$> field <*> field <*> field <*> field <*> field
297− <*> field <*> field <*> field <*> field <*> field
298− <*> field <*> field <*> field
299−
300−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
301− FromField f, FromField g, FromField h, FromField i, FromField j,
302− FromField k, FromField l, FromField m) =>
303− FromRow (Maybe (a,b,c,d,e,f,g,h,i,j,k,l,m)) where
304− fromRow = (null *> null *> null *> null *> null *>
305− null *> null *> null *> null *> null *>
306− null *> null *> null *> pure Nothing)
307− <|> (Just <$> fromRow)
308−
309−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
310− FromField f, FromField g, FromField h, FromField i, FromField j,
311− FromField k, FromField l, FromField m, FromField n) =>
312− FromRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n) where
313− fromRow = (,,,,,,,,,,,,,) <$> field <*> field <*> field <*> field <*> field
314− <*> field <*> field <*> field <*> field <*> field
315− <*> field <*> field <*> field <*> field
316−
317−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
318− FromField f, FromField g, FromField h, FromField i, FromField j,
319− FromField k, FromField l, FromField m, FromField n) =>
320− FromRow (Maybe (a,b,c,d,e,f,g,h,i,j,k,l,m,n)) where
321− fromRow = (null *> null *> null *> null *> null *>
322− null *> null *> null *> null *> null *>
323− null *> null *> null *> null *> pure Nothing)
324− <|> (Just <$> fromRow)
325−
326−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
327− FromField f, FromField g, FromField h, FromField i, FromField j,
328− FromField k, FromField l, FromField m, FromField n, FromField o) =>
329− FromRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) where
330− fromRow = (,,,,,,,,,,,,,,) <$> field <*> field <*> field <*> field <*> field
331− <*> field <*> field <*> field <*> field <*> field
332− <*> field <*> field <*> field <*> field <*> field
333−
334−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
335− FromField f, FromField g, FromField h, FromField i, FromField j,
336− FromField k, FromField l, FromField m, FromField n, FromField o) =>
337− FromRow (Maybe (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o)) where
338− fromRow = (null *> null *> null *> null *> null *>
339− null *> null *> null *> null *> null *>
340− null *> null *> null *> null *> null *> pure Nothing)
341− <|> (Just <$> fromRow)
342−
343−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
344− FromField f, FromField g, FromField h, FromField i, FromField j,
345− FromField k, FromField l, FromField m, FromField n, FromField o,
346− FromField p) =>
347− FromRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) where
348− fromRow = (,,,,,,,,,,,,,,,) <$> field <*> field <*> field <*> field <*> field
349− <*> field <*> field <*> field <*> field <*> field
350− <*> field <*> field <*> field <*> field <*> field
351− <*> field
352−
353−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
354− FromField f, FromField g, FromField h, FromField i, FromField j,
355− FromField k, FromField l, FromField m, FromField n, FromField o,
356− FromField p) =>
357− FromRow (Maybe (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p)) where
358− fromRow = (null *> null *> null *> null *> null *>
359− null *> null *> null *> null *> null *>
360− null *> null *> null *> null *> null *>
361− null *> pure Nothing)
362− <|> (Just <$> fromRow)
363−
364−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
365− FromField f, FromField g, FromField h, FromField i, FromField j,
366− FromField k, FromField l, FromField m, FromField n, FromField o,
367− FromField p, FromField q) =>
368− FromRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q) where
369− fromRow = (,,,,,,,,,,,,,,,,) <$> field <*> field <*> field <*> field <*> field
370− <*> field <*> field <*> field <*> field <*> field
371− <*> field <*> field <*> field <*> field <*> field
372− <*> field <*> field
373−
374−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
375− FromField f, FromField g, FromField h, FromField i, FromField j,
376− FromField k, FromField l, FromField m, FromField n, FromField o,
377− FromField p, FromField q) =>
378− FromRow (Maybe (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q)) where
379− fromRow = (null *> null *> null *> null *> null *>
380− null *> null *> null *> null *> null *>
381− null *> null *> null *> null *> null *>
382− null *> null *> pure Nothing)
383− <|> (Just <$> fromRow)
384−
385−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
386− FromField f, FromField g, FromField h, FromField i, FromField j,
387− FromField k, FromField l, FromField m, FromField n, FromField o,
388− FromField p, FromField q, FromField r) =>
389− FromRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r) where
390− fromRow = (,,,,,,,,,,,,,,,,,) <$> field <*> field <*> field <*> field <*> field
391− <*> field <*> field <*> field <*> field <*> field
392− <*> field <*> field <*> field <*> field <*> field
393− <*> field <*> field <*> field
394−
395−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
396− FromField f, FromField g, FromField h, FromField i, FromField j,
397− FromField k, FromField l, FromField m, FromField n, FromField o,
398− FromField p, FromField q, FromField r) =>
399− FromRow (Maybe (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r)) where
400− fromRow = (null *> null *> null *> null *> null *>
401− null *> null *> null *> null *> null *>
402− null *> null *> null *> null *> null *>
403− null *> null *> null *> pure Nothing)
404− <|> (Just <$> fromRow)
405−
406−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
407− FromField f, FromField g, FromField h, FromField i, FromField j,
408− FromField k, FromField l, FromField m, FromField n, FromField o,
409− FromField p, FromField q, FromField r, FromField s) =>
410− FromRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s) where
411− fromRow = (,,,,,,,,,,,,,,,,,,) <$> field <*> field <*> field <*> field <*> field
412− <*> field <*> field <*> field <*> field <*> field
413− <*> field <*> field <*> field <*> field <*> field
414− <*> field <*> field <*> field <*> field
415−
416−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
417− FromField f, FromField g, FromField h, FromField i, FromField j,
418− FromField k, FromField l, FromField m, FromField n, FromField o,
419− FromField p, FromField q, FromField r, FromField s) =>
420− FromRow (Maybe (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s)) where
421− fromRow = (null *> null *> null *> null *> null *>
422− null *> null *> null *> null *> null *>
423− null *> null *> null *> null *> null *>
424− null *> null *> null *> null *> pure Nothing)
425− <|> (Just <$> fromRow)
426−
427−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
428− FromField f, FromField g, FromField h, FromField i, FromField j,
429− FromField k, FromField l, FromField m, FromField n, FromField o,
430− FromField p, FromField q, FromField r, FromField s, FromField t) =>
431− FromRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t) where
432− fromRow = (,,,,,,,,,,,,,,,,,,,) <$> field <*> field <*> field <*> field <*> field
433− <*> field <*> field <*> field <*> field <*> field
434− <*> field <*> field <*> field <*> field <*> field
435− <*> field <*> field <*> field <*> field <*> field
436−
437−instance (FromField a, FromField b, FromField c, FromField d, FromField e,
438− FromField f, FromField g, FromField h, FromField i, FromField j,
439− FromField k, FromField l, FromField m, FromField n, FromField o,
440− FromField p, FromField q, FromField r, FromField s, FromField t) =>
441− FromRow (Maybe (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t)) where
442− fromRow = (null *> null *> null *> null *> null *>
443− null *> null *> null *> null *> null *>
444− null *> null *> null *> null *> null *>
445− null *> null *> null *> null *> null *> pure Nothing)
446− <|> (Just <$> fromRow)
447−
448−instance FromField a => FromRow [a] where
449− fromRow = do
450− n <- numFieldsRemaining
451− replicateM n field
452−
453−instance FromField a => FromRow (Maybe [a]) where
454− fromRow = do
455− n <- numFieldsRemaining
456− (replicateM_ n null *> pure Nothing) <|> (Just <$> replicateM n field)
457−
458−instance FromField a => FromRow (Vector a) where
459− fromRow = do
460− n <- numFieldsRemaining
461− V.replicateM n field
462−
463−instance FromField a => FromRow (Maybe (Vector a)) where
464− fromRow = do
465− n <- numFieldsRemaining
466− (replicateM_ n null *> pure Nothing) <|> (Just <$> V.replicateM n field)
467−
468−instance (FromRow a, FromRow b) => FromRow (a :. b) where
469− fromRow = (:.) <$> fromRow <*> fromRow
470−
471−
472−
473−-- Type class for default implementation of FromRow using generics
474−class GFromRow f where
475− gfromRow :: RowParser (f p)
476−
477−instance GFromRow f => GFromRow (M1 c i f) where
478− gfromRow = M1 <$> gfromRow
479−
480−instance (GFromRow f, GFromRow g) => GFromRow (f :*: g) where
481− gfromRow = liftA2 (:*:) gfromRow gfromRow
482−
483−instance (FromField a) => GFromRow (K1 R a) where
484− gfromRow = K1 <$> field
485−
486−instance GFromRow U1 where
487− gfromRow = pure U1
src/Database/PostgreSQL/Simple/FromRow.hs-bootdeleted−17
@@ -1,17 +0,0 @@
1−module Database.PostgreSQL.Simple.FromRow where
2−
3−import {-# SOURCE #-} Database.PostgreSQL.Simple.FromField
4−import Database.PostgreSQL.Simple.Types
5−
6−class FromRow a
7−
8−instance (FromField a) => FromRow (Only a)
9−instance (FromField a, FromField b)
10− => FromRow (a,b)
11−instance (FromField a, FromField b, FromField c, FromField d)
12− => FromRow (a,b,c,d)
13−instance (FromField a, FromField b, FromField c, FromField d, FromField e)
14− => FromRow (a,b,c,d,e)
15−instance (FromField a, FromField b, FromField c, FromField d, FromField e
16− ,FromField f)
17− => FromRow (a,b,c,d,e,f)
src/Database/PostgreSQL/Simple/HStore.hsdeleted−36
@@ -1,36 +0,0 @@
1−------------------------------------------------------------------------------
2−-- |
3−-- Module: Database.PostgreSQL.Simple.HStore
4−-- Copyright: (c) 2013 Leon P Smith
5−-- License: BSD3
6−-- Maintainer: Leon P Smith <leon@melding-monads.com>
7−-- Stability: experimental
8−--
9−-- Parsers and printers for hstore, a extended type bundled with
10−-- PostgreSQL providing finite maps from text strings to text strings.
11−-- See <https://www.postgresql.org/docs/9.5/static/hstore.html> for more
12−-- information.
13−--
14−-- Note that in order to use this type, a database superuser must
15−-- install it by running a sql script in the share directory. This
16−-- can be done on PostgreSQL 9.1 and later with the command
17−-- @CREATE EXTENSION hstore@. See
18−-- <https://www.postgresql.org/docs/9.5/static/contrib.html> for more
19−-- information.
20−--
21−------------------------------------------------------------------------------
22−
23−module Database.PostgreSQL.Simple.HStore
24− ( HStoreList(..)
25− , HStoreMap(..)
26− , ToHStore(..)
27− , HStoreBuilder
28− , toBuilder
29− , toLazyByteString
30− , hstore
31− , parseHStoreList
32− , ToHStoreText(..)
33− , HStoreText
34− ) where
35−
36−import Database.PostgreSQL.Simple.HStore.Implementation
src/Database/PostgreSQL/Simple/HStore/Implementation.hsdeleted−210
@@ -1,210 +0,0 @@
1−{-# LANGUAGE CPP, ViewPatterns, DeriveDataTypeable, GeneralizedNewtypeDeriving #-}
2−
3−------------------------------------------------------------------------------
4−-- |
5−-- Module: Database.PostgreSQL.Simple.HStore.Implementation
6−-- Copyright: (c) 2013 Leon P Smith
7−-- License: BSD3
8−-- Maintainer: Leon P Smith <leon@melding-monads.com>
9−-- Stability: experimental
10−--
11−-- This code has yet to be profiled and optimized.
12−--
13−------------------------------------------------------------------------------
14−
15−module Database.PostgreSQL.Simple.HStore.Implementation where
16−
17−import Control.Applicative
18−import qualified Data.Attoparsec.ByteString as P
19−import qualified Data.Attoparsec.ByteString.Char8 as P (isSpace_w8)
20−import qualified Data.ByteString as BS
21−import Data.ByteString.Builder (Builder, byteString, char8)
22−import qualified Data.ByteString.Builder as BU
23−import Data.ByteString.Internal (c2w, w2c)
24−import qualified Data.ByteString.Lazy as BL
25−import Data.Map(Map)
26−import qualified Data.Map as Map
27−import Data.Text(Text)
28−import qualified Data.Text as TS
29−import qualified Data.Text.Encoding as TS
30−import Data.Text.Encoding.Error(UnicodeException)
31−import qualified Data.Text.Lazy as TL
32−import Data.Typeable
33−import Data.Monoid(Monoid(..))
34−import Data.Semigroup
35−import Database.PostgreSQL.Simple.FromField
36−import Database.PostgreSQL.Simple.ToField
37−
38−class ToHStore a where
39− toHStore :: a -> HStoreBuilder
40−
41−-- | Represents valid hstore syntax.
42−data HStoreBuilder
43− = Empty
44− | Comma !Builder
45− deriving (Typeable)
46−
47−instance ToHStore HStoreBuilder where
48− toHStore = id
49−
50−toBuilder :: HStoreBuilder -> Builder
51−toBuilder x = case x of
52− Empty -> mempty
53− Comma c -> c
54−
55−toLazyByteString :: HStoreBuilder -> BL.ByteString
56−toLazyByteString x = case x of
57− Empty -> BL.empty
58− Comma c -> BU.toLazyByteString c
59−
60−instance Semigroup HStoreBuilder where
61− Empty <> x = x
62− Comma a <> x
63− = Comma (a `mappend` case x of
64− Empty -> mempty
65− Comma b -> char8 ',' `mappend` b)
66−
67−instance Monoid HStoreBuilder where
68− mempty = Empty
69−
70−class ToHStoreText a where
71− toHStoreText :: a -> HStoreText
72−
73−-- | Represents escape text, ready to be the key or value to a hstore value
74−newtype HStoreText = HStoreText Builder deriving (Typeable, Semigroup, Monoid)
75−
76−instance ToHStoreText HStoreText where
77− toHStoreText = id
78−
79−-- | Assumed to be UTF-8 encoded
80−instance ToHStoreText BS.ByteString where
81− toHStoreText str = HStoreText (escapeAppend str mempty)
82−
83−-- | Assumed to be UTF-8 encoded
84−instance ToHStoreText BL.ByteString where
85− toHStoreText = HStoreText . BL.foldrChunks escapeAppend mempty
86−
87−instance ToHStoreText TS.Text where
88− toHStoreText str = HStoreText (escapeAppend (TS.encodeUtf8 str) mempty)
89−
90−instance ToHStoreText TL.Text where
91− toHStoreText = HStoreText . TL.foldrChunks (escapeAppend . TS.encodeUtf8) mempty
92−
93−escapeAppend :: BS.ByteString -> Builder -> Builder
94−escapeAppend = loop
95− where
96− loop (BS.break quoteNeeded -> (a,b)) rest
97− = byteString a `mappend`
98− case BS.uncons b of
99− Nothing -> rest
100− Just (c,d) -> quoteChar c `mappend` loop d rest
101−
102− quoteNeeded c = c == c2w '\"' || c == c2w '\\'
103− quoteChar c
104− | c == c2w '\"' = byteString "\\\""
105− | otherwise = byteString "\\\\"
106−
107−hstore :: (ToHStoreText a, ToHStoreText b) => a -> b -> HStoreBuilder
108−hstore (toHStoreText -> (HStoreText key)) (toHStoreText -> (HStoreText val)) =
109− Comma (char8 '"' `mappend` key `mappend` byteString "\"=>\""
110− `mappend` val `mappend` char8 '"')
111−
112−instance ToField HStoreBuilder where
113− toField Empty = toField (BS.empty)
114− toField (Comma x) = toField (BU.toLazyByteString x)
115−
116−newtype HStoreList = HStoreList {fromHStoreList :: [(Text,Text)]} deriving (Typeable, Show)
117−
118−-- | hstore
119−instance ToHStore HStoreList where
120− toHStore (HStoreList xs) = mconcat (map (uncurry hstore) xs)
121−
122−instance ToField HStoreList where
123− toField xs = toField (toHStore xs)
124−
125−-- | hstore
126−instance FromField HStoreList where
127− fromField f mdat = do
128− typ <- typename f
129− if typ /= "hstore"
130− then returnError Incompatible f ""
131− else case mdat of
132− Nothing -> returnError UnexpectedNull f ""
133− Just dat ->
134− case P.parseOnly (parseHStore <* P.endOfInput) dat of
135− Left err ->
136− returnError ConversionFailed f err
137− Right (Left err) ->
138− returnError ConversionFailed f "unicode exception" <|>
139− conversionError err
140− Right (Right val) ->
141− return val
142−
143−newtype HStoreMap = HStoreMap {fromHStoreMap :: Map Text Text} deriving (Eq, Ord, Typeable, Show)
144−
145−instance ToHStore HStoreMap where
146− toHStore (HStoreMap xs) = Map.foldrWithKey f mempty xs
147− where f k v xs' = hstore k v `mappend` xs'
148−
149−instance ToField HStoreMap where
150− toField xs = toField (toHStore xs)
151−
152−instance FromField HStoreMap where
153− fromField f mdat = convert <$> fromField f mdat
154− where convert (HStoreList xs) = HStoreMap (Map.fromList xs)
155−
156−parseHStoreList :: BS.ByteString -> Either String HStoreList
157−parseHStoreList dat =
158− case P.parseOnly (parseHStore <* P.endOfInput) dat of
159− Left err -> Left (show err)
160− Right (Left err) -> Left (show err)
161− Right (Right val) -> Right val
162−
163−parseHStore :: P.Parser (Either UnicodeException HStoreList)
164−parseHStore = do
165− kvs <- P.sepBy' (skipWhiteSpace *> parseHStoreKeyVal)
166− (skipWhiteSpace *> P.word8 (c2w ','))
167− return $ HStoreList <$> sequence kvs
168−
169−parseHStoreKeyVal :: P.Parser (Either UnicodeException (Text,Text))
170−parseHStoreKeyVal = do
171− mkey <- parseHStoreText
172− case mkey of
173− Left err -> return (Left err)
174− Right key -> do
175− skipWhiteSpace
176− _ <- P.string "=>"
177− skipWhiteSpace
178− mval <- parseHStoreText
179− case mval of
180− Left err -> return (Left err)
181− Right val -> return (Right (key,val))
182−
183−
184−skipWhiteSpace :: P.Parser ()
185−skipWhiteSpace = P.skipWhile P.isSpace_w8
186−
187−parseHStoreText :: P.Parser (Either UnicodeException Text)
188−parseHStoreText = do
189− _ <- P.word8 (c2w '"')
190− mtexts <- parseHStoreTexts id
191− case mtexts of
192− Left err -> return (Left err)
193− Right texts -> do
194− _ <- P.word8 (c2w '"')
195− return (Right (TS.concat texts))
196−
197−parseHStoreTexts :: ([Text] -> [Text])
198− -> P.Parser (Either UnicodeException [Text])
199−parseHStoreTexts acc = do
200− mchunk <- TS.decodeUtf8' <$> P.takeWhile (not . isSpecialChar)
201− case mchunk of
202− Left err -> return (Left err)
203− Right chunk ->
204− (do
205− _ <- P.word8 (c2w '\\')
206− c <- TS.singleton . w2c <$> P.satisfy isSpecialChar
207− parseHStoreTexts (acc . (chunk:) . (c:))
208− ) <|> return (Right (acc [chunk]))
209− where
210− isSpecialChar c = c == c2w '\\' || c == c2w '"'
src/Database/PostgreSQL/Simple/HStore/Internal.hsdeleted−19
@@ -1,19 +0,0 @@
1−------------------------------------------------------------------------------
2−-- |
3−-- Module: Database.PostgreSQL.Simple.HStore.Internal
4−-- Copyright: (c) 2013 Leon P Smith
5−-- License: BSD3
6−-- Maintainer: Leon P Smith <leon@melding-monads.com>
7−-- Stability: experimental
8−--
9−------------------------------------------------------------------------------
10−
11−module Database.PostgreSQL.Simple.HStore.Internal
12− ( HStoreBuilder(..)
13− , HStoreText(..)
14− , parseHStore
15− , parseHStoreKeyVal
16− , parseHStoreText
17− ) where
18−
19−import Database.PostgreSQL.Simple.HStore.Implementation
src/Database/PostgreSQL/Simple/Implementation.hsnew file+187
@@ -0,0 +1,187 @@
1+{-# LANGUAGE OverloadedStrings, NamedFieldPuns, RecordWildCards #-}
2+{-# LANGUAGE DeriveDataTypeable, DeriveFunctor #-}
3+module Database.PostgreSQL.Simple.Implementation where
4+
5+import Prelude hiding (catch)
6+
7+import Control.Applicative
8+import Control.Exception
9+import Control.Monad.IO.Class (MonadIO(liftIO))
10+import Control.Concurrent.MVar
11+import Data.ByteString(ByteString)
12+import qualified Data.IntMap as IntMap
13+import Data.String
14+import Data.Typeable
15+import Data.Word
16+import Database.PostgreSQL.LibPQ(Oid(..))
17+import qualified Database.PostgreSQL.LibPQ as PQ
18+import Database.PostgreSQL.Simple.BuiltinTypes (BuiltinType)
19+import System.IO.Unsafe (unsafePerformIO)
20+
21+
22+data Status err a = Fail err | Success a deriving(Eq, Ord, Show, Functor)
23+
24+instance Applicative (Status err) where
25+ pure = Success
26+ (Success f) <*> (Success a) = Success (f a)
27+ (Success _) <*> (Fail err) = Fail err
28+ (Fail err) <*> _ = Fail err
29+
30+instance Monad (Status err) where
31+ return = pure
32+ (Success a) >>= f = f a
33+ (Fail err) >>= _ = Fail err
34+
35+
36+-- | A Field represents metadata about a particular field
37+--
38+-- You don't particularly want to retain these structures for a long
39+-- period of time, as they will retain the entire query result, not
40+-- just the field metadata
41+
42+data Field = Field {
43+ result :: PQ.Result
44+ , column :: PQ.Column
45+ , typename :: ByteString
46+ }
47+
48+name :: Field -> Maybe ByteString
49+name Field{..} = unsafePerformIO (PQ.fname result column)
50+
51+tableOid :: Field -> PQ.Oid
52+tableOid Field{..} = unsafePerformIO (PQ.ftable result column)
53+
54+tableColumn :: Field -> Int
55+tableColumn Field{..} = fromCol (unsafePerformIO (PQ.ftablecol result column))
56+ where
57+ fromCol (PQ.Col x) = fromIntegral x
58+
59+
60+format :: Field -> PQ.Format
61+format Field{..} = unsafePerformIO (PQ.fformat result column)
62+
63+typeOid :: Field -> PQ.Oid
64+typeOid Field{..} = unsafePerformIO (PQ.ftype result column)
65+
66+
67+data Connection = Connection {
68+ connectionHandle :: MVar (Maybe PQ.Connection)
69+ , connectionObjects :: MVar (IntMap.IntMap ByteString)
70+ }
71+
72+data SqlType
73+ = Builtin BuiltinType
74+ | Other Oid
75+
76+data SqlError = SqlError {
77+ sqlState :: ByteString
78+ , sqlNativeError :: Int
79+ , sqlErrorMsg :: ByteString
80+ } deriving (Show, Typeable)
81+
82+instance Exception SqlError
83+
84+data ConnectInfo = ConnectInfo {
85+ connectHost :: String
86+ , connectPort :: Word16
87+ , connectUser :: String
88+ , connectPassword :: String
89+ , connectDatabase :: String
90+ } deriving (Eq,Read,Show,Typeable)
91+
92+defaultConnectInfo :: ConnectInfo
93+defaultConnectInfo = ConnectInfo {
94+ connectHost = "127.0.0.1"
95+ , connectPort = 5432
96+ , connectUser = "postgres"
97+ , connectPassword = ""
98+ , connectDatabase = ""
99+ }
100+
101+-- | Connect with the given username to the given database. Will throw
102+-- an exception if it cannot connect.
103+connect :: MonadIO m => ConnectInfo -> m Connection
104+connect = connectPostgreSQL . postgreSQLConnectionString
105+
106+connectPostgreSQL :: MonadIO m => ByteString -> m Connection
107+connectPostgreSQL connstr = liftIO $ do
108+ conn <- PQ.connectdb connstr
109+ connectionHandle <- newMVar (Just conn)
110+ connectionObjects <- newMVar (IntMap.empty)
111+ return Connection{..}
112+
113+postgreSQLConnectionString :: ConnectInfo -> ByteString
114+postgreSQLConnectionString connectInfo = fromString connstr
115+ where
116+ connstr = str "host=" connectHost
117+ $ num "port=" connectPort
118+ $ str "user=" connectUser
119+ $ str "password=" connectPassword
120+ $ str "dbname=" connectDatabase
121+ $ []
122+
123+ str name field
124+ | null value = id
125+ | otherwise = (name ++) . quote value . space
126+ where value = field connectInfo
127+
128+ num name field
129+ | value <= 0 = id
130+ | otherwise = (name ++) . (show value ++) . space
131+ where value = field connectInfo
132+
133+ quote str rest = '\'' : foldr delta ('\'' : rest) str
134+ where
135+ delta c cs = case c of
136+ '\\' -> '\\' : '\\' : cs
137+ '\'' -> '\\' : '\'' : cs
138+ _ -> c : cs
139+
140+ space [] = []
141+ space xs = ' ':xs
142+
143+
144+
145+oid2int :: Oid -> Int
146+oid2int (Oid x) = fromIntegral x
147+{-# INLINE oid2int #-}
148+
149+exec :: Connection
150+ -> ByteString
151+ -> IO PQ.Result
152+exec conn sql =
153+ withConnection conn $ \h -> do
154+ mres <- PQ.exec h sql
155+ case mres of
156+ Nothing -> do
157+ msg <- PQ.errorMessage h
158+ -- FIXME better error message
159+ fail ("Postgres Error: " ++ show msg)
160+ Just res -> do
161+ return res
162+
163+disconnectedError = SqlError {
164+ sqlNativeError = -1,
165+ sqlErrorMsg = "connection disconnected",
166+ sqlState = ""
167+ }
168+
169+
170+-- | Atomically perform an action with the database handle, if there is one.
171+withConnection :: Connection -> (PQ.Connection -> IO a) -> IO a
172+withConnection Connection{..} m = do
173+ withMVar connectionHandle $ \h -> do
174+ case h of
175+ Just h -> m h
176+ -- TODO: Use extensible exceptions.
177+ Nothing -> throwIO disconnectedError
178+
179+close :: Connection -> IO ()
180+close Connection{..} = do
181+ mconn <- takeMVar connectionHandle
182+ case mconn of
183+ Just conn -> PQ.finish conn
184+ Nothing -> return ()
185+ `finally` putMVar connectionHandle Nothing
186+
187+data RawResult = RawResult { rawField :: Field, rawData :: Maybe ByteString }
src/Database/PostgreSQL/Simple/Internal.hsdeleted−637
@@ -1,637 +0,0 @@
1−{-# LANGUAGE CPP, BangPatterns, DoAndIfThenElse, RecordWildCards #-}
2−{-# LANGUAGE DeriveDataTypeable, DeriveGeneric #-}
3−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4−{-# LANGUAGE ExistentialQuantification #-}
5−{-# LANGUAGE InstanceSigs #-}
6−
7−------------------------------------------------------------------------------
8−-- |
9−-- Module: Database.PostgreSQL.Simple.Internal
10−-- Copyright: (c) 2011-2015 Leon P Smith
11−-- License: BSD3
12−-- Maintainer: Leon P Smith <leon@melding-monads.com>
13−-- Stability: experimental
14−--
15−-- Internal bits. This interface is less stable and can change at any time.
16−-- In particular this means that while the rest of the postgresql-simple
17−-- package endeavors to follow the package versioning policy, this module
18−-- does not. Also, at the moment there are things in here that aren't
19−-- particularly internal and are exported elsewhere; these will eventually
20−-- disappear from this module.
21−--
22−------------------------------------------------------------------------------
23−
24−module Database.PostgreSQL.Simple.Internal where
25−
26−import Control.Applicative
27−import Control.Exception
28−import Control.Concurrent.MVar
29−import Control.Monad(MonadPlus(..))
30−import Data.ByteString(ByteString)
31−import qualified Data.ByteString as B
32−import qualified Data.ByteString.Char8 as B8
33−import Data.ByteString.Builder ( Builder, byteString )
34−import Data.Char (ord)
35−import Data.Int (Int64)
36−import qualified Data.IntMap as IntMap
37−import Data.IORef
38−import Data.Maybe(fromMaybe)
39−import Data.String
40−import qualified Data.Text as T
41−import qualified Data.Text.Encoding as TE
42−import Data.Typeable
43−import Data.Word
44−import Database.PostgreSQL.LibPQ(Oid(..))
45−import qualified Database.PostgreSQL.LibPQ as PQ
46−import Database.PostgreSQL.LibPQ(ExecStatus(..))
47−import Database.PostgreSQL.Simple.Compat ( toByteString )
48−import Database.PostgreSQL.Simple.Ok
49−import Database.PostgreSQL.Simple.ToField (Action(..), inQuotes)
50−import Database.PostgreSQL.Simple.Types (Query(..))
51−import Database.PostgreSQL.Simple.TypeInfo.Types(TypeInfo)
52−import Control.Monad.Trans.State.Strict
53−import Control.Monad.Trans.Reader
54−import Control.Monad.Trans.Class
55−import GHC.Generics
56−import GHC.IO.Exception
57−#if !defined(mingw32_HOST_OS)
58−import Control.Concurrent(threadWaitRead, threadWaitWrite)
59−#endif
60−
61−-- | A Field represents metadata about a particular field
62−--
63−-- You don't particularly want to retain these structures for a long
64−-- period of time, as they will retain the entire query result, not
65−-- just the field metadata
66−
67−data Field = Field {
68− result :: !PQ.Result
69− , column :: {-# UNPACK #-} !PQ.Column
70− , typeOid :: {-# UNPACK #-} !PQ.Oid
71− -- ^ This returns the type oid associated with the column. Analogous
72− -- to libpq's @PQftype@.
73− }
74−
75−type TypeInfoCache = IntMap.IntMap TypeInfo
76−
77−data Connection = Connection {
78− connectionHandle :: {-# UNPACK #-} !(MVar PQ.Connection)
79− , connectionObjects :: {-# UNPACK #-} !(MVar TypeInfoCache)
80− , connectionTempNameCounter :: {-# UNPACK #-} !(IORef Int64)
81− } deriving (Typeable)
82−
83−instance Eq Connection where
84− x == y = connectionHandle x == connectionHandle y
85−
86−-- | Superclass for postgresql exceptions
87−data SomePostgreSqlException = forall e. Exception e => SomePostgreSqlException e
88− deriving Typeable
89−
90−postgresqlExceptionToException :: Exception e => e -> SomeException
91−postgresqlExceptionToException = toException . SomePostgreSqlException
92−
93−postgresqlExceptionFromException :: Exception e => SomeException -> Maybe e
94−postgresqlExceptionFromException x = do
95− SomePostgreSqlException a <- fromException x
96− cast a
97−
98−instance Show SomePostgreSqlException where
99− showsPrec :: Int -> SomePostgreSqlException -> ShowS
100− showsPrec p (SomePostgreSqlException e) = showsPrec p e
101−
102−instance Exception SomePostgreSqlException where
103− displayException (SomePostgreSqlException e) = displayException e
104−
105−data SqlError = SqlError {
106− sqlState :: ByteString
107− , sqlExecStatus :: ExecStatus
108− , sqlErrorMsg :: ByteString
109− , sqlErrorDetail :: ByteString
110− , sqlErrorHint :: ByteString
111− } deriving (Eq, Show, Typeable)
112−
113−fatalError :: ByteString -> SqlError
114−fatalError msg = SqlError "" FatalError msg "" ""
115−
116−instance Exception SqlError where
117− toException = postgresqlExceptionToException
118− fromException = postgresqlExceptionFromException
119−
120−
121−-- | Exception thrown if 'query' is used to perform an @INSERT@-like
122−-- operation, or 'execute' is used to perform a @SELECT@-like operation.
123−data QueryError = QueryError {
124− qeMessage :: String
125− , qeQuery :: Query
126− } deriving (Eq, Show, Typeable)
127−
128−instance Exception QueryError where
129− toException = postgresqlExceptionToException
130− fromException = postgresqlExceptionFromException
131−
132−-- | Exception thrown if a 'Query' could not be formatted correctly.
133−-- This may occur if the number of \'@?@\' characters in the query
134−-- string does not match the number of parameters provided.
135−data FormatError = FormatError {
136− fmtMessage :: String
137− , fmtQuery :: Query
138− , fmtParams :: [ByteString]
139− } deriving (Eq, Show, Typeable)
140−
141−instance Exception FormatError where
142− toException = postgresqlExceptionToException
143− fromException = postgresqlExceptionFromException
144−
145−data ConnectInfo = ConnectInfo {
146− connectHost :: String
147− , connectPort :: Word16
148− , connectUser :: String
149− , connectPassword :: String
150− , connectDatabase :: String
151− } deriving (Generic,Eq,Read,Show,Typeable)
152−
153−-- | Default information for setting up a connection.
154−--
155−-- Defaults are as follows:
156−--
157−-- * Server on @localhost@
158−--
159−-- * Port on @5432@
160−--
161−-- * User @postgres@
162−--
163−-- * No password
164−--
165−-- * Database @postgres@
166−--
167−-- Use as in the following example:
168−--
169−-- > connect defaultConnectInfo { connectHost = "db.example.com" }
170−
171−defaultConnectInfo :: ConnectInfo
172−defaultConnectInfo = ConnectInfo {
173− connectHost = "127.0.0.1"
174− , connectPort = 5432
175− , connectUser = "postgres"
176− , connectPassword = ""
177− , connectDatabase = ""
178− }
179−
180−-- | Connect with the given username to the given database. Will throw
181−-- an exception if it cannot connect.
182−connect :: ConnectInfo -> IO Connection
183−connect = connectPostgreSQL . postgreSQLConnectionString
184−
185−-- | Memory bracket around 'connect' and 'close'.
186−--
187−-- @since 0.6.5
188−withConnect :: ConnectInfo -> (Connection -> IO c) -> IO c
189−withConnect connInfo = bracket (connect connInfo) close
190−
191−-- | Attempt to make a connection based on a libpq connection string.
192−-- See <https://www.postgresql.org/docs/9.5/static/libpq-connect.html#LIBPQ-CONNSTRING>
193−-- for more information. Also note that environment variables also affect
194−-- parameters not provided, parameters provided as the empty string, and a
195−-- few other things; see
196−-- <https://www.postgresql.org/docs/9.5/static/libpq-envars.html>
197−-- for details. Here is an example with some of the most commonly used
198−-- parameters:
199−--
200−-- > host='db.somedomain.com' port=5432 ...
201−--
202−-- This attempts to connect to @db.somedomain.com:5432@. Omitting the port
203−-- will normally default to 5432.
204−--
205−-- On systems that provide unix domain sockets, omitting the host parameter
206−-- will cause libpq to attempt to connect via unix domain sockets.
207−-- The default filesystem path to the socket is constructed from the
208−-- port number and the @DEFAULT_PGSOCKET_DIR@ constant defined in the
209−-- @pg_config_manual.h@ header file. Connecting via unix sockets tends
210−-- to use the @peer@ authentication method, which is very secure and
211−-- does not require a password.
212−--
213−-- On Windows and other systems without unix domain sockets, omitting
214−-- the host will default to @localhost@.
215−--
216−-- > ... dbname='postgres' user='postgres' password='secret \' \\ pw'
217−--
218−-- This attempts to connect to a database named @postgres@ with
219−-- user @postgres@ and password @secret \' \\ pw@. Backslash
220−-- characters will have to be double-quoted in literal Haskell strings,
221−-- of course. Omitting @dbname@ and @user@ will both default to the
222−-- system username that the client process is running as.
223−--
224−-- Omitting @password@ will default to an appropriate password found
225−-- in the @pgpass@ file, or no password at all if a matching line is
226−-- not found. The path of the @pgpass@ file may be specified by setting
227−-- the @PGPASSFILE@ environment variable. See
228−-- <https://www.postgresql.org/docs/9.5/static/libpq-pgpass.html> for
229−-- more information regarding this file.
230−--
231−-- As all parameters are optional and the defaults are sensible, the
232−-- empty connection string can be useful for development and
233−-- exploratory use, assuming your system is set up appropriately.
234−--
235−-- On Unix, such a setup would typically consist of a local
236−-- postgresql server listening on port 5432, as well as a system user,
237−-- database user, and database sharing a common name, with permissions
238−-- granted to the user on the database.
239−--
240−-- On Windows, in addition you will either need @pg_hba.conf@
241−-- to specify the use of the @trust@ authentication method for
242−-- the connection, which may not be appropriate for multiuser
243−-- or production machines, or you will need to use a @pgpass@ file
244−-- with the @password@ or @md5@ authentication methods.
245−--
246−-- See <https://www.postgresql.org/docs/9.5/static/client-authentication.html>
247−-- for more information regarding the authentication process.
248−--
249−-- SSL/TLS will typically "just work" if your postgresql server supports or
250−-- requires it. However, note that libpq is trivially vulnerable to a MITM
251−-- attack without setting additional SSL connection parameters. In
252−-- particular, @sslmode@ needs to be set to @require@, @verify-ca@, or
253−-- @verify-full@ in order to perform certificate validation. When @sslmode@
254−-- is @require@, then you will also need to specify a @sslrootcert@ file,
255−-- otherwise no validation of the server's identity will be performed.
256−-- Client authentication via certificates is also possible via the
257−-- @sslcert@ and @sslkey@ parameters. See
258−-- <https://www.postgresql.org/docs/9.5/static/libpq-ssl.html>
259−-- for detailed information regarding libpq and SSL.
260−
261−connectPostgreSQL :: ByteString -> IO Connection
262−connectPostgreSQL connstr = do
263− conn <- connectdb connstr
264− stat <- PQ.status conn
265− case stat of
266− PQ.ConnectionOk -> do
267− connectionHandle <- newMVar conn
268− connectionObjects <- newMVar (IntMap.empty)
269− connectionTempNameCounter <- newIORef 0
270− let wconn = Connection{..}
271− version <- PQ.serverVersion conn
272− let settings
273− | version < 80200 = "SET datestyle TO ISO;SET client_encoding TO UTF8"
274− | otherwise = "SET datestyle TO ISO;SET client_encoding TO UTF8;SET standard_conforming_strings TO on"
275− _ <- execute_ wconn settings
276− return wconn
277− _ -> do
278− msg <- maybe "connectPostgreSQL error" id <$> PQ.errorMessage conn
279− throwIO $ fatalError msg
280−
281−connectdb :: ByteString -> IO PQ.Connection
282−#if defined(mingw32_HOST_OS)
283−connectdb = PQ.connectdb
284−#else
285−connectdb conninfo = do
286− conn <- PQ.connectStart conninfo
287− loop conn
288− where
289− funcName = "Database.PostgreSQL.Simple.connectPostgreSQL"
290− loop conn = do
291− status <- PQ.connectPoll conn
292− case status of
293− PQ.PollingFailed -> throwLibPQError conn "connection failed"
294− PQ.PollingReading -> do
295− mfd <- PQ.socket conn
296− case mfd of
297− Nothing -> throwIO $! fdError funcName
298− Just fd -> do
299− threadWaitRead fd
300− loop conn
301− PQ.PollingWriting -> do
302− mfd <- PQ.socket conn
303− case mfd of
304− Nothing -> throwIO $! fdError funcName
305− Just fd -> do
306− threadWaitWrite fd
307− loop conn
308− PQ.PollingOk -> return conn
309−
310−#endif
311−
312−-- | Turns a 'ConnectInfo' data structure into a libpq connection string.
313−
314−postgreSQLConnectionString :: ConnectInfo -> ByteString
315−postgreSQLConnectionString connectInfo = fromString connstr
316− where
317− connstr = str "host=" connectHost
318− $ num "port=" connectPort
319− $ str "user=" connectUser
320− $ str "password=" connectPassword
321− $ str "dbname=" connectDatabase
322− $ []
323−
324− str name field
325− | null value = id
326− | otherwise = showString name . addQuotes value . space
327− where value = field connectInfo
328−
329− num name field
330− | value <= 0 = id
331− | otherwise = showString name . shows value . space
332− where value = field connectInfo
333−
334− addQuotes s rest = '\'' : foldr delta ('\'' : rest) s
335− where
336− delta c cs = case c of
337− '\\' -> '\\' : '\\' : cs
338− '\'' -> '\\' : '\'' : cs
339− _ -> c : cs
340−
341− space [] = []
342− space xs = ' ':xs
343−
344−
345−
346−oid2int :: Oid -> Int
347−oid2int (Oid x) = fromIntegral x
348−{-# INLINE oid2int #-}
349−
350−exec :: Connection
351− -> ByteString
352− -> IO PQ.Result
353−#if defined(mingw32_HOST_OS)
354−exec conn sql =
355− withConnection conn $ \h -> do
356− mres <- PQ.exec h sql
357− case mres of
358− Nothing -> throwLibPQError h "PQexec returned no results"
359− Just res -> return res
360−#else
361−exec conn sql =
362− withConnection conn $ \h -> do
363− success <- PQ.sendQuery h sql
364− if success
365− then awaitResult h Nothing
366− else throwLibPQError h "PQsendQuery failed"
367− where
368− awaitResult h mres = do
369− mfd <- PQ.socket h
370− case mfd of
371− Nothing -> throwIO $! fdError "Database.PostgreSQL.Simple.Internal.exec"
372− Just fd -> do
373− threadWaitRead fd
374− _ <- PQ.consumeInput h -- FIXME?
375− getResult h mres
376−
377− getResult h mres = do
378− isBusy <- PQ.isBusy h
379− if isBusy
380− then awaitResult h mres
381− else do
382− mres' <- PQ.getResult h
383− case mres' of
384− Nothing -> case mres of
385− Nothing -> throwLibPQError h "PQgetResult returned no results"
386− Just res -> return res
387− Just res -> do
388− status <- PQ.resultStatus res
389− case status of
390− -- FIXME: handle PQ.CopyBoth and PQ.SingleTuple
391− PQ.EmptyQuery -> getResult h mres'
392− PQ.CommandOk -> getResult h mres'
393− PQ.TuplesOk -> getResult h mres'
394− PQ.CopyOut -> return res
395− PQ.CopyIn -> return res
396− PQ.BadResponse -> getResult h mres'
397− PQ.NonfatalError -> getResult h mres'
398− PQ.FatalError -> getResult h mres'
399−#endif
400−
401−-- | A version of 'execute' that does not perform query substitution.
402−execute_ :: Connection -> Query -> IO Int64
403−execute_ conn q@(Query stmt) = do
404− result <- exec conn stmt
405− finishExecute conn q result
406−
407−finishExecute :: Connection -> Query -> PQ.Result -> IO Int64
408−finishExecute _conn q result = do
409− status <- PQ.resultStatus result
410− case status of
411− -- FIXME: handle PQ.CopyBoth and PQ.SingleTuple
412− PQ.EmptyQuery -> throwIO $ QueryError "execute: Empty query" q
413− PQ.CommandOk -> do
414− ncols <- PQ.nfields result
415− if ncols /= 0
416− then throwIO $ QueryError ("execute resulted in " ++ show ncols ++
417− "-column result") q
418− else do
419− nstr <- PQ.cmdTuples result
420− return $ case nstr of
421− Nothing -> 0 -- is this appropriate?
422− Just str -> mkInteger str
423− PQ.TuplesOk -> do
424− ncols <- PQ.nfields result
425− throwIO $ QueryError ("execute resulted in " ++ show ncols ++
426− "-column result") q
427− PQ.CopyOut ->
428− throwIO $ QueryError "execute: COPY TO is not supported" q
429− PQ.CopyIn ->
430− throwIO $ QueryError "execute: COPY FROM is not supported" q
431− PQ.BadResponse -> throwResultError "execute" result status
432− PQ.NonfatalError -> throwResultError "execute" result status
433− PQ.FatalError -> throwResultError "execute" result status
434− where
435− mkInteger str = B8.foldl' delta 0 str
436− where
437− delta acc c =
438− if '0' <= c && c <= '9'
439− then 10 * acc + fromIntegral (ord c - ord '0')
440− else error ("finishExecute: not an int: " ++ B8.unpack str)
441−
442−throwResultError :: ByteString -> PQ.Result -> PQ.ExecStatus -> IO a
443−throwResultError _ result status = do
444− errormsg <- fromMaybe "" <$>
445− PQ.resultErrorField result PQ.DiagMessagePrimary
446− detail <- fromMaybe "" <$>
447− PQ.resultErrorField result PQ.DiagMessageDetail
448− hint <- fromMaybe "" <$>
449− PQ.resultErrorField result PQ.DiagMessageHint
450− state' <- maybe "" id <$> PQ.resultErrorField result PQ.DiagSqlstate
451− throwIO $ SqlError { sqlState = state'
452− , sqlExecStatus = status
453− , sqlErrorMsg = errormsg
454− , sqlErrorDetail = detail
455− , sqlErrorHint = hint }
456−
457−disconnectedError :: SqlError
458−disconnectedError = fatalError "connection disconnected"
459−
460−-- | Atomically perform an action with the database handle, if there is one.
461−withConnection :: Connection -> (PQ.Connection -> IO a) -> IO a
462−withConnection Connection{..} m = do
463− withMVar connectionHandle $ \conn -> do
464− if PQ.isNullConnection conn
465− then throwIO disconnectedError
466− else m conn
467−
468−close :: Connection -> IO ()
469−close Connection{..} =
470− mask $ \restore -> (do
471− conn <- takeMVar connectionHandle
472− restore (PQ.finish conn)
473− `finally` do
474− putMVar connectionHandle =<< PQ.newNullConnection
475− )
476−
477−newNullConnection :: IO Connection
478−newNullConnection = do
479− connectionHandle <- newMVar =<< PQ.newNullConnection
480− connectionObjects <- newMVar IntMap.empty
481− connectionTempNameCounter <- newIORef 0
482− return Connection{..}
483−
484−data Row = Row {
485− row :: {-# UNPACK #-} !PQ.Row
486− , rowresult :: !PQ.Result
487− }
488−
489−newtype RowParser a = RP { unRP :: ReaderT Row (StateT PQ.Column Conversion) a }
490− deriving ( Functor, Applicative, Alternative, Monad )
491−
492−liftRowParser :: IO a -> RowParser a
493−liftRowParser = RP . lift . lift . liftConversion
494−
495−newtype Conversion a = Conversion { runConversion :: Connection -> IO (Ok a) }
496−
497−liftConversion :: IO a -> Conversion a
498−liftConversion m = Conversion (\_ -> Ok <$> m)
499−
500−instance Functor Conversion where
501− fmap f m = Conversion $ \conn -> (fmap . fmap) f (runConversion m conn)
502−
503−instance Applicative Conversion where
504− pure a = Conversion $ \_conn -> pure (pure a)
505− mf <*> ma = Conversion $ \conn -> do
506− okf <- runConversion mf conn
507− case okf of
508− Ok f -> (fmap . fmap) f (runConversion ma conn)
509− Errors errs -> return (Errors errs)
510−
511−instance Alternative Conversion where
512− empty = Conversion $ \_conn -> pure empty
513− ma <|> mb = Conversion $ \conn -> do
514− oka <- runConversion ma conn
515− case oka of
516− Ok _ -> return oka
517− Errors _ -> (oka <|>) <$> runConversion mb conn
518−
519−instance Monad Conversion where
520− m >>= f = Conversion $ \conn -> do
521− oka <- runConversion m conn
522− case oka of
523− Ok a -> runConversion (f a) conn
524− Errors err -> return (Errors err)
525−
526−instance MonadPlus Conversion where
527− mzero = empty
528− mplus = (<|>)
529−
530−conversionMap :: (Ok a -> Ok b) -> Conversion a -> Conversion b
531−conversionMap f m = Conversion $ \conn -> f <$> runConversion m conn
532−
533−conversionError :: Exception err => err -> Conversion a
534−conversionError err = Conversion $ \_ -> return (Errors [toException err])
535−
536−newTempName :: Connection -> IO Query
537−newTempName Connection{..} = do
538− !n <- atomicModifyIORef connectionTempNameCounter
539− (\n -> let !n' = n+1 in (n', n'))
540− return $! Query $ B8.pack $ "temp" ++ show n
541−
542−-- FIXME? What error should getNotification and getCopyData throw?
543−fdError :: ByteString -> IOError
544−fdError funcName = IOError {
545− ioe_handle = Nothing,
546− ioe_type = ResourceVanished,
547− ioe_location = B8.unpack funcName,
548− ioe_description = "failed to fetch file descriptor",
549− ioe_errno = Nothing,
550− ioe_filename = Nothing
551− }
552−
553−
554−libPQError :: ByteString -> IOError
555−libPQError desc = IOError {
556− ioe_handle = Nothing,
557− ioe_type = OtherError,
558− ioe_location = "libpq",
559− ioe_description = B8.unpack desc,
560− ioe_errno = Nothing,
561− ioe_filename = Nothing
562− }
563−
564−throwLibPQError :: PQ.Connection -> ByteString -> IO a
565−throwLibPQError conn default_desc = do
566− msg <- maybe default_desc id <$> PQ.errorMessage conn
567− throwIO $! libPQError msg
568−
569−
570−fmtError :: String -> Query -> [Action] -> a
571−fmtError msg q xs = throw FormatError {
572− fmtMessage = msg
573− , fmtQuery = q
574− , fmtParams = map twiddle xs
575− }
576− where twiddle (Plain b) = toByteString b
577− twiddle (Escape s) = s
578− twiddle (EscapeByteA s) = s
579− twiddle (EscapeIdentifier s) = s
580− twiddle (Many ys) = B.concat (map twiddle ys)
581−
582−fmtErrorBs :: Query -> [Action] -> ByteString -> a
583−fmtErrorBs q xs msg = fmtError (T.unpack $ TE.decodeUtf8 msg) q xs
584−
585−-- | Quote bytestring or throw 'FormatError'
586−quote :: Query -> [Action] -> Either ByteString ByteString -> Builder
587−quote q xs = either (fmtErrorBs q xs) (inQuotes . byteString)
588−
589−buildAction :: Connection -- ^ Connection for string escaping
590− -> Query -- ^ Query for message error
591− -> [Action] -- ^ List of parameters for message error
592− -> Action -- ^ Action to build
593− -> IO Builder
594−buildAction _ _ _ (Plain b) = pure b
595−buildAction conn q xs (Escape s) = quote q xs <$> escapeStringConn conn s
596−buildAction conn q xs (EscapeByteA s) = quote q xs <$> escapeByteaConn conn s
597−buildAction conn q xs (EscapeIdentifier s) =
598− either (fmtErrorBs q xs) byteString <$> escapeIdentifier conn s
599−buildAction conn q xs (Many ys) =
600− mconcat <$> mapM (buildAction conn q xs) ys
601−
602−checkError :: PQ.Connection -> Maybe a -> IO (Either ByteString a)
603−checkError _ (Just x) = return $ Right x
604−checkError c Nothing = Left . maybe "" id <$> PQ.errorMessage c
605−
606−escapeWrap :: (PQ.Connection -> ByteString -> IO (Maybe ByteString))
607− -> Connection
608− -> ByteString
609− -> IO (Either ByteString ByteString)
610−escapeWrap f conn s =
611− withConnection conn $ \c ->
612− f c s >>= checkError c
613−
614−escapeStringConn :: Connection -> ByteString -> IO (Either ByteString ByteString)
615−escapeStringConn = escapeWrap PQ.escapeStringConn
616−
617−escapeIdentifier :: Connection -> ByteString -> IO (Either ByteString ByteString)
618−escapeIdentifier = escapeWrap PQ.escapeIdentifier
619−
620−escapeByteaConn :: Connection -> ByteString -> IO (Either ByteString ByteString)
621−escapeByteaConn = escapeWrap PQ.escapeByteaConn
622−
623−breakOnSingleQuestionMark :: ByteString -> (ByteString, ByteString)
624−breakOnSingleQuestionMark b = go (B8.empty, b)
625− where go (x,bs) = (x `B8.append` x',bs')
626− -- seperate from first QM
627− where tup@(noQ, restWithQ) = B8.break (=='?') bs
628− -- if end of query, just return
629− -- else check for second QM in 'go2'
630− (x', bs') = maybe tup go2 $
631− -- drop found QM and peek at next char
632− B8.uncons restWithQ >>= B8.uncons . snd
633− -- another QM after the first means:
634− -- take literal QM and keep going.
635− go2 ('?', t2) = go (noQ `B8.snoc` '?',t2)
636− -- Anything else means
637− go2 _ = tup
src/Database/PostgreSQL/Simple/Internal/PQResultUtils.hsdeleted−123
@@ -1,123 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−
3−------------------------------------------------------------------------------
4−-- |
5−-- Module: Database.PostgreSQL.Simple.Internal.PQResultUtils
6−-- Copyright: (c) 2011 MailRank, Inc.
7−-- (c) 2011-2012 Leon P Smith
8−-- License: BSD3
9−-- Maintainer: Leon P Smith <leon@melding-monads.com>
10−-- Stability: experimental
11−--
12−------------------------------------------------------------------------------
13−
14−
15−module Database.PostgreSQL.Simple.Internal.PQResultUtils
16− ( finishQueryWith
17− , finishQueryWithV
18− , finishQueryWithVU
19− , getRowWith
20− ) where
21−
22−import Control.Exception as E
23−import Data.ByteString (ByteString)
24−import Data.Foldable (for_)
25−import Database.PostgreSQL.Simple.FromField (ResultError(..))
26−import Database.PostgreSQL.Simple.Ok
27−import Database.PostgreSQL.Simple.Types (Query(..))
28−import Database.PostgreSQL.Simple.Internal as Base hiding (result, row)
29−import Database.PostgreSQL.Simple.TypeInfo
30−import qualified Database.PostgreSQL.LibPQ as PQ
31−import qualified Data.ByteString.Char8 as B
32−import qualified Data.Vector as V
33−import qualified Data.Vector.Mutable as MV
34−import qualified Data.Vector.Unboxed as VU
35−import qualified Data.Vector.Unboxed.Mutable as MVU
36−import Control.Monad.Trans.Reader
37−import Control.Monad.Trans.State.Strict
38−
39−finishQueryWith :: RowParser r -> Connection -> Query -> PQ.Result -> IO [r]
40−finishQueryWith parser conn q result = finishQueryWith' q result $ do
41− nrows <- PQ.ntuples result
42− ncols <- PQ.nfields result
43− forM' 0 (nrows-1) $ \row ->
44− getRowWith parser row ncols conn result
45−
46−finishQueryWithV :: RowParser r -> Connection -> Query -> PQ.Result -> IO (V.Vector r)
47−finishQueryWithV parser conn q result = finishQueryWith' q result $ do
48− nrows <- PQ.ntuples result
49− let PQ.Row nrows' = nrows
50− ncols <- PQ.nfields result
51− mv <- MV.unsafeNew (fromIntegral nrows')
52− for_ [ 0 .. nrows-1 ] $ \row -> do
53− let PQ.Row row' = row
54− value <- getRowWith parser row ncols conn result
55− MV.unsafeWrite mv (fromIntegral row') value
56− V.unsafeFreeze mv
57−
58−finishQueryWithVU :: VU.Unbox r => RowParser r -> Connection -> Query -> PQ.Result -> IO (VU.Vector r)
59−finishQueryWithVU parser conn q result = finishQueryWith' q result $ do
60− nrows <- PQ.ntuples result
61− let PQ.Row nrows' = nrows
62− ncols <- PQ.nfields result
63− mv <- MVU.unsafeNew (fromIntegral nrows')
64− for_ [ 0 .. nrows-1 ] $ \row -> do
65− let PQ.Row row' = row
66− value <- getRowWith parser row ncols conn result
67− MVU.unsafeWrite mv (fromIntegral row') value
68− VU.unsafeFreeze mv
69−
70−finishQueryWith' :: Query -> PQ.Result -> IO a -> IO a
71−finishQueryWith' q result k = do
72− status <- PQ.resultStatus result
73− case status of
74− PQ.TuplesOk -> k
75− PQ.EmptyQuery -> queryErr "query: Empty query"
76− PQ.CommandOk -> queryErr "query resulted in a command response (did you mean to use `execute` or forget a RETURNING?)"
77− PQ.CopyOut -> queryErr "query: COPY TO is not supported"
78− PQ.CopyIn -> queryErr "query: COPY FROM is not supported"
79− PQ.CopyBoth -> queryErr "query: COPY BOTH is not supported"
80− PQ.SingleTuple -> queryErr "query: single-row mode is not supported"
81− PQ.BadResponse -> throwResultError "query" result status
82− PQ.NonfatalError -> throwResultError "query" result status
83− PQ.FatalError -> throwResultError "query" result status
84− where
85− queryErr msg = throwIO $ QueryError msg q
86−
87−getRowWith :: RowParser r -> PQ.Row -> PQ.Column -> Connection -> PQ.Result -> IO r
88−getRowWith parser row ncols conn result = do
89− let rw = Row row result
90− let unCol (PQ.Col x) = fromIntegral x :: Int
91− okvc <- runConversion (runStateT (runReaderT (unRP parser) rw) 0) conn
92− case okvc of
93− Ok (val,col) | col == ncols -> return val
94− | otherwise -> do
95− vals <- forM' 0 (ncols-1) $ \c -> do
96− tinfo <- getTypeInfo conn =<< PQ.ftype result c
97− v <- PQ.getvalue result row c
98− return ( tinfo
99− , fmap ellipsis v )
100− throw (ConversionFailed
101− (show (unCol ncols) ++ " values: " ++ show vals)
102− Nothing
103− ""
104− (show (unCol col) ++ " slots in target type")
105− "mismatch between number of columns to convert and number in target type")
106− Errors [] -> throwIO $ ConversionFailed "" Nothing "" "" "unknown error"
107− Errors [x] -> throwIO x
108− Errors xs -> throwIO $ ManyErrors xs
109−
110−ellipsis :: ByteString -> ByteString
111−ellipsis bs
112− | B.length bs > 15 = B.take 10 bs `B.append` "[...]"
113− | otherwise = bs
114−
115−forM' :: (Ord n, Num n) => n -> n -> (n -> IO a) -> IO [a]
116−forM' lo hi m = loop hi []
117− where
118− loop !n !as
119− | n < lo = return as
120− | otherwise = do
121− a <- m n
122− loop (n-1) (a:as)
123−{-# INLINE forM' #-}
src/Database/PostgreSQL/Simple/LargeObjects.hsdeleted−97
@@ -1,97 +0,0 @@
1−-----------------------------------------------------------------------------
2−-- |
3−-- Module : Database.PostgreSQL.Simple.LargeObjects
4−-- Copyright : (c) 2011-2012 Leon P Smith
5−-- License : BSD3
6−--
7−-- Maintainer : leon@melding-monads.com
8−--
9−-- Support for PostgreSQL's Large Objects; see
10−-- <https://www.postgresql.org/docs/9.5/static/largeobjects.html> for more
11−-- information.
12−--
13−-- Note that Large Object File Descriptors are only valid within a single
14−-- database transaction, so if you are interested in using anything beyond
15−-- 'loCreat', 'loCreate', and 'loUnlink', you will need to run the entire
16−-- sequence of functions in a transaction. As 'loImport' and 'loExport'
17−-- are simply C functions that call 'loCreat', 'loOpen', 'loRead', and
18−-- 'loWrite', and do not perform any transaction handling themselves,
19−-- they also need to be wrapped in an explicit transaction.
20−--
21−-----------------------------------------------------------------------------
22−
23−module Database.PostgreSQL.Simple.LargeObjects
24− ( loCreat
25− , loCreate
26− , loImport
27− , loImportWithOid
28− , loExport
29− , loOpen
30− , loWrite
31− , loRead
32− , loSeek
33− , loTell
34− , loTruncate
35− , loClose
36− , loUnlink
37− , Oid(..)
38− , LoFd
39− , IOMode(..)
40− , SeekMode(..)
41− ) where
42−
43−import Control.Applicative ((<$>))
44−import Control.Exception (throwIO)
45−import qualified Data.ByteString as B
46−import Database.PostgreSQL.LibPQ (Oid(..),LoFd(..))
47−import qualified Database.PostgreSQL.LibPQ as PQ
48−import Database.PostgreSQL.Simple.Internal
49−import System.IO (IOMode(..),SeekMode(..))
50−
51−liftPQ :: B.ByteString -> Connection -> (PQ.Connection -> IO (Maybe a)) -> IO a
52−liftPQ str conn m = withConnection conn $ \c -> do
53− res <- m c
54− case res of
55− Nothing -> do
56− msg <- maybe str id <$> PQ.errorMessage c
57− throwIO $ fatalError msg
58− Just x -> return x
59−
60−loCreat :: Connection -> IO Oid
61−loCreat conn = liftPQ "loCreat" conn (\c -> PQ.loCreat c)
62−
63−loCreate :: Connection -> Oid -> IO Oid
64−loCreate conn oid = liftPQ "loCreate" conn (\c -> PQ.loCreate c oid)
65−
66−loImport :: Connection -> FilePath -> IO Oid
67−loImport conn path = liftPQ "loImport" conn (\c -> PQ.loImport c path)
68−
69−loImportWithOid :: Connection -> FilePath -> Oid -> IO Oid
70−loImportWithOid conn path oid = liftPQ "loImportWithOid" conn (\c -> PQ.loImportWithOid c path oid)
71−
72−loExport :: Connection -> Oid -> FilePath -> IO ()
73−loExport conn oid path = liftPQ "loExport" conn (\c -> PQ.loExport c oid path)
74−
75−loOpen :: Connection -> Oid -> IOMode -> IO LoFd
76−loOpen conn oid mode = liftPQ "loOpen" conn (\c -> PQ.loOpen c oid mode )
77−
78−loWrite :: Connection -> LoFd -> B.ByteString -> IO Int
79−loWrite conn fd dat = liftPQ "loWrite" conn (\c -> PQ.loWrite c fd dat)
80−
81−loRead :: Connection -> LoFd -> Int -> IO B.ByteString
82−loRead conn fd maxlen = liftPQ "loRead" conn (\c -> PQ.loRead c fd maxlen)
83−
84−loSeek :: Connection -> LoFd -> SeekMode -> Int -> IO Int
85−loSeek conn fd seekmode offset = liftPQ "loSeek" conn (\c -> PQ.loSeek c fd seekmode offset)
86−
87−loTell :: Connection -> LoFd -> IO Int
88−loTell conn fd = liftPQ "loTell" conn (\c -> PQ.loTell c fd)
89−
90−loTruncate :: Connection -> LoFd -> Int -> IO ()
91−loTruncate conn fd len = liftPQ "loTruncate" conn (\c -> PQ.loTruncate c fd len)
92−
93−loClose :: Connection -> LoFd -> IO ()
94−loClose conn fd = liftPQ "loClose" conn (\c -> PQ.loClose c fd)
95−
96−loUnlink :: Connection -> Oid -> IO ()
97−loUnlink conn oid = liftPQ "loUnlink" conn (\c -> PQ.loUnlink c oid)
src/Database/PostgreSQL/Simple/Newtypes.hsdeleted−49
@@ -1,49 +0,0 @@
1−{-# LANGUAGE DeriveFunctor #-}
2−{-# LANGUAGE DeriveDataTypeable #-}
3−-- | Module with newtypes suitable to usage with @DerivingVia@ or standalone.
4−--
5−-- The newtypes are named after packages they wrap.
6−module Database.PostgreSQL.Simple.Newtypes (
7− Aeson (..), getAeson,
8−) where
9−
10−import Data.Typeable (Typeable)
11−import Database.PostgreSQL.Simple.ToField (ToField (..))
12−import Database.PostgreSQL.Simple.FromField (FromField (..), fromJSONField)
13−
14−import qualified Data.Aeson as Aeson
15−
16−-------------------------------------------------------------------------------
17−-- aeson
18−-------------------------------------------------------------------------------
19−
20−-- | A newtype wrapper with 'ToField' and 'FromField' instances
21−-- based on 'Aeson.ToJSON' and 'Aeson.FromJSON' type classes from @aeson@.
22−--
23−-- Example using @DerivingVia@:
24−--
25−-- @
26−-- data Foo = Foo Int String
27−-- deriving stock (Eq, Show, Generic) -- GHC built int
28−-- deriving anyclass ('Aeson.FromJSON', 'Aeson.ToJSON') -- Derived using GHC Generics
29−-- deriving ('ToField', 'FromField') via 'Aeson' Foo -- DerivingVia
30−-- @
31−--
32−-- Example using 'Aeson' newtype directly, for more ad-hoc queries
33−--
34−-- @
35−-- execute conn "INSERT INTO tbl (fld) VALUES (?)" (Only ('Aeson' x))
36−-- @
37−--
38−-- @since 0.6.3
39−newtype Aeson a = Aeson a
40− deriving (Eq, Show, Read, Typeable, Functor)
41−
42−getAeson :: Aeson a -> a
43−getAeson (Aeson a) = a
44−
45−instance Aeson.ToJSON a => ToField (Aeson a) where
46− toField = toField . Aeson.encode . getAeson
47−
48−instance (Aeson.FromJSON a, Typeable a) => FromField (Aeson a) where
49− fromField f bs = fmap Aeson (fromJSONField f bs)
src/Database/PostgreSQL/Simple/Notification.hsdeleted−145
@@ -1,145 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE NamedFieldPuns #-}
3−{-# LANGUAGE RecordWildCards #-}
4−
5−-----------------------------------------------------------------------------
6−-- |
7−-- Module : Database.PostgreSQL.Simple.Notification
8−-- Copyright : (c) 2011-2015 Leon P Smith
9−-- (c) 2012 Joey Adams
10−-- License : BSD3
11−--
12−-- Maintainer : leon@melding-monads.com
13−-- Stability : experimental
14−--
15−-- Support for receiving asynchronous notifications via PostgreSQL's
16−-- Listen/Notify mechanism. See
17−-- <https://www.postgresql.org/docs/9.5/static/sql-notify.html> for more
18−-- information.
19−--
20−-- Note that on Windows, @getNotification@ currently uses a polling loop
21−-- of 1 second to check for more notifications, due to some inadequacies
22−-- in GHC's IO implementation and interface on that platform. See GHC
23−-- issue #7353 for more information. While this workaround is less than
24−-- ideal, notifications are still better than polling the database directly.
25−-- Notifications do not create any extra work for the backend, and are
26−-- likely cheaper on the client side as well.
27−--
28−-- <https://hackage.haskell.org/trac/ghc/ticket/7353>
29−--
30−-----------------------------------------------------------------------------
31−
32−module Database.PostgreSQL.Simple.Notification
33− ( Notification(..)
34− , getNotification
35− , getNotificationNonBlocking
36− , getBackendPID
37− ) where
38−
39−import Control.Monad ( join, void )
40−import Control.Exception ( throwIO, catch )
41−import qualified Data.ByteString as B
42−import qualified Data.ByteString.Char8 as B8
43−import Database.PostgreSQL.Simple.Internal
44−import qualified Database.PostgreSQL.LibPQ as PQ
45−import System.Posix.Types ( CPid )
46−import GHC.IO.Exception ( ioe_location )
47−
48−#if defined(mingw32_HOST_OS)
49−import Control.Concurrent ( threadDelay )
50−#else
51−import GHC.Conc ( atomically )
52−import Control.Concurrent ( threadWaitReadSTM )
53−#endif
54−
55−data Notification = Notification
56− { notificationPid :: {-# UNPACK #-} !CPid
57− , notificationChannel :: {-# UNPACK #-} !B.ByteString
58− , notificationData :: {-# UNPACK #-} !B.ByteString
59− } deriving (Show, Eq)
60−
61−convertNotice :: PQ.Notify -> Notification
62−convertNotice PQ.Notify{..}
63− = Notification { notificationPid = notifyBePid
64− , notificationChannel = notifyRelname
65− , notificationData = notifyExtra }
66−
67−-- | Returns a single notification. If no notifications are available,
68−-- 'getNotification' blocks until one arrives.
69−--
70−-- It is safe to call 'getNotification' on a connection that is concurrently
71−-- being used for other purposes, note however that PostgreSQL does not
72−-- deliver notifications while a connection is inside a transaction.
73−
74−getNotification :: Connection -> IO Notification
75−getNotification conn = join $ withConnection conn fetch
76− where
77− funcName = "Database.PostgreSQL.Simple.Notification.getNotification"
78−
79− fetch c = do
80− mmsg <- PQ.notifies c
81− case mmsg of
82− Just msg -> return (return $! convertNotice msg)
83− Nothing -> do
84− mfd <- PQ.socket c
85− case mfd of
86− Nothing -> return (throwIO $! fdError funcName)
87−#if defined(mingw32_HOST_OS)
88− -- threadWaitRead doesn't work for sockets on Windows, so just
89− -- poll for input every second (PQconsumeInput is non-blocking).
90− --
91− -- We could call select(), but FFI calls can't be interrupted
92− -- with async exceptions, whereas threadDelay can.
93− Just _fd -> do
94− return (threadDelay 1000000 >> loop)
95−#else
96− -- This case fixes the race condition above. By registering
97− -- our interest in the descriptor before we drop the lock,
98− -- there is no opportunity for the descriptor index to be
99− -- reallocated on us.
100− --
101− -- (That is, assuming there isn't concurrently executing
102− -- code that manipulates the descriptor without holding
103− -- the lock... but such a major bug is likely to exhibit
104− -- itself in an at least somewhat more dramatic fashion.)
105− Just fd -> do
106− (waitRead, _) <- threadWaitReadSTM fd
107− return $ do
108− atomically waitRead `catch` (throwIO . setIOErrorLocation)
109− loop
110−#endif
111−
112− loop = join $ withConnection conn $ \c -> do
113− void $ PQ.consumeInput c
114− fetch c
115−
116− setIOErrorLocation :: IOError -> IOError
117− setIOErrorLocation err = err { ioe_location = B8.unpack funcName }
118−
119−
120−-- | Non-blocking variant of 'getNotification'. Returns a single notification,
121−-- if available. If no notifications are available, returns 'Nothing'.
122−
123−getNotificationNonBlocking :: Connection -> IO (Maybe Notification)
124−getNotificationNonBlocking conn =
125− withConnection conn $ \c -> do
126− mmsg <- PQ.notifies c
127− case mmsg of
128− Just msg -> return $! Just $! convertNotice msg
129− Nothing -> do
130− _ <- PQ.consumeInput c
131− mmsg' <- PQ.notifies c
132− case mmsg' of
133− Just msg -> return $! Just $! convertNotice msg
134− Nothing -> return Nothing
135−
136−-- | Returns the process 'CPid' of the backend server process
137−-- handling this connection.
138−--
139−-- The backend PID is useful for debugging purposes and for comparison
140−-- to NOTIFY messages (which include the PID of the notifying backend
141−-- process). Note that the PID belongs to a process executing on the
142−-- database server host, not the local host!
143−
144−getBackendPID :: Connection -> IO CPid
145−getBackendPID conn = withConnection conn PQ.backendPID
src/Database/PostgreSQL/Simple/Ok.hsdeleted−90
@@ -1,90 +0,0 @@
1−{-# LANGUAGE DeriveDataTypeable #-}
2−{-# LANGUAGE DeriveFunctor #-}
3−{-# LANGUAGE CPP #-}
4−
5−------------------------------------------------------------------------------
6−-- |
7−-- Module : Database.PostgreSQL.Simple.Ok
8−-- Copyright : (c) 2012-2015 Leon P Smith
9−-- License : BSD3
10−--
11−-- Maintainer : leon@melding-monads.com
12−-- Stability : experimental
13−--
14−-- The 'Ok' type is a simple error handler, basically equivalent to
15−-- @Either [SomeException]@. This type (without the list) was used to
16−-- handle conversion errors in early versions of postgresql-simple.
17−--
18−-- One of the primary reasons why this type was introduced is that
19−-- @Either SomeException@ had not been provided an instance for 'Alternative',
20−-- and it would have been a bad idea to provide an orphaned instance for a
21−-- commonly-used type and typeclass included in @base@.
22−--
23−-- Extending the failure case to a list of 'SomeException's enables a
24−-- more sensible 'Alternative' instance definitions: '<|>' concatenates
25−-- the list of exceptions when both cases fail, and 'empty' is defined as
26−-- 'Errors []'. Though '<|>' one could pick one of two exceptions, and
27−-- throw away the other, and have 'empty' provide a generic exception,
28−-- this avoids cases where 'empty' overrides a more informative exception
29−-- and allows you to see all the different ways your computation has failed.
30−--
31−------------------------------------------------------------------------------
32−
33−module Database.PostgreSQL.Simple.Ok where
34−
35−import Control.Applicative
36−import Control.Exception
37−import Control.Monad(MonadPlus(..))
38−import Data.Typeable
39−
40−import qualified Control.Monad.Fail as Fail
41−
42−-- FIXME: [SomeException] should probably be something else, maybe
43−-- a difference list (or a tree?)
44−
45−data Ok a = Errors [SomeException] | Ok !a
46− deriving(Show, Typeable, Functor)
47−
48−-- | Two 'Errors' cases are considered equal, regardless of what the
49−-- list of exceptions looks like.
50−
51−instance Eq a => Eq (Ok a) where
52− Errors _ == Errors _ = True
53− Ok a == Ok b = a == b
54− _ == _ = False
55−
56−instance Applicative Ok where
57− pure = Ok
58−
59− Errors es <*> _ = Errors es
60− _ <*> Errors es = Errors es
61− Ok f <*> Ok a = Ok (f a)
62−
63−instance Alternative Ok where
64− empty = Errors []
65−
66− a@(Ok _) <|> _ = a
67− Errors _ <|> b@(Ok _) = b
68− Errors as <|> Errors bs = Errors (as ++ bs)
69−
70−instance MonadPlus Ok where
71− mzero = empty
72− mplus = (<|>)
73−
74−instance Monad Ok where
75− Errors es >>= _ = Errors es
76− Ok a >>= f = f a
77−
78−#if !(MIN_VERSION_base(4,13,0))
79− fail = Fail.fail
80−#endif
81−
82−instance Fail.MonadFail Ok where
83− fail str = Errors [SomeException (ErrorCall str)]
84−
85−-- | a way to reify a list of exceptions into a single exception
86−
87−newtype ManyErrors = ManyErrors [SomeException]
88− deriving (Show, Typeable)
89−
90−instance Exception ManyErrors
src/Database/PostgreSQL/Simple/Param.hsnew file+205
@@ -0,0 +1,205 @@
1+{-# LANGUAGE DeriveDataTypeable, DeriveFunctor, FlexibleInstances,
2+ OverloadedStrings #-}
3+
4+-- |
5+-- Module: Database.PostgreSQL.Simple.Param
6+-- Copyright: (c) 2011 MailRank, Inc.
7+-- (c) 2011 Leon P Smith
8+-- License: BSD3
9+-- Maintainer: Leon P Smith <leon@melding-monads.com>
10+-- Stability: experimental
11+-- Portability: portable
12+--
13+-- The 'Param' typeclass, for rendering a parameter to a SQL query.
14+
15+module Database.PostgreSQL.Simple.Param
16+ (
17+ Action(..)
18+ , Param(..)
19+ , inQuotes
20+ ) where
21+
22+import Blaze.ByteString.Builder (Builder, fromByteString, fromLazyByteString,
23+ toByteString)
24+import Blaze.ByteString.Builder.Char8 (fromChar)
25+import Blaze.Text (integral, double, float)
26+import Data.ByteString (ByteString)
27+import qualified Data.ByteString.Base16 as B16
28+import qualified Data.ByteString.Base16.Lazy as L16
29+import Data.Int (Int8, Int16, Int32, Int64)
30+import Data.List (intersperse)
31+import Data.Monoid (mappend)
32+import Data.Time.Calendar (Day, showGregorian)
33+import Data.Time.Clock (UTCTime)
34+import Data.Time.Format (formatTime)
35+import Data.Time.LocalTime (TimeOfDay)
36+import Data.Typeable (Typeable)
37+import Data.Word (Word, Word8, Word16, Word32, Word64)
38+import Database.PostgreSQL.Simple.Types (Binary(..), In(..), Null)
39+import System.Locale (defaultTimeLocale)
40+import qualified Blaze.ByteString.Builder.Char.Utf8 as Utf8
41+import qualified Data.ByteString as SB
42+import qualified Data.ByteString.Lazy as LB
43+import qualified Data.Text as ST
44+import qualified Data.Text.Encoding as ST
45+import qualified Data.Text.Lazy as LT
46+import qualified Database.PostgreSQL.LibPQ as PQ
47+
48+-- | How to render an element when substituting it into a query.
49+data Action =
50+ Plain Builder
51+ -- ^ Render without escaping or quoting. Use for non-text types
52+ -- such as numbers, when you are /certain/ that they will not
53+ -- introduce formatting vulnerabilities via use of characters such
54+ -- as spaces or \"@'@\".
55+ | Escape ByteString
56+ -- ^ Escape and enclose in quotes before substituting. Use for all
57+ -- text-like types, and anything else that may contain unsafe
58+ -- characters when rendered.
59+ | Many [Action]
60+ -- ^ Concatenate a series of rendering actions.
61+ deriving (Typeable)
62+
63+instance Show Action where
64+ show (Plain b) = "Plain " ++ show (toByteString b)
65+ show (Escape b) = "Escape " ++ show b
66+ show (Many b) = "Many " ++ show b
67+
68+-- | A type that may be used as a single parameter to a SQL query.
69+class Param a where
70+ render :: a -> Action
71+ -- ^ Prepare a value for substitution into a query string.
72+
73+instance Param Action where
74+ render a = a
75+ {-# INLINE render #-}
76+
77+instance (Param a) => Param (Maybe a) where
78+ render Nothing = renderNull
79+ render (Just a) = render a
80+ {-# INLINE render #-}
81+
82+instance (Param a) => Param (In [a]) where
83+ render (In []) = Plain $ fromByteString "(null)"
84+ render (In xs) = Many $
85+ Plain (fromChar '(') :
86+ (intersperse (Plain (fromChar ',')) . map render $ xs) ++
87+ [Plain (fromChar ')')]
88+
89+instance Param (Binary SB.ByteString) where
90+ render (Binary bs) = Plain $ fromByteString "'\\x" `mappend`
91+ fromByteString (B16.encode bs) `mappend`
92+ fromChar '\''
93+
94+instance Param (Binary LB.ByteString) where
95+ render (Binary bs) = Plain $ fromByteString "'\\x" `mappend`
96+ fromLazyByteString (L16.encode bs) `mappend`
97+ fromChar '\''
98+
99+renderNull :: Action
100+renderNull = Plain (fromByteString "null")
101+
102+instance Param Null where
103+ render _ = renderNull
104+ {-# INLINE render #-}
105+
106+instance Param Bool where
107+ render = Plain . integral . fromEnum
108+ {-# INLINE render #-}
109+
110+instance Param Int8 where
111+ render = Plain . integral
112+ {-# INLINE render #-}
113+
114+instance Param Int16 where
115+ render = Plain . integral
116+ {-# INLINE render #-}
117+
118+instance Param Int32 where
119+ render = Plain . integral
120+ {-# INLINE render #-}
121+
122+instance Param Int where
123+ render = Plain . integral
124+ {-# INLINE render #-}
125+
126+instance Param Int64 where
127+ render = Plain . integral
128+ {-# INLINE render #-}
129+
130+instance Param Integer where
131+ render = Plain . integral
132+ {-# INLINE render #-}
133+
134+instance Param Word8 where
135+ render = Plain . integral
136+ {-# INLINE render #-}
137+
138+instance Param Word16 where
139+ render = Plain . integral
140+ {-# INLINE render #-}
141+
142+instance Param Word32 where
143+ render = Plain . integral
144+ {-# INLINE render #-}
145+
146+instance Param Word where
147+ render = Plain . integral
148+ {-# INLINE render #-}
149+
150+instance Param Word64 where
151+ render = Plain . integral
152+ {-# INLINE render #-}
153+
154+instance Param PQ.Oid where
155+ render = Plain . integral . \(PQ.Oid x) -> x
156+ {-# INLINE render #-}
157+
158+instance Param Float where
159+ render v | isNaN v || isInfinite v = renderNull
160+ | otherwise = Plain (float v)
161+ {-# INLINE render #-}
162+
163+instance Param Double where
164+ render v | isNaN v || isInfinite v = renderNull
165+ | otherwise = Plain (double v)
166+ {-# INLINE render #-}
167+
168+instance Param SB.ByteString where
169+ render = Escape
170+ {-# INLINE render #-}
171+
172+instance Param LB.ByteString where
173+ render = render . SB.concat . LB.toChunks
174+ {-# INLINE render #-}
175+
176+instance Param ST.Text where
177+ render = Escape . ST.encodeUtf8
178+ {-# INLINE render #-}
179+
180+instance Param [Char] where
181+ render = Escape . toByteString . Utf8.fromString
182+ {-# INLINE render #-}
183+
184+instance Param LT.Text where
185+ render = render . LT.toStrict
186+ {-# INLINE render #-}
187+
188+instance Param UTCTime where
189+ render = Plain . Utf8.fromString . formatTime defaultTimeLocale "'%F %T%Q+00'"
190+ {-# INLINE render #-}
191+
192+instance Param Day where
193+ render = Plain . inQuotes . Utf8.fromString . showGregorian
194+ {-# INLINE render #-}
195+
196+instance Param TimeOfDay where
197+ render = Plain . inQuotes . Utf8.fromString . show
198+ {-# INLINE render #-}
199+
200+-- | Surround a string with single-quote characters: \"@'@\"
201+--
202+-- This function /does not/ perform any other escaping.
203+inQuotes :: Builder -> Builder
204+inQuotes b = quote `mappend` b `mappend` quote
205+ where quote = Utf8.fromChar '\''
src/Database/PostgreSQL/Simple/QueryParams.hsnew file+85
@@ -0,0 +1,85 @@
1+-- |
2+-- Module: Database.PostgreSQL.Simple.QueryParams
3+-- Copyright: (c) 2011 MailRank, Inc.
4+-- (c) 2011 Leon P Smith
5+-- License: BSD3
6+-- Maintainer: Leon P Smith <leon@melding-monads.com>
7+-- Stability: experimental
8+-- Portability: portable
9+--
10+-- The 'QueryParams' typeclass, for rendering a collection of
11+-- parameters to a SQL query.
12+--
13+-- Predefined instances are provided for tuples containing up to ten
14+-- elements.
15+
16+module Database.PostgreSQL.Simple.QueryParams
17+ (
18+ QueryParams(..)
19+ ) where
20+
21+import Database.PostgreSQL.Simple.Param (Action(..), Param(..))
22+import Database.PostgreSQL.Simple.Types (Only(..))
23+
24+-- | A collection type that can be turned into a list of rendering
25+-- 'Action's.
26+--
27+-- Instances should use the 'render' method of the 'Param' class
28+-- to perform conversion of each element of the collection.
29+class QueryParams a where
30+ renderParams :: a -> [Action]
31+ -- ^ Render a collection of values.
32+
33+instance QueryParams () where
34+ renderParams _ = []
35+
36+instance (Param a) => QueryParams (Only a) where
37+ renderParams (Only v) = [render v]
38+
39+instance (Param a, Param b) => QueryParams (a,b) where
40+ renderParams (a,b) = [render a, render b]
41+
42+instance (Param a, Param b, Param c) => QueryParams (a,b,c) where
43+ renderParams (a,b,c) = [render a, render b, render c]
44+
45+instance (Param a, Param b, Param c, Param d) => QueryParams (a,b,c,d) where
46+ renderParams (a,b,c,d) = [render a, render b, render c, render d]
47+
48+instance (Param a, Param b, Param c, Param d, Param e)
49+ => QueryParams (a,b,c,d,e) where
50+ renderParams (a,b,c,d,e) =
51+ [render a, render b, render c, render d, render e]
52+
53+instance (Param a, Param b, Param c, Param d, Param e, Param f)
54+ => QueryParams (a,b,c,d,e,f) where
55+ renderParams (a,b,c,d,e,f) =
56+ [render a, render b, render c, render d, render e, render f]
57+
58+instance (Param a, Param b, Param c, Param d, Param e, Param f, Param g)
59+ => QueryParams (a,b,c,d,e,f,g) where
60+ renderParams (a,b,c,d,e,f,g) =
61+ [render a, render b, render c, render d, render e, render f, render g]
62+
63+instance (Param a, Param b, Param c, Param d, Param e, Param f, Param g,
64+ Param h)
65+ => QueryParams (a,b,c,d,e,f,g,h) where
66+ renderParams (a,b,c,d,e,f,g,h) =
67+ [render a, render b, render c, render d, render e, render f, render g,
68+ render h]
69+
70+instance (Param a, Param b, Param c, Param d, Param e, Param f, Param g,
71+ Param h, Param i)
72+ => QueryParams (a,b,c,d,e,f,g,h,i) where
73+ renderParams (a,b,c,d,e,f,g,h,i) =
74+ [render a, render b, render c, render d, render e, render f, render g,
75+ render h, render i]
76+
77+instance (Param a, Param b, Param c, Param d, Param e, Param f, Param g,
78+ Param h, Param i, Param j)
79+ => QueryParams (a,b,c,d,e,f,g,h,i,j) where
80+ renderParams (a,b,c,d,e,f,g,h,i,j) =
81+ [render a, render b, render c, render d, render e, render f, render g,
82+ render h, render i, render j]
83+
84+instance (Param a) => QueryParams [a] where
85+ renderParams = map render
src/Database/PostgreSQL/Simple/QueryResults.hsnew file+219
@@ -0,0 +1,219 @@
1+{-# LANGUAGE BangPatterns, OverloadedStrings #-}
2+
3+-- |
4+-- Module: Database.MySQL.Simpe.QueryResults
5+-- Copyright: (c) 2011 MailRank, Inc.
6+-- (c) 2011 Leon P Smith
7+-- License: BSD3
8+-- Maintainer: Leon P Smith <leon@melding-monads.com>
9+-- Stability: experimental
10+-- Portability: portable
11+--
12+-- The 'QueryResults' typeclass, for converting a row of results
13+-- returned by a SQL query into a more useful Haskell representation.
14+--
15+-- Predefined instances are provided for tuples containing up to ten
16+-- elements.
17+
18+module Database.PostgreSQL.Simple.QueryResults
19+ (
20+ QueryResults(..)
21+ , convertError
22+ ) where
23+
24+import Control.Exception (throw)
25+import Data.ByteString (ByteString)
26+import qualified Data.ByteString.Char8 as B
27+import Database.PostgreSQL.Simple.Implementation
28+import Database.PostgreSQL.Simple.Result (ResultError(..), Result(..),Status(..))
29+import Database.PostgreSQL.Simple.Types (Only(..))
30+
31+-- | A collection type that can be converted from a list of strings.
32+--
33+-- Instances should use the 'convert' method of the 'Result' class
34+-- to perform conversion of each element of the collection.
35+--
36+-- This example instance demonstrates how to convert a two-column row
37+-- into a Haskell pair. Each field in the metadata is paired up with
38+-- each value from the row, and the two are passed to 'convert'.
39+--
40+-- @
41+-- instance ('Result' a, 'Result' b) => 'QueryResults' (a,b) where
42+-- 'convertResults' [fa,fb] [va,vb] = (a,b)
43+-- where !a = 'convert' fa va
44+-- !b = 'convert' fb vb
45+-- 'convertResults' fs vs = 'convertError' fs vs 2
46+-- @
47+--
48+-- Notice that this instance evaluates each element to WHNF before
49+-- constructing the pair. By doing this, we guarantee two important
50+-- properties:
51+--
52+-- * Keep resource usage under control by preventing the construction
53+-- of potentially long-lived thunks.
54+--
55+-- * Ensure that any 'ResultError' that might arise is thrown
56+-- immediately, rather than some place later in application code
57+-- that cannot handle it.
58+--
59+-- You can also declare Haskell types of your own to be instances of
60+-- 'QueryResults'.
61+--
62+-- @
63+--data User { firstName :: String, lastName :: String }
64+--
65+--instance 'QueryResults' User where
66+-- 'convertResults' [fa,fb] [va,vb] = User a b
67+-- where !a = 'convert' fa va
68+-- !b = 'convert' fb vb
69+-- 'convertResults' fs vs = 'convertError' fs vs
70+-- @
71+
72+class QueryResults a where
73+ convertResults :: [Field] -> [Maybe ByteString] -> Status ResultError a
74+ -- ^ Convert values from a row into a Haskell collection.
75+ --
76+ -- This function will throw a 'ResultError' if conversion of the
77+ -- collection fails.
78+
79+instance (Result a) => QueryResults (Only a) where
80+ convertResults [fa] [va] = do
81+ !a <- convert fa va
82+ return (Only a)
83+ convertResults fs vs = convertError fs vs 1
84+
85+instance (Result a, Result b) => QueryResults (a,b) where
86+ convertResults [fa,fb] [va,vb] = do
87+ !a <- convert fa va
88+ !b <- convert fb vb
89+ return (a,b)
90+ convertResults fs vs = convertError fs vs 2
91+
92+instance (Result a, Result b, Result c) => QueryResults (a,b,c) where
93+ convertResults [fa,fb,fc] [va,vb,vc] = do
94+ !a <- convert fa va
95+ !b <- convert fb vb
96+ !c <- convert fc vc
97+ return (a,b,c)
98+ convertResults fs vs = convertError fs vs 3
99+
100+instance (Result a, Result b, Result c, Result d) =>
101+ QueryResults (a,b,c,d) where
102+ convertResults [fa,fb,fc,fd] [va,vb,vc,vd] = do
103+ !a <- convert fa va
104+ !b <- convert fb vb
105+ !c <- convert fc vc
106+ !d <- convert fd vd
107+ return (a,b,c,d)
108+ convertResults fs vs = convertError fs vs 4
109+
110+instance (Result a, Result b, Result c, Result d, Result e) =>
111+ QueryResults (a,b,c,d,e) where
112+ convertResults [fa,fb,fc,fd,fe] [va,vb,vc,vd,ve] = do
113+ !a <- convert fa va
114+ !b <- convert fb vb
115+ !c <- convert fc vc
116+ !d <- convert fd vd
117+ !e <- convert fe ve
118+ return (a,b,c,d,e)
119+ convertResults fs vs = convertError fs vs 5
120+
121+instance (Result a, Result b, Result c, Result d, Result e, Result f) =>
122+ QueryResults (a,b,c,d,e,f) where
123+ convertResults [fa,fb,fc,fd,fe,ff] [va,vb,vc,vd,ve,vf] = do
124+ !a <- convert fa va
125+ !b <- convert fb vb
126+ !c <- convert fc vc
127+ !d <- convert fd vd
128+ !e <- convert fe ve
129+ !f <- convert ff vf
130+ return (a,b,c,d,e,f)
131+ convertResults fs vs = convertError fs vs 6
132+
133+instance (Result a, Result b, Result c, Result d, Result e, Result f,
134+ Result g) =>
135+ QueryResults (a,b,c,d,e,f,g) where
136+ convertResults [fa,fb,fc,fd,fe,ff,fg] [va,vb,vc,vd,ve,vf,vg] = do
137+ !a <- convert fa va
138+ !b <- convert fb vb
139+ !c <- convert fc vc
140+ !d <- convert fd vd
141+ !e <- convert fe ve
142+ !f <- convert ff vf
143+ !g <- convert fg vg
144+ return (a,b,c,d,e,f,g)
145+ convertResults fs vs = convertError fs vs 7
146+
147+instance (Result a, Result b, Result c, Result d, Result e, Result f,
148+ Result g, Result h) =>
149+ QueryResults (a,b,c,d,e,f,g,h) where
150+ convertResults [fa,fb,fc,fd,fe,ff,fg,fh] [va,vb,vc,vd,ve,vf,vg,vh] = do
151+ !a <- convert fa va
152+ !b <- convert fb vb
153+ !c <- convert fc vc
154+ !d <- convert fd vd
155+ !e <- convert fe ve
156+ !f <- convert ff vf
157+ !g <- convert fg vg
158+ !h <- convert fh vh
159+ return (a,b,c,d,e,f,g,h)
160+ convertResults fs vs = convertError fs vs 8
161+
162+instance (Result a, Result b, Result c, Result d, Result e, Result f,
163+ Result g, Result h, Result i) =>
164+ QueryResults (a,b,c,d,e,f,g,h,i) where
165+ convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi] [va,vb,vc,vd,ve,vf,vg,vh,vi] =
166+ do
167+ !a <- convert fa va
168+ !b <- convert fb vb
169+ !c <- convert fc vc
170+ !d <- convert fd vd
171+ !e <- convert fe ve
172+ !f <- convert ff vf
173+ !g <- convert fg vg
174+ !h <- convert fh vh
175+ !i <- convert fi vi
176+ return (a,b,c,d,e,f,g,h,i)
177+ convertResults fs vs = convertError fs vs 9
178+
179+instance (Result a, Result b, Result c, Result d, Result e, Result f,
180+ Result g, Result h, Result i, Result j) =>
181+ QueryResults (a,b,c,d,e,f,g,h,i,j) where
182+ convertResults [fa,fb,fc,fd,fe,ff,fg,fh,fi,fj]
183+ [va,vb,vc,vd,ve,vf,vg,vh,vi,vj] =
184+ do
185+ !a <- convert fa va
186+ !b <- convert fb vb
187+ !c <- convert fc vc
188+ !d <- convert fd vd
189+ !e <- convert fe ve
190+ !f <- convert ff vf
191+ !g <- convert fg vg
192+ !h <- convert fh vh
193+ !i <- convert fi vi
194+ !j <- convert fj vj
195+ return (a,b,c,d,e,f,g,h,i,j)
196+ convertResults fs vs = convertError fs vs 10
197+
198+-- | Throw a 'ConversionFailed' exception, indicating a mismatch
199+-- between the number of columns in the 'Field' and row, and the
200+-- number in the collection to be converted to.
201+convertError :: [Field]
202+ -- ^ Descriptors of fields to be converted.
203+ -> [Maybe ByteString]
204+ -- ^ Contents of the row to be converted.
205+ -> Int
206+ -- ^ Number of columns expected for conversion. For
207+ -- instance, if converting to a 3-tuple, the number to
208+ -- provide here would be 3.
209+ -> Status ResultError a
210+convertError fs vs n = Fail $ ConversionFailed
211+ (show (length fs) ++ " values: " ++ show (zip (map typename fs)
212+ (map (fmap ellipsis) vs)))
213+ (show n ++ " slots in target type")
214+ "mismatch between number of columns to convert and number in target type"
215+
216+ellipsis :: ByteString -> ByteString
217+ellipsis bs
218+ | B.length bs > 15 = B.take 10 bs `B.append` "[...]"
219+ | otherwise = bs
src/Database/PostgreSQL/Simple/Range.hsdeleted−317
@@ -1,317 +0,0 @@
1−{-# LANGUAGE DeriveDataTypeable #-}
2−{-# LANGUAGE DeriveFunctor #-}
3−{-# LANGUAGE OverloadedStrings #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−
6−------------------------------------------------------------------------------
7−-- |
8−-- Module: Database.PostgreSQL.Simple.Range
9−-- Copyright: (c) 2014-2015 Leonid Onokhov
10−-- (c) 2014-2015 Leon P Smith
11−-- License: BSD3
12−-- Maintainer: Leon P Smith <leon@melding-monads.com>
13−--
14−------------------------------------------------------------------------------
15−
16−module Database.PostgreSQL.Simple.Range
17− ( RangeBound(..)
18− , PGRange(..)
19− , empty
20− , isEmpty, isEmptyBy
21− , contains, containsBy
22− , fromFieldRange
23− ) where
24−
25−import Control.Applicative hiding (empty)
26−import Data.Attoparsec.ByteString.Char8 (Parser, parseOnly)
27−import qualified Data.Attoparsec.ByteString.Char8 as A
28−import qualified Data.ByteString as B
29−import Data.ByteString.Builder
30− ( Builder, byteString, lazyByteString, char8
31− , intDec, int8Dec, int16Dec, int32Dec, int64Dec, integerDec
32− , wordDec, word8Dec, word16Dec, word32Dec, word64Dec
33− , doubleDec, floatDec )
34−import Data.Int (Int16, Int32, Int64,
35− Int8)
36−import Data.Function (on)
37−import Data.Monoid (mempty)
38−import Data.Scientific (Scientific)
39−import qualified Data.Text.Lazy.Builder as LT
40−import qualified Data.Text.Lazy.Encoding as LT
41−import Data.Time.Compat (Day, LocalTime,
42− NominalDiffTime,
43− TimeOfDay, UTCTime,
44− ZonedTime,
45− zonedTimeToUTC)
46−import Data.Typeable (Typeable)
47−import Data.Word (Word, Word16, Word32,
48− Word64, Word8)
49−
50−import Database.PostgreSQL.Simple.Compat (scientificBuilder, (<>), toByteString)
51−import Database.PostgreSQL.Simple.FromField
52−import Database.PostgreSQL.Simple.Time
53− hiding (PosInfinity, NegInfinity)
54−-- import qualified Database.PostgreSQL.Simple.Time as Time
55−import Database.PostgreSQL.Simple.ToField
56−
57−-- | Represents boundary of a range
58−data RangeBound a = NegInfinity
59− | Inclusive !a
60− | Exclusive !a
61− | PosInfinity
62− deriving (Show, Typeable, Eq, Functor)
63−
64−-- | Generic range type
65−data PGRange a = PGRange !(RangeBound a) !(RangeBound a)
66− deriving (Show, Typeable, Functor)
67−
68−empty :: PGRange a
69−empty = PGRange PosInfinity NegInfinity
70−
71−instance Ord a => Eq (PGRange a) where
72− x == y = eq x y || (isEmpty x && isEmpty y)
73− where eq (PGRange a m) (PGRange b n) = a == b && m == n
74−
75−isEmptyBy :: (a -> a -> Ordering) -> PGRange a -> Bool
76−isEmptyBy cmp v =
77− case v of
78− (PGRange PosInfinity _) -> True
79− (PGRange _ NegInfinity) -> True
80− (PGRange NegInfinity _) -> False
81− (PGRange _ PosInfinity) -> False
82− (PGRange (Inclusive x) (Inclusive y)) -> cmp x y == GT
83− (PGRange (Inclusive x) (Exclusive y)) -> cmp x y /= LT
84− (PGRange (Exclusive x) (Inclusive y)) -> cmp x y /= LT
85− (PGRange (Exclusive x) (Exclusive y)) -> cmp x y /= LT
86−
87−-- | Is a range empty? If this returns 'True', then the 'contains'
88−-- predicate will always return 'False'. However, if this returns
89−-- 'False', it is not necessarily true that there exists a point for
90−-- which 'contains' returns 'True'.
91−-- Consider @'PGRange' ('Excludes' 2) ('Excludes' 3) :: PGRange Int@,
92−-- for example.
93−isEmpty :: Ord a => PGRange a -> Bool
94−isEmpty = isEmptyBy compare
95−
96−
97−-- | Does a range contain a given point? Note that in some cases, this may
98−-- not correspond exactly with a server-side computation. Consider @UTCTime@
99−-- for example, which has a resolution of a picosecond, whereas postgresql's
100−-- @timestamptz@ types have a resolution of a microsecond. Putting such
101−-- Haskell values into the database will result in them being rounded, which
102−-- can change the value of the containment predicate.
103−
104−contains :: Ord a => PGRange a -> (a -> Bool)
105−contains = containsBy compare
106−
107−containsBy :: (a -> a -> Ordering) -> PGRange a -> (a -> Bool)
108−containsBy cmp rng x =
109− case rng of
110− PGRange _lb NegInfinity -> False
111− PGRange lb ub -> checkLB lb x && checkUB ub x
112− where
113− checkLB lb y =
114− case lb of
115− NegInfinity -> True
116− PosInfinity -> False
117− Inclusive a -> cmp a y /= GT
118− Exclusive a -> cmp a y == LT
119−
120− checkUB ub y =
121− case ub of
122− NegInfinity -> False
123− PosInfinity -> True
124− Inclusive z -> cmp y z /= GT
125− Exclusive z -> cmp y z == LT
126−
127−lowerBound :: Parser (a -> RangeBound a)
128−lowerBound = (A.char '(' *> pure Exclusive) <|> (A.char '[' *> pure Inclusive)
129−{-# INLINE lowerBound #-}
130−
131−upperBound :: Parser (a -> RangeBound a)
132−upperBound = (A.char ')' *> pure Exclusive) <|> (A.char ']' *> pure Inclusive)
133−{-# INLINE upperBound #-}
134−
135−-- | Generic range parser
136−pgrange :: Parser (RangeBound B.ByteString, RangeBound B.ByteString)
137−pgrange = do
138− lb <- lowerBound
139− v1 <- (A.char ',' *> "") <|> (rangeElem (==',') <* A.char ',')
140− v2 <- rangeElem $ \c -> c == ')' || c == ']'
141− ub <- upperBound
142− A.endOfInput
143− let low = if B.null v1 then NegInfinity else lb v1
144− up = if B.null v2 then PosInfinity else ub v2
145− return (low, up)
146−
147−rangeElem :: (Char -> Bool) -> Parser B.ByteString
148−rangeElem end = (A.char '"' *> doubleQuoted)
149− <|> A.takeTill end
150−{-# INLINE rangeElem #-}
151−
152−-- | Simple double quoted value parser
153−doubleQuoted :: Parser B.ByteString
154−doubleQuoted = toByteString <$> go mempty
155− where
156− go acc = do
157− h <- byteString <$> A.takeTill (\c -> c == '\\' || c == '"')
158− let rest = do
159− start <- A.anyChar
160− case start of
161− '\\' -> do
162− c <- A.anyChar
163− go (acc <> h <> char8 c)
164− '"' -> (A.char '"' *> go (acc <> h <> char8 '"'))
165− <|> pure (acc <> h)
166− _ -> error "impossible in doubleQuoted"
167− rest
168−
169−rangeToBuilder :: Ord a => (a -> Builder) -> PGRange a -> Builder
170−rangeToBuilder = rangeToBuilderBy compare
171−
172−-- | Generic range to builder for plain values
173−rangeToBuilderBy :: (a -> a -> Ordering) -> (a -> Builder) -> PGRange a -> Builder
174−rangeToBuilderBy cmp f x =
175− if isEmptyBy cmp x
176− then byteString "'empty'"
177− else let (PGRange a b) = x
178− in buildLB a <> buildUB b
179− where
180− buildLB NegInfinity = byteString "'[,"
181− buildLB (Inclusive v) = byteString "'[\"" <> f v <> byteString "\","
182− buildLB (Exclusive v) = byteString "'(\"" <> f v <> byteString "\","
183− buildLB PosInfinity = error "impossible in rangeToBuilder"
184−
185− buildUB NegInfinity = error "impossible in rangeToBuilder"
186− buildUB (Inclusive v) = char8 '"' <> f v <> byteString "\"]'"
187− buildUB (Exclusive v) = char8 '"' <> f v <> byteString "\")'"
188− buildUB PosInfinity = byteString "]'"
189−{-# INLINE rangeToBuilder #-}
190−
191−
192−instance (FromField a, Typeable a) => FromField (PGRange a) where
193− fromField = fromFieldRange fromField
194−
195−fromFieldRange :: Typeable a => FieldParser a -> FieldParser (PGRange a)
196−fromFieldRange fromField' f mdat = do
197− info <- typeInfo f
198− case info of
199− Range{} ->
200− let f' = f { typeOid = typoid (rngsubtype info) }
201− in case mdat of
202− Nothing -> returnError UnexpectedNull f ""
203− Just "empty" -> pure $ empty
204− Just bs ->
205− let parseIt NegInfinity = pure NegInfinity
206− parseIt (Inclusive v) = Inclusive <$> fromField' f' (Just v)
207− parseIt (Exclusive v) = Exclusive <$> fromField' f' (Just v)
208− parseIt PosInfinity = pure PosInfinity
209− in case parseOnly pgrange bs of
210− Left e -> returnError ConversionFailed f e
211− Right (lb,ub) -> PGRange <$> parseIt lb <*> parseIt ub
212− _ -> returnError Incompatible f ""
213−
214−
215−instance ToField (PGRange Int8) where
216− toField = Plain . rangeToBuilder int8Dec
217− {-# INLINE toField #-}
218−
219−instance ToField (PGRange Int16) where
220− toField = Plain . rangeToBuilder int16Dec
221− {-# INLINE toField #-}
222−
223−instance ToField (PGRange Int32) where
224− toField = Plain . rangeToBuilder int32Dec
225− {-# INLINE toField #-}
226−
227−instance ToField (PGRange Int) where
228− toField = Plain . rangeToBuilder intDec
229− {-# INLINE toField #-}
230−
231−instance ToField (PGRange Int64) where
232− toField = Plain . rangeToBuilder int64Dec
233− {-# INLINE toField #-}
234−
235−instance ToField (PGRange Integer) where
236− toField = Plain . rangeToBuilder integerDec
237− {-# INLINE toField #-}
238−
239−instance ToField (PGRange Word8) where
240− toField = Plain . rangeToBuilder word8Dec
241− {-# INLINE toField #-}
242−
243−instance ToField (PGRange Word16) where
244− toField = Plain . rangeToBuilder word16Dec
245− {-# INLINE toField #-}
246−
247−instance ToField (PGRange Word32) where
248− toField = Plain . rangeToBuilder word32Dec
249− {-# INLINE toField #-}
250−
251−instance ToField (PGRange Word) where
252− toField = Plain . rangeToBuilder wordDec
253− {-# INLINE toField #-}
254−
255−instance ToField (PGRange Word64) where
256− toField = Plain . rangeToBuilder word64Dec
257− {-# INLINE toField #-}
258−
259−instance ToField (PGRange Float) where
260− toField = Plain . rangeToBuilder floatDec
261− {-# INLINE toField #-}
262−
263−instance ToField (PGRange Double) where
264− toField = Plain . rangeToBuilder doubleDec
265− {-# INLINE toField #-}
266−
267−instance ToField (PGRange Scientific) where
268− toField = Plain . rangeToBuilder f
269− where
270− f = lazyByteString . LT.encodeUtf8 . LT.toLazyText . scientificBuilder
271− {-# INLINE toField #-}
272−
273−instance ToField (PGRange UTCTime) where
274− toField = Plain . rangeToBuilder utcTimeToBuilder
275− {-# INLINE toField #-}
276−
277−instance ToField (PGRange ZonedTime) where
278− toField = Plain . rangeToBuilderBy cmpZonedTime zonedTimeToBuilder
279− {-# INLINE toField #-}
280−
281−cmpZonedTime :: ZonedTime -> ZonedTime -> Ordering
282−cmpZonedTime = compare `on` zonedTimeToUTC -- FIXME: optimize
283−
284−instance ToField (PGRange LocalTime) where
285− toField = Plain . rangeToBuilder localTimeToBuilder
286− {-# INLINE toField #-}
287−
288−instance ToField (PGRange Day) where
289− toField = Plain . rangeToBuilder dayToBuilder
290− {-# INLINE toField #-}
291−
292−instance ToField (PGRange TimeOfDay) where
293− toField = Plain . rangeToBuilder timeOfDayToBuilder
294− {-# INLINE toField #-}
295−
296−instance ToField (PGRange UTCTimestamp) where
297− toField = Plain . rangeToBuilder utcTimestampToBuilder
298− {-# INLINE toField #-}
299−
300−instance ToField (PGRange ZonedTimestamp) where
301− toField = Plain . rangeToBuilderBy cmpZonedTimestamp zonedTimestampToBuilder
302− {-# INLINE toField #-}
303−
304−cmpZonedTimestamp :: ZonedTimestamp -> ZonedTimestamp -> Ordering
305−cmpZonedTimestamp = compare `on` (zonedTimeToUTC <$>)
306−
307−instance ToField (PGRange LocalTimestamp) where
308− toField = Plain . rangeToBuilder localTimestampToBuilder
309− {-# INLINE toField #-}
310−
311−instance ToField (PGRange Date) where
312− toField = Plain . rangeToBuilder dateToBuilder
313− {-# INLINE toField #-}
314−
315−instance ToField (PGRange NominalDiffTime) where
316− toField = Plain . rangeToBuilder nominalDiffTimeToBuilder
317− {-# INLINE toField #-}
src/Database/PostgreSQL/Simple/Result.hsnew file+252
@@ -0,0 +1,252 @@
1+{-# LANGUAGE CPP, DeriveDataTypeable, DeriveFunctor, FlexibleInstances #-}
2+{-# LANGUAGE PatternGuards, ScopedTypeVariables #-}
3+------------------------------------------------------------------------------
4+-- |
5+-- Module: Database.PostgreSQL.Simple.QueryResults
6+-- Copyright: (c) 2011 MailRank, Inc.
7+-- (c) 2011 Leon P Smith
8+-- License: BSD3
9+-- Maintainer: Leon P Smith <leon@melding-monads.com>
10+-- Stability: experimental
11+-- Portability: portable
12+--
13+-- The 'Result' typeclass, for converting a single value in a row
14+-- returned by a SQL query into a more useful Haskell representation.
15+--
16+-- A Haskell numeric type is considered to be compatible with all
17+-- MySQL numeric types that are less accurate than it. For instance,
18+-- the Haskell 'Double' type is compatible with the MySQL 'Long' type
19+-- because it can represent a 'Long' exactly. On the other hand, since
20+-- a 'Double' might lose precision if representing a 'LongLong', the
21+-- two are /not/ considered compatible.
22+--
23+------------------------------------------------------------------------------
24+
25+module Database.PostgreSQL.Simple.Result
26+ (
27+ Result(..)
28+ , ResultError(..)
29+ , Status(..)
30+ ) where
31+
32+#include "MachDeps.h"
33+
34+import Control.Applicative (Applicative, (<$>), (<*>), (<*), pure)
35+import Control.Exception (Exception, throw)
36+import Data.Attoparsec.Char8 hiding (Result)
37+import Data.Bits ((.&.), (.|.), shiftL)
38+import Data.ByteString (ByteString)
39+import qualified Data.ByteString.Char8 as B
40+import Data.Int (Int16, Int32, Int64)
41+import Data.List (foldl')
42+import Data.Ratio (Ratio)
43+import Data.Time.Calendar (Day, fromGregorian)
44+import Data.Time.Clock (UTCTime)
45+import Data.Time.Format (parseTime)
46+import Data.Time.LocalTime (TimeOfDay, makeTimeOfDayValid)
47+import Data.Typeable (TypeRep, Typeable, typeOf)
48+import Data.Word ({- Word, Word8, Word16, Word32, -} Word64)
49+-- import Database.MySQL.Base.Types (Field(..), Type(..))
50+import Database.PostgreSQL.Simple.Implementation
51+import Database.PostgreSQL.Simple.Field (Field(..), RawResult(..))
52+import Database.PostgreSQL.Simple.BuiltinTypes
53+import qualified Database.PostgreSQL.LibPQ as PQ
54+import System.Locale (defaultTimeLocale)
55+import qualified Data.ByteString as SB
56+import qualified Data.ByteString.Char8 as B8
57+import qualified Data.ByteString.Lazy as LB
58+import qualified Data.Text as ST
59+import qualified Data.Text.Encoding as ST
60+import qualified Data.Text.Lazy as LT
61+
62+-- | Exception thrown if conversion from a SQL value to a Haskell
63+-- value fails.
64+data ResultError = Incompatible { errSQLType :: String
65+ , errHaskellType :: String
66+ , errMessage :: String }
67+ -- ^ The SQL and Haskell types are not compatible.
68+ | UnexpectedNull { errSQLType :: String
69+ , errHaskellType :: String
70+ , errMessage :: String }
71+ -- ^ A SQL @NULL@ was encountered when the Haskell
72+ -- type did not permit it.
73+ | ConversionFailed { errSQLType :: String
74+ , errHaskellType :: String
75+ , errMessage :: String }
76+ -- ^ The SQL value could not be parsed, or could not
77+ -- be represented as a valid Haskell value, or an
78+ -- unexpected low-level error occurred (e.g. mismatch
79+ -- between metadata and actual data in a row).
80+ deriving (Eq, Show, Typeable)
81+
82+instance Exception ResultError
83+
84+-- | A type that may be converted from a SQL type.
85+class Result a where
86+ convert :: Field -> Maybe ByteString -> Status ResultError a
87+ -- ^ Convert a SQL value to a Haskell value.
88+ --
89+ -- Throws a 'ResultError' if conversion fails.
90+{--
91+instance (Result a) => Result (Maybe a) where
92+ convert _ Nothing = pure Nothing
93+ convert f bs = Just <$> convert f bs
94+
95+instance Result Bool where
96+ convert = atto ok8 ((/=(0::Int)) <$> decimal)
97+
98+instance Result Int8 where
99+ convert = atto ok8 $ signed decimal
100+--}
101+instance Result Int16 where
102+ convert = atto ok16 $ signed decimal
103+
104+instance Result Int32 where
105+ convert = atto ok32 $ signed decimal
106+
107+instance Result Int where
108+ convert = atto okInt $ signed decimal
109+
110+instance Result Int64 where
111+ convert = atto ok64 $ signed decimal
112+
113+instance Result Integer where
114+ convert = atto ok64 $ signed decimal
115+{--
116+instance Result Word8 where
117+ convert = atto ok8 decimal
118+
119+instance Result Word16 where
120+ convert = atto ok16 decimal
121+
122+instance Result Word32 where
123+ convert = atto ok32 decimal
124+
125+instance Result Word where
126+ convert = atto okWord decimal
127+
128+instance Result Word64 where
129+ convert = atto ok64 decimal
130+--}
131+{--
132+instance Result Float where
133+ convert = atto ok (realToFrac <$> double)
134+ where ok = mkCompats [Float,Double,Decimal,NewDecimal,Tiny,Short,Int24]
135+
136+instance Result Double where
137+ convert = atto ok double
138+ where ok = mkCompats [Float,Double,Decimal,NewDecimal,Tiny,Short,Int24,
139+ Long]
140+--}
141+{--
142+instance Result (Ratio Integer) where
143+ convert = atto ok rational
144+ where ok = mkCompats [Float,Double,Decimal,NewDecimal,Tiny,Short,Int24,
145+ Long,LongLong]
146+--}
147+instance Result SB.ByteString where
148+ convert f = doConvert f okText $ pure
149+
150+instance Result PQ.Oid where
151+ convert f dat = PQ.Oid <$> atto (mkCompat Oid) decimal f dat
152+
153+instance Result LB.ByteString where
154+ convert f dat = LB.fromChunks . (:[]) <$> convert f dat
155+
156+
157+instance Result ST.Text where
158+ convert f {- | isText f -} = doConvert f okText $ (Success . ST.decodeUtf8)
159+{- | otherwise = incompatible f (typeOf ST.empty)
160+ "attempt to mix binary and text" -}
161+ -- FIXME: return a "Fail someUnicodeError" instead of throwing it
162+ -- check character encoding
163+
164+instance Result LT.Text where
165+ convert f dat = LT.fromStrict <$> convert f dat
166+
167+instance Result [Char] where
168+ convert f dat = ST.unpack <$> convert f dat
169+
170+instance Result UTCTime where
171+ convert f = doConvert f ok $ \bs ->
172+ case parseTime defaultTimeLocale "%F %T%Q%z" (B8.unpack bs ++ "00") of
173+ Just t -> Success t
174+ Nothing -> conversionFailed f "UTCTime" "could not parse"
175+ where ok = mkCompats [TimestampWithTimeZone]
176+{--
177+instance Result Day where
178+ convert f = flip (atto ok) f $ case fieldType f of
179+ Year -> year
180+ _ -> date
181+ where ok = mkCompats [Year,Date,NewDate]
182+ year = fromGregorian <$> decimal <*> pure 1 <*> pure 1
183+ date = fromGregorian <$> (decimal <* char '-')
184+ <*> (decimal <* char '-')
185+ <*> decimal
186+
187+instance Result TimeOfDay where
188+ convert f = flip (atto ok) f $ do
189+ hours <- decimal <* char ':'
190+ mins <- decimal <* char ':'
191+ secs <- decimal :: Parser Int
192+ case makeTimeOfDayValid hours mins (fromIntegral secs) of
193+ Just t -> return t
194+ _ -> conversionFailed f "TimeOfDay" "could not parse"
195+ where ok = mkCompats [Time]
196+
197+isText :: Field -> Bool
198+isText f = fieldCharSet f /= 63
199+--}
200+newtype Compat = Compat Word64
201+
202+mkCompats :: [BuiltinType] -> Compat
203+mkCompats = foldl' f (Compat 0) . map mkCompat
204+ where f (Compat a) (Compat b) = Compat (a .|. b)
205+
206+mkCompat :: BuiltinType -> Compat
207+mkCompat = Compat . shiftL 1 . fromEnum
208+
209+compat :: Compat -> Compat -> Bool
210+compat (Compat a) (Compat b) = a .&. b /= 0
211+{--
212+okText, ok8, ok16, ok32, ok64, okWord :: Compat
213+--}
214+okText = mkCompats [Name,Text,Char,Bpchar,Varchar]
215+{--}
216+--ok8 = mkCompats [Tiny]
217+ok16 = mkCompats [Int2]
218+ok32 = mkCompats [Int2,Int4]
219+ok64 = mkCompats [Int2,Int4,Int8]
220+#if WORD_SIZE_IN_BITS < 64
221+okInt = ok32
222+#else
223+okInt = ok64
224+#endif
225+--}
226+doConvert :: forall a . (Typeable a)
227+ => Field -> Compat -> (ByteString -> Status ResultError a)
228+ -> Maybe ByteString -> Status ResultError a
229+doConvert f types cvt (Just bs)
230+ | Just typ <- oid2builtin (typeOid f)
231+ , mkCompat typ `compat` types = cvt bs
232+ | otherwise = incompatible f (typeOf (undefined::a)) "types incompatible"
233+doConvert f _ cvt _ = Fail $ UnexpectedNull (B.unpack (typename f))
234+ (show (typeOf (undefined::a))) ""
235+
236+incompatible :: Field -> TypeRep -> String -> Status ResultError a
237+incompatible f r = Fail . Incompatible (B.unpack (typename f)) (show r)
238+
239+conversionFailed :: Field -> String -> String -> Status ResultError a
240+conversionFailed f s = Fail . ConversionFailed (B.unpack (typename f)) s
241+
242+atto :: (Typeable a) => Compat -> Parser a -> Field -> Maybe ByteString -> Status ResultError a
243+atto types p0 f dat = doConvert f types (go undefined p0) dat
244+ where
245+ go :: (Typeable a) => a -> Parser a -> ByteString -> Status ResultError a
246+ go dummy p s =
247+ case parseOnly p s of
248+ Left err -> conversionFailed f (show (typeOf dummy)) err
249+ Right v -> Success v
250+
251+instance Result RawResult where
252+ convert field rawData = Success (RawResult field rawData)
src/Database/PostgreSQL/Simple/SqlQQ.hsdeleted−90
@@ -1,90 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−#if __GLASGOW_HASKELL__ >= 800
3−{-# LANGUAGE TemplateHaskellQuotes #-}
4−#else
5−{-# LANGUAGE TemplateHaskell #-}
6−#endif
7−------------------------------------------------------------------------------
8−-- |
9−-- Module: Database.PostgreSQL.Simple.SqlQQ
10−-- Copyright: (c) 2011-2012 Leon P Smith
11−-- License: BSD3
12−-- Maintainer: Leon P Smith <leon@melding-monads.com>
13−-- Stability: experimental
14−--
15−------------------------------------------------------------------------------
16−
17−module Database.PostgreSQL.Simple.SqlQQ (sql) where
18−import Database.PostgreSQL.Simple.Types (Query)
19−import Language.Haskell.TH
20−import Language.Haskell.TH.Quote
21−import Data.Char
22−import Data.String
23−
24−-- | 'sql' is a quasiquoter that eases the syntactic burden
25−-- of writing big sql statements in Haskell source code. For example:
26−--
27−-- > {-# LANGUAGE QuasiQuotes #-}
28−-- >
29−-- > query conn [sql| SELECT column_a, column_b
30−-- > FROM table1 NATURAL JOIN table2
31−-- > WHERE ? <= time AND time < ?
32−-- > AND name LIKE ?
33−-- > ORDER BY size DESC
34−-- > LIMIT 100 |]
35−-- > (beginTime,endTime,string)
36−--
37−-- This quasiquoter returns a literal string expression of type 'Query',
38−-- and attempts to minimize whitespace; otherwise the above query would
39−-- consist of approximately half whitespace when sent to the database
40−-- backend. It also recognizes and strips out standard sql comments "--".
41−--
42−-- The implementation of the whitespace reducer is currently incomplete.
43−-- Thus it can mess up your syntax in cases where whitespace should be
44−-- preserved as-is. It does preserve whitespace inside standard SQL string
45−-- literals. But it can get confused by the non-standard PostgreSQL string
46−-- literal syntax (which is the default setting in PostgreSQL 8 and below),
47−-- the extended escape string syntax, quoted identifiers, and other similar
48−-- constructs.
49−--
50−-- Of course, this caveat only applies to text written inside the SQL
51−-- quasiquoter; whitespace reduction is a compile-time computation and
52−-- thus will not touch the @string@ parameter above, which is a run-time
53−-- value.
54−--
55−-- Also note that this will not work if the substring @|]@ is contained
56−-- in the query.
57−
58−sql :: QuasiQuoter
59−sql = QuasiQuoter
60− { quotePat = error "Database.PostgreSQL.Simple.SqlQQ.sql: quasiquoter used in pattern context"
61− , quoteType = error "Database.PostgreSQL.Simple.SqlQQ.sql: quasiquoter used in type context"
62− , quoteExp = sqlExp
63− , quoteDec = error "Database.PostgreSQL.Simple.SqlQQ.sql: quasiquoter used in declaration context"
64− }
65−
66−sqlExp :: String -> Q Exp
67−sqlExp = appE [| fromString :: String -> Query |] . stringE . minimizeSpace
68−
69−minimizeSpace :: String -> String
70−minimizeSpace = drop 1 . reduceSpace
71− where
72− needsReduced [] = False
73− needsReduced ('-':'-':_) = True
74− needsReduced (x:_) = isSpace x
75−
76− reduceSpace xs =
77− case dropWhile isSpace xs of
78− [] -> []
79− ('-':'-':ys) -> reduceSpace (dropWhile (/= '\n') ys)
80− ys -> ' ' : insql ys
81−
82− insql ('\'':xs) = '\'' : instring xs
83− insql xs | needsReduced xs = reduceSpace xs
84− insql (x:xs) = x : insql xs
85− insql [] = []
86−
87− instring ('\'':'\'':xs) = '\'':'\'': instring xs
88− instring ('\'':xs) = '\'': insql xs
89− instring (x:xs) = x : instring xs
90− instring [] = error "Database.PostgreSQL.Simple.SqlQQ.sql: string literal not terminated"
src/Database/PostgreSQL/Simple/Time.hsdeleted−246
@@ -1,246 +0,0 @@
1−{- |
2−Module: Database.PostgreSQL.Simple.Time
3−Copyright: (c) 2012-2015 Leon P Smith
4−License: BSD3
5−Maintainer: Leon P Smith <leon@melding-monads.com>
6−Stability: experimental
7−
8−This module provides time types that supports positive and negative
9−infinity, as well as some functions for converting to and from strings.
10−
11−Also, this module also contains commentary regarding postgresql's timestamp
12−types, civil timekeeping in general, and how it relates to
13−postgresql-simple. You can read more about PostgreSQL's date and time types
14−at <https://www.postgresql.org/docs/9.5/static/datatype-datetime.html>,
15−and the IANA time zone database at <https://en.wikipedia.org/wiki/Tz_database>.
16−
17−Stack Overflow also has some excellent commentary on time, if it is a
18−wiki page or a highly upvoted question and answer. If the answer regarding
19−time has not received about a hundred upvotes at least, then the answer is
20−almost invariably completely and painfully wrong, even if it's the chosen
21−answer or the most highly upvoted answer to a question.
22−
23−PostgreSQL's @timestamp with time zone@ (hereafter, @timestamptz@) can be
24−converted to Haskell's 'Data.Time.UTCTime' and 'Data.Time.ZonedTime' types,
25−because values of these types represent a self-contained, unambiguous point
26−in time. PostgreSQL's @timestamp without time zone@ (hereafter, @timestamp@)
27−can be converted to Haskell's 'Data.Time.LocalTime', because values of these
28−types are ambiguous by themselves, and require context to disambiguate.
29−
30−While this behavior may be superficially counterintuitive because the
31−names might suggest otherwise, this behavior is correct. In fact,
32−the \"timezone\" language in both the postgresql and haskell types would
33−be better read as \"offset (from UTC)\", thus we have postgresql's
34−\"timestamp with offset\" corresponding to Haskell's \"time with the
35−offset \'zero\'\" and Haskell's \"time with an offset (that might be
36−nonzero)\". Similarly, postgresql's \"timestamp without an offset\"
37−corresponds to Haskell's \"local time (without an offset)\".
38−
39−It's important to distinguish between an offset, a standard time, and
40−a time zone. An offset is simply a difference of a local time from UTC,
41−such as @+00@, @-05@, or @+05:30@. A standard time specifies an offset
42−(which may vary throughout the year, due to daylight savings) that a
43−region follows, such as Universal Coordinated Time (UTC), Eastern Standard
44−Time\/Eastern Daylight Time (EST\/EDT), or India Standard Time (IST).
45−And a time zone, much like a standard time, is a function from
46−timestamps to offsets.
47−
48−A time zone is different from a standard time because different regions
49−inside a standard time can be governed by different civil authorities with
50−different laws and thus have different histories of civil time. An IANA
51−time zone is any region of the world that has had the same history of
52−civil time since @1970-01-01 00:00+00@.
53−
54−For example, as of today, both @America\/New_York@ and
55−@America\/Indiana\/Indianapolis@ are on the EST\/EDT time standard, but
56−Indiana used to be on Central Standard Time until 1942, and did not observe
57−daylight savings time (EST only) until 2006. Thus, the choice between
58−these two time zones still matters if you are dealing with timestamps
59−prior to 2006, and could become relevant again if (most of) Indiana
60−moves back to Central Time. (Of course, if the Central to Eastern switch
61−was the only difference, then these two time zones would be the same in
62−IANA's eyes, due to their cutoff date of 1970-01-01.)
63−
64−Getting back to practicalities, PostgreSQL's @timestamptz@ type does not
65−actually store an offset; rather, it uses the offset provided to calculate
66−UTC, and stores the timestamp as UTC. If an offset is not provided, the
67−given timestamp is assumed to be a local time for whatever the @timezone@
68−variable is set to, and the IANA TZ database is consulted to calculate an
69−offset from UTC for the time in question.
70−
71−Note that while most (local timestamp, time zone) pairs correspond to exactly
72−one UTC timestamp, some correspond to two UTC timestamps, while others
73−correspond to none at all. The ambiguous case occurs when the civil time
74−is rolled back, making a calendar day longer than 24 hours. In this case,
75−PostgreSQL silently chooses the second, later possibility. The inconsistent
76−case occurs when the civil time is moved forward, making a calendar day less
77−than 24 hours. In this case, PostgreSQL silently assumes the local time
78−was read off a clock that had not been moved forward at the prescribed time,
79−and moves the clock forward for you. Thus, converting from local time
80−to UTC need not be monotonic, if these inconsistent cases are allowed.
81−
82−When retrieving a @timestamptz@, the backend looks at the @time zone@
83−connection variable and then consults the IANA TZ database to calculate
84−an offset for the timestamp in the given time zone.
85−
86−Note that while some of the information contained in the IANA TZ database
87−is a bit of a standardized fiction, the conversion from UTC time to a
88−(local time, offset) pair in a particular time zone is always unambiguous,
89−and the result can always be unambiguously converted back to UTC. Thus,
90−postgresql-simple can interpret such a result as a 'Data.Time.ZonedTime',
91−or use the offset to convert back to 'Data.Time.UTCTime'.
92−
93−By contrast, the @timestamp@ type ignores any offsets provided to it,
94−and never sends back an offset. Thus, postgresql-simple equates this
95−with 'Data.Time.LocalTime', which has no concept of an offset. One can
96−convert between @timestamptz@ and @timestamp@ using the @AT TIME ZONE@
97−operator, whose semantics also demonstrates that @timestamptz@ is
98−'Data.Time.UTCTime' whereas @timestamp@ is 'Data.Time.LocalTime'.
99−
100−PostgreSQL's @timezone@ is a per-connection variable that by default is
101−initialized to @\'localtime\'@, which normally corresponds to the server's
102−time zone. However, this default can be modified on the server side for an
103−entire cluster, or on a per-user or per-database basis. Moreover, a client
104−can modify their instance of the variable at any time, and can apply that
105−change to the remaining duration of the connection, the current transaction,
106−or the execution context of a server-side function. In addition, upon
107−connection initialization, the libpq client checks for the existence of
108−the @PGTZ@ environment variable, and if it exists, modifies @timezone@
109−accordingly.
110−
111−With a few caveats, postgresql-simple is designed so that you can both send
112−and receive timestamps with the server and get a correct result, no matter
113−what the @timezone@ setting is. But it is important to understand the caveats:
114−
115−1. The correctness of server-side computations can depend on the @timezone@
116− setting. Examples include adding an @interval@ to a @timestamptz@, or
117− type casting between @timestamp@ and @timestamptz@, or applying
118− the @DATE@ function to a @timestamptz@.
119−
120−2. The (localtime, offset) pair contained in a 'Data.Time.ZonedTime' result
121− will depend on the @timezone@ setting, although the result will always
122− represent the same instant in time regardless of the time zone.
123−
124−3. Sending a 'Data.Time.LocalTime' and interpreting it as a @timestamptz@
125− can be useful, as it will be converted to UTC via the tz database,
126− but correctness will depend on the @timezone@ setting. You may prefer
127− to use an explicit @AT TIME ZONE@ conversion instead, which would avoid
128− this contextual dependence.
129−
130−Furthermore, although these following points don't involve the @timezone@
131−setting, they are related to the last point above:
132−
133−1. Sending a 'Data.Time.UTCTime' and interpreting it as a @timestamp@ can
134− be useful. In practice, the most common context used to disambiguate
135− @timestamp@ is that it represents UTC, and this coding technique will
136− work as expected in this situation.
137−
138−2. Sending a 'Data.Time.ZonedTime' and interpreting it as a @timestamp@ is
139− almost always the wrong thing to do, as the offset will be ignored and
140− discarded. This is likely to lead to inconsistencies in the database,
141− and may lead to partial data loss.
142−
143−When dealing with local timestamps that refer to the future, it is often
144−useful to store it as a local time in a @timestamp@ column and store the
145−time zone in a second column. One reason to do this is so that you can
146−convert to UTC on the fly as needed, and be protected against future changes
147−to the TZ database due to changes in local time standards. In any case,
148−'Data.Time.ZonedTime' is not suitable for this application, because despite
149−its name, it represents an offset and not a time zone. Time zones can change;
150−offsets do not. In reality, we can't convert a local timestamp that occurs
151−sufficiently far in the future to UTC, because we don't know how to do it yet.
152−
153−There are a few limitations and caveats that one might need to be aware
154−of with the current implementation when dealing with older timestamps:
155−
156−For sufficiently old timestamps in almost all time zones, the IANA TZ
157−database specifies offsets from UTC that is not an integral number of
158−minutes. This corresponds to local mean time; that is, astronomical
159−time in the city that defines the time zone. Different time zones moved
160−away from local mean time to a standard time at different points in
161−history, so \"sufficiently old\" depends on the time zone in question.
162−
163−Thus, when retrieving a @timestamptz@ postgresql will in some cases
164−provide seconds in the offset. For example:
165−
166−@
167−$ psql
168−psql (9.4.5)
169−Type \"help\" for help.
170−
171−lpsmith=> SET timezone TO \'America/New_York\';
172−SET
173−lpsmith=> VALUES (\'1883-11-18 16:59:59+00\'::timestamptz),
174− (\'1883-11-18 17:00:00+00\'::timestamptz);
175− column1
176−------------------------------
177− 1883-11-18 12:03:57-04:56:02
178− 1883-11-18 12:00:00-05
179−(2 rows)
180−@
181−
182−Both of these timestamps can be parsed as a 'Data.Time.UTCTime' type,
183−however 'Data.Time.ZonedTime' will fail on the former timestamp.
184−Because 'Data.Time.ZonedTime' assumes that offsets are an integer number
185−of minutes, there isn't an particularly good solution here.
186−
187−PostgreSQL, like most software, uses the proleptic Gregorian calendar
188−for its date calculations, extending the Gregorian calendar backwards
189−in time before its introduction and pretending that the Julian calendar
190−does not exist. For most purposes, the adoption of the Gregorian calendar
191−ranges from @1582-10-15@ to @1923-03-01@, depending on location and
192−sometimes even political allegiances within a single location.
193−
194−Timestamps BCE are not supported. For example, PostgreSQL
195−will emit \"@0045-01-01 BC@\" for the first proleptic Gregorian day of
196−the year the Roman Empire adopted the Julian Calendar, but
197−postgresql-simple does not (yet?) have the ability to either parse or
198−generate this syntax. Unfortunately this syntax isn't convenient to
199−print or especially parse.
200−
201−Also, postgresql itself cannot parse or print dates before @4714-11-24 BC@,
202−which is the Julian date on the proleptic Gregorian Calendar. Although
203−postgresql's timestamp types are perfectly capable of representing timestamps
204−nearly 300,000 years in the past, using this would require postgresql-simple
205−and other client programs to support binary parameters and results.
206−
207−Dealing with years BCE is also complicated slightly by the fact that
208−Haskell's time library has a year \"0000\", which is a convention often
209−used by astronomers, while postgresql adopts the more historically
210−accurate convention that there is no year zero, but rather \"1 BCE\"
211−was immediately followed by \"1 CE\".
212−
213−-}
214−
215−module Database.PostgreSQL.Simple.Time
216− ( Unbounded(..)
217− , Date
218− , UTCTimestamp
219− , ZonedTimestamp
220− , LocalTimestamp
221− , parseDay
222− , parseUTCTime
223− , parseZonedTime
224− , parseLocalTime
225− , parseTimeOfDay
226− , parseDate
227− , parseUTCTimestamp
228− , parseZonedTimestamp
229− , parseLocalTimestamp
230− , parseCalendarDiffTime
231− , dayToBuilder
232− , utcTimeToBuilder
233− , zonedTimeToBuilder
234− , localTimeToBuilder
235− , timeOfDayToBuilder
236− , timeZoneToBuilder
237− , dateToBuilder
238− , utcTimestampToBuilder
239− , zonedTimestampToBuilder
240− , localTimestampToBuilder
241− , unboundedToBuilder
242− , nominalDiffTimeToBuilder
243− , calendarDiffTimeToBuilder
244− ) where
245−
246−import Database.PostgreSQL.Simple.Time.Implementation
src/Database/PostgreSQL/Simple/Time/Implementation.hsdeleted−176
@@ -1,176 +0,0 @@
1−------------------------------------------------------------------------------
2−-- |
3−-- Module: Database.PostgreSQL.Simple.Time.Implementation
4−-- Copyright: (c) 2012-2015 Leon P Smith
5−-- License: BSD3
6−-- Maintainer: Leon P Smith <leon@melding-monads.com>
7−-- Stability: experimental
8−--
9−------------------------------------------------------------------------------
10−
11−{-# LANGUAGE DeriveDataTypeable, DeriveFunctor #-}
12−
13−module Database.PostgreSQL.Simple.Time.Implementation where
14−
15−import Prelude hiding (take)
16−import Data.ByteString.Builder(Builder, byteString)
17−import Data.ByteString.Builder.Prim(primBounded)
18−import Control.Arrow((***))
19−import Control.Applicative
20−import qualified Data.ByteString as B
21−import Data.Time.Compat (LocalTime, UTCTime, ZonedTime, Day, TimeOfDay, TimeZone, NominalDiffTime, utc)
22−import Data.Time.LocalTime.Compat (CalendarDiffTime)
23−import Data.Typeable
24−import Data.Maybe (fromMaybe)
25−import qualified Data.Attoparsec.ByteString.Char8 as A
26−import Database.PostgreSQL.Simple.Compat ((<>))
27−import qualified Database.PostgreSQL.Simple.Time.Internal.Parser as TP
28−import qualified Database.PostgreSQL.Simple.Time.Internal.Printer as TPP
29−
30−data Unbounded a
31− = NegInfinity
32− | Finite !a
33− | PosInfinity
34− deriving (Eq, Ord, Typeable, Functor)
35−
36−instance Show a => Show (Unbounded a) where
37− showsPrec prec x rest
38− = case x of
39− NegInfinity -> "-infinity" <> rest
40− Finite time -> showsPrec prec time rest
41− PosInfinity -> "infinity" <> rest
42−
43−instance Read a => Read (Unbounded a) where
44− readsPrec prec = readParen False $ \str -> case str of
45− ('-':'i':'n':'f':'i':'n':'i':'t':'y':xs) -> [(NegInfinity,xs)]
46− ( 'i':'n':'f':'i':'n':'i':'t':'y':xs) -> [(PosInfinity,xs)]
47− xs -> map (Finite *** id) (readsPrec prec xs)
48−
49−type LocalTimestamp = Unbounded LocalTime
50−type UTCTimestamp = Unbounded UTCTime
51−type ZonedTimestamp = Unbounded ZonedTime
52−type Date = Unbounded Day
53−
54−parseUTCTime :: B.ByteString -> Either String UTCTime
55−parseUTCTime = A.parseOnly (getUTCTime <* A.endOfInput)
56−
57−parseZonedTime :: B.ByteString -> Either String ZonedTime
58−parseZonedTime = A.parseOnly (getZonedTime <* A.endOfInput)
59−
60−parseLocalTime :: B.ByteString -> Either String LocalTime
61−parseLocalTime = A.parseOnly (getLocalTime <* A.endOfInput)
62−
63−parseDay :: B.ByteString -> Either String Day
64−parseDay = A.parseOnly (getDay <* A.endOfInput)
65−
66−parseTimeOfDay :: B.ByteString -> Either String TimeOfDay
67−parseTimeOfDay = A.parseOnly (getTimeOfDay <* A.endOfInput)
68−
69−parseUTCTimestamp :: B.ByteString -> Either String UTCTimestamp
70−parseUTCTimestamp = A.parseOnly (getUTCTimestamp <* A.endOfInput)
71−
72−parseZonedTimestamp :: B.ByteString -> Either String ZonedTimestamp
73−parseZonedTimestamp = A.parseOnly (getZonedTimestamp <* A.endOfInput)
74−
75−parseLocalTimestamp :: B.ByteString -> Either String LocalTimestamp
76−parseLocalTimestamp = A.parseOnly (getLocalTimestamp <* A.endOfInput)
77−
78−parseDate :: B.ByteString -> Either String Date
79−parseDate = A.parseOnly (getDate <* A.endOfInput)
80−
81−parseCalendarDiffTime :: B.ByteString -> Either String CalendarDiffTime
82−parseCalendarDiffTime = A.parseOnly (getCalendarDiffTime <* A.endOfInput)
83−
84−getUnbounded :: A.Parser a -> A.Parser (Unbounded a)
85−getUnbounded getFinite
86− = (pure NegInfinity <* A.string "-infinity")
87− <|> (pure PosInfinity <* A.string "infinity")
88− <|> (Finite <$> getFinite)
89−
90−getDay :: A.Parser Day
91−getDay = TP.day
92−
93−getDate :: A.Parser Date
94−getDate = getUnbounded getDay
95−
96−getTimeOfDay :: A.Parser TimeOfDay
97−getTimeOfDay = TP.timeOfDay
98−
99−getLocalTime :: A.Parser LocalTime
100−getLocalTime = TP.localTime
101−
102−getLocalTimestamp :: A.Parser LocalTimestamp
103−getLocalTimestamp = getUnbounded getLocalTime
104−
105−getTimeZone :: A.Parser TimeZone
106−getTimeZone = fromMaybe utc <$> TP.timeZone
107−
108−type TimeZoneHMS = (Int,Int,Int)
109−
110−getTimeZoneHMS :: A.Parser TimeZoneHMS
111−getTimeZoneHMS = munge <$> TP.timeZoneHMS
112− where
113− munge Nothing = (0,0,0)
114− munge (Just (TP.UTCOffsetHMS h m s)) = (h,m,s)
115−
116−localToUTCTimeOfDayHMS :: TimeZoneHMS -> TimeOfDay -> (Integer, TimeOfDay)
117−localToUTCTimeOfDayHMS (dh, dm, ds) tod =
118− TP.localToUTCTimeOfDayHMS (TP.UTCOffsetHMS dh dm ds) tod
119−
120−getZonedTime :: A.Parser ZonedTime
121−getZonedTime = TP.zonedTime
122−
123−getZonedTimestamp :: A.Parser ZonedTimestamp
124−getZonedTimestamp = getUnbounded getZonedTime
125−
126−getUTCTime :: A.Parser UTCTime
127−getUTCTime = TP.utcTime
128−
129−getUTCTimestamp :: A.Parser UTCTimestamp
130−getUTCTimestamp = getUnbounded getUTCTime
131−
132−getCalendarDiffTime :: A.Parser CalendarDiffTime
133−getCalendarDiffTime = TP.calendarDiffTime
134−
135−dayToBuilder :: Day -> Builder
136−dayToBuilder = primBounded TPP.day
137−
138−timeOfDayToBuilder :: TimeOfDay -> Builder
139−timeOfDayToBuilder = primBounded TPP.timeOfDay
140−
141−timeZoneToBuilder :: TimeZone -> Builder
142−timeZoneToBuilder = primBounded TPP.timeZone
143−
144−utcTimeToBuilder :: UTCTime -> Builder
145−utcTimeToBuilder = primBounded TPP.utcTime
146−
147−zonedTimeToBuilder :: ZonedTime -> Builder
148−zonedTimeToBuilder = primBounded TPP.zonedTime
149−
150−localTimeToBuilder :: LocalTime -> Builder
151−localTimeToBuilder = primBounded TPP.localTime
152−
153−unboundedToBuilder :: (a -> Builder) -> (Unbounded a -> Builder)
154−unboundedToBuilder finiteToBuilder unbounded
155− = case unbounded of
156− NegInfinity -> byteString "-infinity"
157− Finite a -> finiteToBuilder a
158− PosInfinity -> byteString "infinity"
159−
160−utcTimestampToBuilder :: UTCTimestamp -> Builder
161−utcTimestampToBuilder = unboundedToBuilder utcTimeToBuilder
162−
163−zonedTimestampToBuilder :: ZonedTimestamp -> Builder
164−zonedTimestampToBuilder = unboundedToBuilder zonedTimeToBuilder
165−
166−localTimestampToBuilder :: LocalTimestamp -> Builder
167−localTimestampToBuilder = unboundedToBuilder localTimeToBuilder
168−
169−dateToBuilder :: Date -> Builder
170−dateToBuilder = unboundedToBuilder dayToBuilder
171−
172−nominalDiffTimeToBuilder :: NominalDiffTime -> Builder
173−nominalDiffTimeToBuilder = TPP.nominalDiffTime
174−
175−calendarDiffTimeToBuilder :: CalendarDiffTime -> Builder
176−calendarDiffTimeToBuilder = TPP.calendarDiffTime
src/Database/PostgreSQL/Simple/Time/Internal.hsdeleted−27
@@ -1,27 +0,0 @@
1−------------------------------------------------------------------------------
2−-- |
3−-- Module: Database.PostgreSQL.Simple.Time.Internal
4−-- Copyright: (c) 2012 Leon P Smith
5−-- License: BSD3
6−-- Maintainer: Leon P Smith <leon@melding-monads.com>
7−-- Stability: experimental
8−--
9−------------------------------------------------------------------------------
10−
11−module Database.PostgreSQL.Simple.Time.Internal
12− ( getDay
13− , getDate
14− , getTimeOfDay
15− , getLocalTime
16− , getLocalTimestamp
17− , getTimeZone
18− , getZonedTime
19− , getZonedTimestamp
20− , getUTCTime
21− , getUTCTimestamp
22− , TimeZoneHMS
23− , getTimeZoneHMS
24− , localToUTCTimeOfDayHMS
25− ) where
26−
27−import Database.PostgreSQL.Simple.Time.Implementation
src/Database/PostgreSQL/Simple/Time/Internal/Parser.hsdeleted−204
@@ -1,204 +0,0 @@
1−{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}
2−
3−-- |
4−-- Module: Database.PostgreSQL.Simple.Time.Internal.Parser
5−-- Copyright: (c) 2012-2015 Leon P Smith
6−-- (c) 2015 Bryan O'Sullivan
7−-- License: BSD3
8−-- Maintainer: Leon P Smith <leon@melding-monads.com>
9−-- Stability: experimental
10−--
11−-- Parsers for parsing dates and times.
12−
13−module Database.PostgreSQL.Simple.Time.Internal.Parser
14− (
15− day
16− , localTime
17− , timeOfDay
18− , timeZone
19− , UTCOffsetHMS(..)
20− , timeZoneHMS
21− , localToUTCTimeOfDayHMS
22− , utcTime
23− , zonedTime
24− , calendarDiffTime
25− ) where
26−
27−import Control.Applicative ((<$>), (<*>), (<*), (*>))
28−import Database.PostgreSQL.Simple.Compat (toPico)
29−import Data.Attoparsec.ByteString.Char8 as A
30−import Data.Bits ((.&.))
31−import Data.ByteString (ByteString)
32−import Data.Char (ord)
33−import Data.Fixed (Pico)
34−import Data.Int (Int64)
35−import Data.Maybe (fromMaybe)
36−import Data.Time.Calendar.Compat (Day, fromGregorianValid, addDays)
37−import Data.Time.Clock.Compat (UTCTime(..))
38−import Data.Time.Format.ISO8601.Compat (iso8601ParseM)
39−import Data.Time.LocalTime.Compat (CalendarDiffTime)
40−import qualified Data.ByteString.Char8 as B8
41−import qualified Data.Time.LocalTime.Compat as Local
42−
43−-- | Parse a date of the form @YYYY-MM-DD@.
44−day :: Parser Day
45−day = do
46− y <- decimal <* char '-'
47− m <- twoDigits <* char '-'
48− d <- twoDigits
49− maybe (fail "invalid date") return (fromGregorianValid y m d)
50−
51−-- | Parse a two-digit integer (e.g. day of month, hour).
52−twoDigits :: Parser Int
53−twoDigits = do
54− a <- digit
55− b <- digit
56− let c2d c = ord c .&. 15
57− return $! c2d a * 10 + c2d b
58−
59−-- | Parse a time of the form @HH:MM[:SS[.SSS]]@.
60−timeOfDay :: Parser Local.TimeOfDay
61−timeOfDay = do
62− h <- twoDigits <* char ':'
63− m <- twoDigits
64− mc <- peekChar
65− s <- case mc of
66− Just ':' -> anyChar *> seconds
67− _ -> return 0
68− if h < 24 && m < 60 && s <= 60
69− then return (Local.TimeOfDay h m s)
70− else fail "invalid time"
71−
72−-- | Parse a count of seconds, with the integer part being two digits
73−-- long.
74−seconds :: Parser Pico
75−seconds = do
76− real <- twoDigits
77− mc <- peekChar
78− case mc of
79− Just '.' -> do
80− t <- anyChar *> takeWhile1 isDigit
81− return $! parsePicos (fromIntegral real) t
82− _ -> return $! fromIntegral real
83− where
84− parsePicos :: Int64 -> B8.ByteString -> Pico
85− parsePicos a0 t = toPico (fromIntegral (t' * 10^n))
86− where n = max 0 (12 - B8.length t)
87− t' = B8.foldl' (\a c -> 10 * a + fromIntegral (ord c .&. 15)) a0
88− (B8.take 12 t)
89−
90−-- | Parse a time zone, and return 'Nothing' if the offset from UTC is
91−-- zero. (This makes some speedups possible.)
92−timeZone :: Parser (Maybe Local.TimeZone)
93−timeZone = do
94− ch <- satisfy $ \c -> c == '+' || c == '-' || c == 'Z'
95− if ch == 'Z'
96− then return Nothing
97− else do
98− h <- twoDigits
99− mm <- peekChar
100− m <- case mm of
101− Just ':' -> anyChar *> twoDigits
102− _ -> return 0
103− let off | ch == '-' = negate off0
104− | otherwise = off0
105− off0 = h * 60 + m
106− case undefined of
107− _ | off == 0 ->
108− return Nothing
109− | h > 23 || m > 59 ->
110− fail "invalid time zone offset"
111− | otherwise ->
112− let !tz = Local.minutesToTimeZone off
113− in return (Just tz)
114−
115−data UTCOffsetHMS = UTCOffsetHMS {-# UNPACK #-} !Int {-# UNPACK #-} !Int {-# UNPACK #-} !Int
116−
117−-- | Parse a time zone, and return 'Nothing' if the offset from UTC is
118−-- zero. (This makes some speedups possible.)
119−timeZoneHMS :: Parser (Maybe UTCOffsetHMS)
120−timeZoneHMS = do
121− ch <- satisfy $ \c -> c == '+' || c == '-' || c == 'Z'
122− if ch == 'Z'
123− then return Nothing
124− else do
125− h <- twoDigits
126− m <- maybeTwoDigits
127− s <- maybeTwoDigits
128− case undefined of
129− _ | h == 0 && m == 0 && s == 0 ->
130− return Nothing
131− | h > 23 || m >= 60 || s >= 60 ->
132− fail "invalid time zone offset"
133− | otherwise ->
134− if ch == '+'
135− then let !tz = UTCOffsetHMS h m s
136− in return (Just tz)
137− else let !tz = UTCOffsetHMS (-h) (-m) (-s)
138− in return (Just tz)
139− where
140− maybeTwoDigits = do
141− ch <- peekChar
142− case ch of
143− Just ':' -> anyChar *> twoDigits
144− _ -> return 0
145−
146−localToUTCTimeOfDayHMS :: UTCOffsetHMS -> Local.TimeOfDay -> (Integer, Local.TimeOfDay)
147−localToUTCTimeOfDayHMS (UTCOffsetHMS dh dm ds) (Local.TimeOfDay h m s) =
148− (\ !a !b -> (a,b)) dday (Local.TimeOfDay h'' m'' s'')
149− where
150− s' = s - fromIntegral ds
151− (!s'', m')
152− | s' < 0 = (s' + 60, m - dm - 1)
153− | s' >= 60 = (s' - 60, m - dm + 1)
154− | otherwise = (s' , m - dm )
155− (!m'', h')
156− | m' < 0 = (m' + 60, h - dh - 1)
157− | m' >= 60 = (m' - 60, h - dh + 1)
158− | otherwise = (m' , h - dh )
159− (!h'', dday)
160− | h' < 0 = (h' + 24, -1)
161− | h' >= 24 = (h' - 24, 1)
162− | otherwise = (h' , 0)
163−
164−
165−-- | Parse a date and time, of the form @YYYY-MM-DD HH:MM:SS@.
166−-- The space may be replaced with a @T@. The number of seconds may be
167−-- followed by a fractional component.
168−localTime :: Parser Local.LocalTime
169−localTime = Local.LocalTime <$> day <* daySep <*> timeOfDay
170− where daySep = satisfy (\c -> c == ' ' || c == 'T')
171−
172−-- | Behaves as 'zonedTime', but converts any time zone offset into a
173−-- UTC time.
174−utcTime :: Parser UTCTime
175−utcTime = do
176− (Local.LocalTime d t) <- localTime
177− mtz <- timeZoneHMS
178− case mtz of
179− Nothing -> let !tt = Local.timeOfDayToTime t
180− in return (UTCTime d tt)
181− Just tz -> let !(dd,t') = localToUTCTimeOfDayHMS tz t
182− !d' = addDays dd d
183− !tt = Local.timeOfDayToTime t'
184− in return (UTCTime d' tt)
185−
186−-- | Parse a date with time zone info. Acceptable formats:
187−--
188−-- @YYYY-MM-DD HH:MM:SS Z@
189−--
190−-- The first space may instead be a @T@, and the second space is
191−-- optional. The @Z@ represents UTC. The @Z@ may be replaced with a
192−-- time zone offset of the form @+0000@ or @-08:00@, where the first
193−-- two digits are hours, the @:@ is optional and the second two digits
194−-- (also optional) are minutes.
195−zonedTime :: Parser Local.ZonedTime
196−zonedTime = Local.ZonedTime <$> localTime <*> (fromMaybe utc <$> timeZone)
197−
198−utc :: Local.TimeZone
199−utc = Local.TimeZone 0 False ""
200−
201−calendarDiffTime :: Parser CalendarDiffTime
202−calendarDiffTime = do
203− contents <- takeByteString
204− iso8601ParseM $ B8.unpack contents
src/Database/PostgreSQL/Simple/Time/Internal/Printer.hsdeleted−135
@@ -1,135 +0,0 @@
1−{-# LANGUAGE BangPatterns, ViewPatterns #-}
2−
3−------------------------------------------------------------------------------
4−-- Module: Database.PostgreSQL.Simple.Time.Internal.Printer
5−-- Copyright: (c) 2012-2015 Leon P Smith
6−-- License: BSD3
7−-- Maintainer: Leon P Smith <leon@melding-monads.com>
8−-- Stability: experimental
9−------------------------------------------------------------------------------
10−
11−module Database.PostgreSQL.Simple.Time.Internal.Printer
12− (
13− day
14− , timeOfDay
15− , timeZone
16− , utcTime
17− , localTime
18− , zonedTime
19− , nominalDiffTime
20− , calendarDiffTime
21− ) where
22−
23−import Control.Arrow ((>>>))
24−import Data.ByteString.Builder (Builder, byteString, integerDec)
25−import Data.ByteString.Builder.Prim
26− ( liftFixedToBounded, (>$<), (>*<)
27− , BoundedPrim, primBounded, condB, emptyB, FixedPrim, char8, int32Dec)
28−import Data.Char ( chr )
29−import Data.Int ( Int32, Int64 )
30−import Data.String (fromString)
31−import Data.Time.Compat
32− ( UTCTime(..), ZonedTime(..), LocalTime(..), NominalDiffTime
33− , Day, toGregorian, TimeOfDay(..), timeToTimeOfDay
34− , TimeZone, timeZoneMinutes )
35−import Data.Time.Format.ISO8601.Compat (iso8601Show)
36−import Data.Time.LocalTime.Compat (CalendarDiffTime)
37−import Database.PostgreSQL.Simple.Compat ((<>), fromPico)
38−import Unsafe.Coerce (unsafeCoerce)
39−
40−liftB :: FixedPrim a -> BoundedPrim a
41−liftB = liftFixedToBounded
42−
43−digit :: FixedPrim Int
44−digit = (\x -> chr (x + 48)) >$< char8
45−
46−digits2 :: FixedPrim Int
47−digits2 = (`quotRem` 10) >$< (digit >*< digit)
48−
49−digits3 :: FixedPrim Int
50−digits3 = (`quotRem` 10) >$< (digits2 >*< digit)
51−
52−digits4 :: FixedPrim Int
53−digits4 = (`quotRem` 10) >$< (digits3 >*< digit)
54−
55−frac :: BoundedPrim Int64
56−frac = condB (== 0) emptyB ((,) '.' >$< (liftB char8 >*< trunc12))
57− where
58− trunc12 :: BoundedPrim Int64
59− trunc12 = (`quotRem` 1000000) >$<
60− condB (\(_,y) -> y == 0)
61− (fst >$< trunc6)
62− (liftB digits6 >*< trunc6)
63−
64− digitB = liftB digit
65−
66− digits6 = (fromIntegral >>> (`quotRem` 10)) >$< (digits5 >*< digit)
67− digits5 = (`quotRem` 10) >$< (digits4 >*< digit)
68−
69− trunc6 = (fromIntegral >>> (`quotRem` 100000)) >$< (digitB >*< trunc5)
70− trunc5 = condB (== 0) emptyB ((`quotRem` 10000) >$< (digitB >*< trunc4))
71− trunc4 = condB (== 0) emptyB ((`quotRem` 1000) >$< (digitB >*< trunc3))
72− trunc3 = condB (== 0) emptyB ((`quotRem` 100) >$< (digitB >*< trunc2))
73− trunc2 = condB (== 0) emptyB ((`quotRem` 10) >$< (digitB >*< trunc1))
74− trunc1 = condB (== 0) emptyB digitB
75−
76−
77−year :: BoundedPrim Int32
78−year = condB (>= 10000) int32Dec (checkBCE >$< liftB digits4)
79− where
80− checkBCE :: Int32 -> Int
81− checkBCE y
82− | y > 0 = fromIntegral y
83− | otherwise = error msg
84−
85− msg = "Database.PostgreSQL.Simple.Time.Printer.year: years BCE not supported"
86−
87−day :: BoundedPrim Day
88−day = toYMD >$< (year >*< liftB (char8 >*< digits2 >*< char8 >*< digits2))
89− where
90− toYMD (toGregorian -> (fromIntegral -> !y, !m,!d)) = (y,('-',(m,('-',d))))
91−
92−timeOfDay :: BoundedPrim TimeOfDay
93−timeOfDay = f >$< (hh_mm_ >*< ss)
94− where
95− f (TimeOfDay h m s) = ((h,(':',(m,':'))),s)
96−
97− hh_mm_ = liftB (digits2 >*< char8 >*< digits2 >*< char8)
98−
99− ss = (\s -> fromIntegral (fromPico s) `quotRem` 1000000000000) >$<
100− (liftB (fromIntegral >$< digits2) >*< frac)
101−
102−timeZone :: BoundedPrim TimeZone
103−timeZone = timeZoneMinutes >$< tz
104− where
105− tz = condB (>= 0) ((,) '+' >$< tzh) ((,) '-' . negate >$< tzh)
106−
107− tzh = liftB char8 >*< ((`quotRem` 60) >$< (liftB digits2 >*< tzm))
108−
109− tzm = condB (==0) emptyB ((,) ':' >$< liftB (char8 >*< digits2))
110−
111−utcTime :: BoundedPrim UTCTime
112−utcTime = f >$< (day >*< liftB char8 >*< timeOfDay >*< liftB char8)
113− where f (UTCTime d (timeToTimeOfDay -> tod)) = (d,(' ',(tod,'Z')))
114−
115−localTime :: BoundedPrim LocalTime
116−localTime = f >$< (day >*< liftB char8 >*< timeOfDay)
117− where f (LocalTime d tod) = (d, (' ', tod))
118−
119−zonedTime :: BoundedPrim ZonedTime
120−zonedTime = f >$< (localTime >*< timeZone)
121− where f (ZonedTime lt tz) = (lt, tz)
122−
123−
124−nominalDiffTime :: NominalDiffTime -> Builder
125−nominalDiffTime xy = integerDec x <> primBounded frac (abs (fromIntegral y))
126− where
127− (x,y) = fromPico (unsafeCoerce xy) `quotRem` 1000000000000
128−
129−calendarDiffTime :: CalendarDiffTime -> Builder
130−calendarDiffTime = byteString
131− . fromString
132− -- from the docs: "Beware: fromString truncates multi-byte characters to octets".
133− -- However, I think this is a safe usage, because ISO8601-encoding seems restricted
134− -- to ASCII output.
135− . iso8601Show
src/Database/PostgreSQL/Simple/ToField.hsdeleted−393
@@ -1,393 +0,0 @@
1−{-# LANGUAGE CPP, DeriveDataTypeable, DeriveFunctor #-}
2−{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
3−{-# LANGUAGE PolyKinds #-}
4−
5−------------------------------------------------------------------------------
6−-- |
7−-- Module: Database.PostgreSQL.Simple.ToField
8−-- Copyright: (c) 2011 MailRank, Inc.
9−-- (c) 2011-2012 Leon P Smith
10−-- License: BSD3
11−-- Maintainer: Leon P Smith <leon@melding-monads.com>
12−-- Stability: experimental
13−--
14−-- The 'ToField' typeclass, for rendering a parameter to a SQL query.
15−--
16−------------------------------------------------------------------------------
17−
18−module Database.PostgreSQL.Simple.ToField
19− (
20− Action(..)
21− , ToField(..)
22− , toJSONField
23− , inQuotes
24− ) where
25−
26−import Control.Applicative (Const(Const))
27−import qualified Data.Aeson as JSON
28−import Data.ByteString (ByteString)
29−import Data.ByteString.Builder
30− ( Builder, byteString, char8, stringUtf8
31− , intDec, int8Dec, int16Dec, int32Dec, int64Dec, integerDec
32− , wordDec, word8Dec, word16Dec, word32Dec, word64Dec
33− , floatDec, doubleDec
34− )
35−import Data.Functor.Identity (Identity(Identity))
36−import Data.Int (Int8, Int16, Int32, Int64)
37−import Data.List (intersperse)
38−import Data.Monoid (mappend)
39−import Data.Time.Compat (Day, TimeOfDay, LocalTime, UTCTime, ZonedTime, NominalDiffTime)
40−import Data.Time.LocalTime.Compat (CalendarDiffTime)
41−import Data.Typeable (Typeable)
42−import Data.Word (Word, Word8, Word16, Word32, Word64)
43−import {-# SOURCE #-} Database.PostgreSQL.Simple.ToRow
44−import Database.PostgreSQL.Simple.Types
45−import Database.PostgreSQL.Simple.Compat (toByteString)
46−
47−import qualified Data.ByteString as SB
48−import qualified Data.ByteString.Lazy as LB
49−import Data.CaseInsensitive (CI)
50−import qualified Data.CaseInsensitive as CI
51−import qualified Data.Text as ST
52−import qualified Data.Text.Encoding as ST
53−import qualified Data.Text.Lazy as LT
54−import qualified Data.Text.Lazy.Builder as LT
55−import Data.UUID.Types (UUID)
56−import qualified Data.UUID.Types as UUID
57−import Data.Vector (Vector)
58−import qualified Data.Vector as V
59−import qualified Database.PostgreSQL.LibPQ as PQ
60−import Database.PostgreSQL.Simple.Time
61−import Data.Scientific (Scientific)
62−import Data.Text.Lazy.Builder.Scientific (scientificBuilder)
63−import Foreign.C.Types (CUInt(..))
64−
65−-- | How to render an element when substituting it into a query.
66−data Action =
67− Plain Builder
68− -- ^ Render without escaping or quoting. Use for non-text types
69− -- such as numbers, when you are /certain/ that they will not
70− -- introduce formatting vulnerabilities via use of characters such
71− -- as spaces or \"@'@\".
72− | Escape ByteString
73− -- ^ Escape and enclose in quotes before substituting. Use for all
74− -- text-like types, and anything else that may contain unsafe
75− -- characters when rendered.
76− | EscapeByteA ByteString
77− -- ^ Escape binary data for use as a @bytea@ literal. Include surrounding
78− -- quotes. This is used by the 'Binary' newtype wrapper.
79− | EscapeIdentifier ByteString
80− -- ^ Escape before substituting. Use for all sql identifiers like
81− -- table, column names, etc. This is used by the 'Identifier' newtype
82− -- wrapper.
83− | Many [Action]
84− -- ^ Concatenate a series of rendering actions.
85− deriving (Typeable)
86−
87−instance Show Action where
88− show (Plain b) = "Plain " ++ show (toByteString b)
89− show (Escape b) = "Escape " ++ show b
90− show (EscapeByteA b) = "EscapeByteA " ++ show b
91− show (EscapeIdentifier b) = "EscapeIdentifier " ++ show b
92− show (Many b) = "Many " ++ show b
93−
94−-- | A type that may be used as a single parameter to a SQL query.
95−class ToField a where
96− toField :: a -> Action
97− -- ^ Prepare a value for substitution into a query string.
98−
99−instance ToField Action where
100− toField a = a
101− {-# INLINE toField #-}
102−
103−instance (ToField a) => ToField (Const a b) where
104− toField (Const a) = toField a
105− {-# INLINE toField #-}
106−
107−instance (ToField a) => ToField (Identity a) where
108− toField (Identity a) = toField a
109− {-# INLINE toField #-}
110−
111−instance (ToField a) => ToField (Maybe a) where
112− toField Nothing = renderNull
113− toField (Just a) = toField a
114− {-# INLINE toField #-}
115−
116−instance (ToField a) => ToField (In [a]) where
117− toField (In []) = Plain $ byteString "(null)"
118− toField (In xs) = Many $
119− Plain (char8 '(') :
120− (intersperse (Plain (char8 ',')) . map toField $ xs) ++
121− [Plain (char8 ')')]
122−
123−renderNull :: Action
124−renderNull = Plain (byteString "null")
125−
126−instance ToField Null where
127− toField _ = renderNull
128− {-# INLINE toField #-}
129−
130−instance ToField Default where
131− toField _ = Plain (byteString "default")
132− {-# INLINE toField #-}
133−
134−instance ToField Bool where
135− toField True = Plain (byteString "true")
136− toField False = Plain (byteString "false")
137− {-# INLINE toField #-}
138−
139−instance ToField Int8 where
140− toField = Plain . int8Dec
141− {-# INLINE toField #-}
142−
143−instance ToField Int16 where
144− toField = Plain . int16Dec
145− {-# INLINE toField #-}
146−
147−instance ToField Int32 where
148− toField = Plain . int32Dec
149− {-# INLINE toField #-}
150−
151−instance ToField Int where
152− toField = Plain . intDec
153− {-# INLINE toField #-}
154−
155−instance ToField Int64 where
156− toField = Plain . int64Dec
157− {-# INLINE toField #-}
158−
159−instance ToField Integer where
160− toField = Plain . integerDec
161− {-# INLINE toField #-}
162−
163−instance ToField Word8 where
164− toField = Plain . word8Dec
165− {-# INLINE toField #-}
166−
167−instance ToField Word16 where
168− toField = Plain . word16Dec
169− {-# INLINE toField #-}
170−
171−instance ToField Word32 where
172− toField = Plain . word32Dec
173− {-# INLINE toField #-}
174−
175−instance ToField Word where
176− toField = Plain . wordDec
177− {-# INLINE toField #-}
178−
179−instance ToField Word64 where
180− toField = Plain . word64Dec
181− {-# INLINE toField #-}
182−
183−instance ToField PQ.Oid where
184− toField = Plain . \(PQ.Oid (CUInt x)) -> word32Dec x
185− {-# INLINE toField #-}
186−
187−instance ToField Float where
188− toField v | isNaN v || isInfinite v = Plain (inQuotes (floatDec v))
189− | otherwise = Plain (floatDec v)
190− {-# INLINE toField #-}
191−
192−instance ToField Double where
193− toField v | isNaN v || isInfinite v = Plain (inQuotes (doubleDec v))
194− | otherwise = Plain (doubleDec v)
195− {-# INLINE toField #-}
196−
197−instance ToField Scientific where
198− toField x = toField (LT.toLazyText (scientificBuilder x))
199− {-# INLINE toField #-}
200−
201−instance ToField (Binary SB.ByteString) where
202− toField (Binary bs) = EscapeByteA bs
203− {-# INLINE toField #-}
204−
205−instance ToField (Binary LB.ByteString) where
206− toField (Binary bs) = (EscapeByteA . SB.concat . LB.toChunks) bs
207− {-# INLINE toField #-}
208−
209−instance ToField Identifier where
210− toField (Identifier bs) = EscapeIdentifier (ST.encodeUtf8 bs)
211− {-# INLINE toField #-}
212−
213−instance ToField QualifiedIdentifier where
214− toField (QualifiedIdentifier (Just s) t) =
215− Many [ EscapeIdentifier (ST.encodeUtf8 s)
216− , Plain (char8 '.')
217− , EscapeIdentifier (ST.encodeUtf8 t)
218− ]
219− toField (QualifiedIdentifier Nothing t) =
220− EscapeIdentifier (ST.encodeUtf8 t)
221− {-# INLINE toField #-}
222−
223−instance ToField SB.ByteString where
224− toField = Escape
225− {-# INLINE toField #-}
226−
227−instance ToField LB.ByteString where
228− toField = toField . SB.concat . LB.toChunks
229− {-# INLINE toField #-}
230−
231−instance ToField ST.Text where
232− toField = Escape . ST.encodeUtf8
233− {-# INLINE toField #-}
234−
235−instance ToField [Char] where
236− toField = Escape . toByteString . stringUtf8
237− {-# INLINE toField #-}
238−
239−instance ToField LT.Text where
240− toField = toField . LT.toStrict
241− {-# INLINE toField #-}
242−
243−-- | citext
244−instance ToField (CI ST.Text) where
245− toField = toField . CI.original
246− {-# INLINE toField #-}
247−
248−-- | citext
249−instance ToField (CI LT.Text) where
250− toField = toField . LT.toStrict . CI.original
251− {-# INLINE toField #-}
252−
253−instance ToField UTCTime where
254− toField = Plain . inQuotes . utcTimeToBuilder
255− {-# INLINE toField #-}
256−
257−instance ToField ZonedTime where
258− toField = Plain . inQuotes . zonedTimeToBuilder
259− {-# INLINE toField #-}
260−
261−instance ToField LocalTime where
262− toField = Plain . inQuotes . localTimeToBuilder
263− {-# INLINE toField #-}
264−
265−instance ToField Day where
266− toField = Plain . inQuotes . dayToBuilder
267− {-# INLINE toField #-}
268−
269−instance ToField TimeOfDay where
270− toField = Plain . inQuotes . timeOfDayToBuilder
271− {-# INLINE toField #-}
272−
273−instance ToField UTCTimestamp where
274− toField = Plain . inQuotes . utcTimestampToBuilder
275− {-# INLINE toField #-}
276−
277−instance ToField ZonedTimestamp where
278− toField = Plain . inQuotes . zonedTimestampToBuilder
279− {-# INLINE toField #-}
280−
281−instance ToField LocalTimestamp where
282− toField = Plain . inQuotes . localTimestampToBuilder
283− {-# INLINE toField #-}
284−
285−instance ToField Date where
286− toField = Plain . inQuotes . dateToBuilder
287− {-# INLINE toField #-}
288−
289−instance ToField NominalDiffTime where
290− toField = Plain . inQuotes . nominalDiffTimeToBuilder
291− {-# INLINE toField #-}
292−
293−instance ToField CalendarDiffTime where
294− toField = Plain . inQuotes . calendarDiffTimeToBuilder
295− {-# INLINE toField #-}
296−
297−instance (ToField a) => ToField (PGArray a) where
298− toField pgArray =
299− case fromPGArray pgArray of
300− [] -> Plain (byteString "'{}'")
301− xs -> Many $
302− Plain (byteString "ARRAY[") :
303− (intersperse (Plain (char8 ',')) . map toField $ xs) ++
304− [Plain (char8 ']')]
305− -- Because the ARRAY[...] input syntax is being used, it is possible
306− -- that the use of type-specific separator characters is unnecessary.
307−
308−instance (ToField a) => ToField (Vector a) where
309− toField = toField . PGArray . V.toList
310−
311−instance ToField UUID where
312− toField = Plain . inQuotes . byteString . UUID.toASCIIBytes
313−
314−instance ToField JSON.Value where
315− toField = toField . JSON.encode
316−
317−-- | Convert a Haskell value to JSON using 'JSON.toEncoding'.
318−--
319−-- This can be used as the default implementation for the 'toField'
320−-- method for Haskell types that have a JSON representation in
321−-- PostgreSQL.
322−toJSONField :: JSON.ToJSON a => a -> Action
323−toJSONField = toField . JSON.encode
324−
325−-- | Surround a string with single-quote characters: \"@'@\"
326−--
327−-- This function /does not/ perform any other escaping.
328−inQuotes :: Builder -> Builder
329−inQuotes b = quote `mappend` b `mappend` quote
330− where quote = char8 '\''
331−
332−interleaveFoldr :: (a -> [b] -> [b]) -> b -> [b] -> [a] -> [b]
333−interleaveFoldr f b bs' as = foldr (\a bs -> b : f a bs) bs' as
334−{-# INLINE interleaveFoldr #-}
335−
336−instance ToRow a => ToField (Values a) where
337− toField (Values types rows) =
338− case rows of
339− [] -> case types of
340− [] -> error norows
341− (_:_) -> values $ typedRow (repeat (lit "null"))
342− types
343− [lit " LIMIT 0)"]
344− (_:_) -> case types of
345− [] -> values $ untypedRows rows [litC ')']
346− (_:_) -> values $ typedRows rows types [litC ')']
347− where
348− funcname = "Database.PostgreSQL.Simple.toField :: Values a -> Action"
349− norows = funcname ++ " either values or types must be non-empty"
350− emptyrow = funcname ++ " each row must contain at least one column"
351− lit = Plain . byteString
352− litC = Plain . char8
353− values x = Many (lit "(VALUES ": x)
354−
355− typedField :: (Action, QualifiedIdentifier) -> [Action] -> [Action]
356− typedField (val,typ) rest = val : lit "::" : toField typ : rest
357−
358− typedRow :: [Action] -> [QualifiedIdentifier] -> [Action] -> [Action]
359− typedRow (val:vals) (typ:typs) rest =
360− litC '(' :
361− typedField (val,typ) ( interleaveFoldr
362− typedField
363− (litC ',')
364− (litC ')' : rest)
365− (zip vals typs) )
366− typedRow _ _ _ = error emptyrow
367−
368− untypedRow :: [Action] -> [Action] -> [Action]
369− untypedRow (val:vals) rest =
370− litC '(' : val :
371− interleaveFoldr
372− (:)
373− (litC ',')
374− (litC ')' : rest)
375− vals
376− untypedRow _ _ = error emptyrow
377−
378− typedRows :: ToRow a => [a] -> [QualifiedIdentifier] -> [Action] -> [Action]
379− typedRows [] _ _ = error funcname
380− typedRows (val:vals) typs rest =
381− typedRow (toRow val) typs (multiRows vals rest)
382−
383− untypedRows :: ToRow a => [a] -> [Action] -> [Action]
384− untypedRows [] _ = error funcname
385− untypedRows (val:vals) rest =
386− untypedRow (toRow val) (multiRows vals rest)
387−
388− multiRows :: ToRow a => [a] -> [Action] -> [Action]
389− multiRows vals rest = interleaveFoldr
390− (untypedRow . toRow)
391− (litC ',')
392− rest
393− vals
src/Database/PostgreSQL/Simple/ToField.hs-bootdeleted−30
@@ -1,30 +0,0 @@
1−module Database.PostgreSQL.Simple.ToField where
2−
3−import Database.PostgreSQL.Simple.Types
4−import Data.ByteString.Builder(Builder)
5−import Data.ByteString(ByteString)
6−
7−-- | How to render an element when substituting it into a query.
8−data Action =
9− Plain Builder
10− -- ^ Render without escaping or quoting. Use for non-text types
11− -- such as numbers, when you are /certain/ that they will not
12− -- introduce formatting vulnerabilities via use of characters such
13− -- as spaces or \"@'@\".
14− | Escape ByteString
15− -- ^ Escape and enclose in quotes before substituting. Use for all
16− -- text-like types, and anything else that may contain unsafe
17− -- characters when rendered.
18− | EscapeByteA ByteString
19− -- ^ Escape binary data for use as a @bytea@ literal. Include surrounding
20− -- quotes. This is used by the 'Binary' newtype wrapper.
21− | EscapeIdentifier ByteString
22− -- ^ Escape before substituting. Use for all sql identifiers like
23− -- table, column names, etc. This is used by the 'Identifier' newtype
24− -- wrapper.
25− | Many [Action]
26− -- ^ Concatenate a series of rendering actions.
27−
28−class ToField a
29−
30−instance ToField Oid
src/Database/PostgreSQL/Simple/ToRow.hsdeleted−219
@@ -1,219 +0,0 @@
1−{-# LANGUAGE DefaultSignatures, FlexibleInstances, FlexibleContexts #-}
2−------------------------------------------------------------------------------
3−-- |
4−-- Module: Database.PostgreSQL.Simple.ToRow
5−-- Copyright: (c) 2011 MailRank, Inc.
6−-- (c) 2011-2012 Leon P Smith
7−-- License: BSD3
8−-- Maintainer: Leon P Smith <leon@melding-monads.com>
9−-- Stability: experimental
10−--
11−-- The 'ToRow' typeclass, for rendering a collection of
12−-- parameters to a SQL query.
13−--
14−-- Predefined instances are provided for tuples containing up to ten
15−-- elements.
16−--
17−------------------------------------------------------------------------------
18−
19−module Database.PostgreSQL.Simple.ToRow
20− (
21− ToRow(..)
22− ) where
23−
24−import Database.PostgreSQL.Simple.ToField (Action(..), ToField(..))
25−import Database.PostgreSQL.Simple.Types (Only(..), (:.)(..))
26−import GHC.Generics
27−
28−-- | A collection type that can be turned into a list of rendering
29−-- 'Action's.
30−--
31−-- Instances should use the 'toField' method of the 'ToField' class
32−-- to perform conversion of each element of the collection.
33−--
34−-- You can derive 'ToRow' for your data type using GHC generics, like this:
35−--
36−-- @
37−-- \{-# LANGUAGE DeriveAnyClass \#-}
38−-- \{-# LANGUAGE DeriveGeneric \#-}
39−--
40−-- import "GHC.Generics" ('GHC.Generics.Generic')
41−-- import "Database.PostgreSQL.Simple" ('ToRow')
42−--
43−-- data User = User { name :: String, fileQuota :: Int }
44−-- deriving ('GHC.Generics.Generic', 'ToRow')
45−-- @
46−--
47−-- Note that this only works for product types (e.g. records) and does not
48−-- support sum types or recursive types.
49−class ToRow a where
50− toRow :: a -> [Action]
51− default toRow :: (Generic a, GToRow (Rep a)) => a -> [Action]
52− toRow = gtoRow . from
53− -- ^ ToField a collection of values.
54−
55−instance ToRow () where
56− toRow _ = []
57−
58−instance (ToField a) => ToRow (Only a) where
59− toRow (Only v) = [toField v]
60−
61−instance (ToField a, ToField b) => ToRow (a,b) where
62− toRow (a,b) = [toField a, toField b]
63−
64−instance (ToField a, ToField b, ToField c) => ToRow (a,b,c) where
65− toRow (a,b,c) = [toField a, toField b, toField c]
66−
67−instance (ToField a, ToField b, ToField c, ToField d) => ToRow (a,b,c,d) where
68− toRow (a,b,c,d) = [toField a, toField b, toField c, toField d]
69−
70−instance (ToField a, ToField b, ToField c, ToField d, ToField e)
71− => ToRow (a,b,c,d,e) where
72− toRow (a,b,c,d,e) =
73− [toField a, toField b, toField c, toField d, toField e]
74−
75−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f)
76− => ToRow (a,b,c,d,e,f) where
77− toRow (a,b,c,d,e,f) =
78− [toField a, toField b, toField c, toField d, toField e, toField f]
79−
80−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
81− ToField g)
82− => ToRow (a,b,c,d,e,f,g) where
83− toRow (a,b,c,d,e,f,g) =
84− [toField a, toField b, toField c, toField d, toField e, toField f,
85− toField g]
86−
87−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
88− ToField g, ToField h)
89− => ToRow (a,b,c,d,e,f,g,h) where
90− toRow (a,b,c,d,e,f,g,h) =
91− [toField a, toField b, toField c, toField d, toField e, toField f,
92− toField g, toField h]
93−
94−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
95− ToField g, ToField h, ToField i)
96− => ToRow (a,b,c,d,e,f,g,h,i) where
97− toRow (a,b,c,d,e,f,g,h,i) =
98− [toField a, toField b, toField c, toField d, toField e, toField f,
99− toField g, toField h, toField i]
100−
101−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
102− ToField g, ToField h, ToField i, ToField j)
103− => ToRow (a,b,c,d,e,f,g,h,i,j) where
104− toRow (a,b,c,d,e,f,g,h,i,j) =
105− [toField a, toField b, toField c, toField d, toField e, toField f,
106− toField g, toField h, toField i, toField j]
107−
108−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
109− ToField g, ToField h, ToField i, ToField j, ToField k)
110− => ToRow (a,b,c,d,e,f,g,h,i,j,k) where
111− toRow (a,b,c,d,e,f,g,h,i,j,k) =
112− [toField a, toField b, toField c, toField d, toField e, toField f,
113− toField g, toField h, toField i, toField j, toField k]
114−
115−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
116− ToField g, ToField h, ToField i, ToField j, ToField k, ToField l)
117− => ToRow (a,b,c,d,e,f,g,h,i,j,k,l) where
118− toRow (a,b,c,d,e,f,g,h,i,j,k,l) =
119− [toField a, toField b, toField c, toField d, toField e, toField f,
120− toField g, toField h, toField i, toField j, toField k, toField l]
121−
122−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
123− ToField g, ToField h, ToField i, ToField j, ToField k, ToField l,
124− ToField m)
125− => ToRow (a,b,c,d,e,f,g,h,i,j,k,l,m) where
126− toRow (a,b,c,d,e,f,g,h,i,j,k,l,m) =
127− [toField a, toField b, toField c, toField d, toField e, toField f,
128− toField g, toField h, toField i, toField j, toField k, toField l,
129− toField m]
130−
131−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
132− ToField g, ToField h, ToField i, ToField j, ToField k, ToField l,
133− ToField m, ToField n)
134− => ToRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n) where
135− toRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n) =
136− [toField a, toField b, toField c, toField d, toField e, toField f,
137− toField g, toField h, toField i, toField j, toField k, toField l,
138− toField m, toField n]
139−
140−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
141− ToField g, ToField h, ToField i, ToField j, ToField k, ToField l,
142− ToField m, ToField n, ToField o)
143− => ToRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) where
144− toRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) =
145− [toField a, toField b, toField c, toField d, toField e, toField f,
146− toField g, toField h, toField i, toField j, toField k, toField l,
147− toField m, toField n, toField o]
148−
149−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
150− ToField g, ToField h, ToField i, ToField j, ToField k, ToField l,
151− ToField m, ToField n, ToField o, ToField p)
152− => ToRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) where
153− toRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) =
154− [toField a, toField b, toField c, toField d, toField e, toField f,
155− toField g, toField h, toField i, toField j, toField k, toField l,
156− toField m, toField n, toField o, toField p]
157−
158−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
159− ToField g, ToField h, ToField i, ToField j, ToField k, ToField l,
160− ToField m, ToField n, ToField o, ToField p, ToField q)
161− => ToRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q) where
162− toRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q) =
163− [toField a, toField b, toField c, toField d, toField e, toField f,
164− toField g, toField h, toField i, toField j, toField k, toField l,
165− toField m, toField n, toField o, toField p, toField q]
166−
167−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
168− ToField g, ToField h, ToField i, ToField j, ToField k, ToField l,
169− ToField m, ToField n, ToField o, ToField p, ToField q, ToField r)
170− => ToRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r) where
171− toRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r) =
172− [toField a, toField b, toField c, toField d, toField e, toField f,
173− toField g, toField h, toField i, toField j, toField k, toField l,
174− toField m, toField n, toField o, toField p, toField q, toField r]
175−
176−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
177− ToField g, ToField h, ToField i, ToField j, ToField k, ToField l,
178− ToField m, ToField n, ToField o, ToField p, ToField q, ToField r,
179− ToField s)
180− => ToRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s) where
181− toRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s) =
182− [toField a, toField b, toField c, toField d, toField e, toField f,
183− toField g, toField h, toField i, toField j, toField k, toField l,
184− toField m, toField n, toField o, toField p, toField q, toField r,
185− toField s]
186−
187−instance (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f,
188− ToField g, ToField h, ToField i, ToField j, ToField k, ToField l,
189− ToField m, ToField n, ToField o, ToField p, ToField q, ToField r,
190− ToField s, ToField t)
191− => ToRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t) where
192− toRow (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t) =
193− [toField a, toField b, toField c, toField d, toField e, toField f,
194− toField g, toField h, toField i, toField j, toField k, toField l,
195− toField m, toField n, toField o, toField p, toField q, toField r,
196− toField s, toField t]
197−
198−instance (ToField a) => ToRow [a] where
199− toRow = map toField
200−
201−instance (ToRow a, ToRow b) => ToRow (a :. b) where
202− toRow (a :. b) = toRow a ++ toRow b
203−
204−
205−-- Type class for default implementation of ToRow using generics
206−class GToRow f where
207− gtoRow :: f p -> [Action]
208−
209−instance GToRow f => GToRow (M1 c i f) where
210− gtoRow (M1 x) = gtoRow x
211−
212−instance (GToRow f, GToRow g) => GToRow (f :*: g) where
213− gtoRow (f :*: g) = gtoRow f ++ gtoRow g
214−
215−instance (ToField a) => GToRow (K1 R a) where
216− gtoRow (K1 a) = [toField a]
217−
218−instance GToRow U1 where
219− gtoRow _ = []
src/Database/PostgreSQL/Simple/ToRow.hs-bootdeleted−18
@@ -1,18 +0,0 @@
1−{-# LANGUAGE DefaultSignatures, FlexibleInstances, FlexibleContexts #-}
2−module Database.PostgreSQL.Simple.ToRow (
3− ToRow(..)
4− ) where
5−
6−import Database.PostgreSQL.Simple.Types
7−import {-# SOURCE #-} Database.PostgreSQL.Simple.ToField
8−import GHC.Generics
9−
10−class ToRow a where
11− toRow :: a -> [Action]
12− default toRow :: (Generic a, GToRow (Rep a)) => a -> [Action]
13− toRow = gtoRow . from
14−
15−class GToRow f where
16− gtoRow :: f p -> [Action]
17−
18−instance ToField a => ToRow (Only a)
src/Database/PostgreSQL/Simple/Transaction.hsdeleted−264
@@ -1,264 +0,0 @@
1−{-# LANGUAGE RecordWildCards, ScopedTypeVariables #-}
2−
3−------------------------------------------------------------------------------
4−-- |
5−-- Module: Database.PostgreSQL.Simple.Transaction
6−-- Copyright: (c) 2011-2013 Leon P Smith
7−-- (c) 2013 Joey Adams
8−-- License: BSD3
9−-- Maintainer: Leon P Smith <leon@melding-monads.com>
10−--
11−------------------------------------------------------------------------------
12−
13−module Database.PostgreSQL.Simple.Transaction
14− (
15− -- * Transaction handling
16− withTransaction
17− , withTransactionLevel
18− , withTransactionMode
19− , withTransactionModeRetry
20− , withTransactionModeRetry'
21− , withTransactionSerializable
22− , TransactionMode(..)
23− , IsolationLevel(..)
24− , ReadWriteMode(..)
25− , defaultTransactionMode
26− , defaultIsolationLevel
27− , defaultReadWriteMode
28−-- , Base.autocommit
29− , begin
30− , beginLevel
31− , beginMode
32− , commit
33− , rollback
34−
35− -- * Savepoint
36− , withSavepoint
37− , Savepoint
38− , newSavepoint
39− , releaseSavepoint
40− , rollbackToSavepoint
41− , rollbackToAndReleaseSavepoint
42−
43− -- * Error predicates
44− , isSerializationError
45− , isNoActiveTransactionError
46− , isFailedTransactionError
47− ) where
48−
49−import qualified Control.Exception as E
50−import qualified Data.ByteString as B
51−import Database.PostgreSQL.Simple.Internal
52−import Database.PostgreSQL.Simple.Types
53−import Database.PostgreSQL.Simple.Errors
54−import Database.PostgreSQL.Simple.Compat (mask, (<>))
55−
56−
57−-- | Of the four isolation levels defined by the SQL standard,
58−-- these are the three levels distinguished by PostgreSQL as of version 9.0.
59−-- See <https://www.postgresql.org/docs/9.5/static/transaction-iso.html>
60−-- for more information. Note that prior to PostgreSQL 9.0, 'RepeatableRead'
61−-- was equivalent to 'Serializable'.
62−
63−data IsolationLevel
64− = DefaultIsolationLevel -- ^ the isolation level will be taken from
65− -- PostgreSQL's per-connection
66− -- @default_transaction_isolation@ variable,
67− -- which is initialized according to the
68− -- server's config. The default configuration
69− -- is 'ReadCommitted'.
70− | ReadCommitted
71− | RepeatableRead
72− | Serializable
73− deriving (Show, Eq, Ord, Enum, Bounded)
74−
75−data ReadWriteMode
76− = DefaultReadWriteMode -- ^ the read-write mode will be taken from
77− -- PostgreSQL's per-connection
78− -- @default_transaction_read_only@ variable,
79− -- which is initialized according to the
80− -- server's config. The default configuration
81− -- is 'ReadWrite'.
82− | ReadWrite
83− | ReadOnly
84− deriving (Show, Eq, Ord, Enum, Bounded)
85−
86−data TransactionMode = TransactionMode {
87− isolationLevel :: !IsolationLevel,
88− readWriteMode :: !ReadWriteMode
89− } deriving (Show, Eq)
90−
91−defaultTransactionMode :: TransactionMode
92−defaultTransactionMode = TransactionMode
93− defaultIsolationLevel
94− defaultReadWriteMode
95−
96−defaultIsolationLevel :: IsolationLevel
97−defaultIsolationLevel = DefaultIsolationLevel
98−
99−defaultReadWriteMode :: ReadWriteMode
100−defaultReadWriteMode = DefaultReadWriteMode
101−
102−-- | Execute an action inside a SQL transaction.
103−--
104−-- This function initiates a transaction with a \"@begin
105−-- transaction@\" statement, then executes the supplied action. If
106−-- the action succeeds, the transaction will be completed with
107−-- 'Base.commit' before this function returns.
108−--
109−-- If the action throws /any/ kind of exception (not just a
110−-- PostgreSQL-related exception), the transaction will be rolled back using
111−-- 'rollback', then the exception will be rethrown.
112−--
113−-- For nesting transactions, see 'withSavepoint'.
114−withTransaction :: Connection -> IO a -> IO a
115−withTransaction = withTransactionMode defaultTransactionMode
116−
117−-- | Execute an action inside of a 'Serializable' transaction. If a
118−-- serialization failure occurs, roll back the transaction and try again.
119−-- Be warned that this may execute the IO action multiple times.
120−--
121−-- A 'Serializable' transaction creates the illusion that your program has
122−-- exclusive access to the database. This means that, even in a concurrent
123−-- setting, you can perform queries in sequence without having to worry about
124−-- what might happen between one statement and the next.
125−--
126−-- Think of it as STM, but without @retry@.
127−withTransactionSerializable :: Connection -> IO a -> IO a
128−withTransactionSerializable =
129− withTransactionModeRetry
130− TransactionMode
131− { isolationLevel = Serializable
132− , readWriteMode = ReadWrite
133− }
134− isSerializationError
135−
136−-- | Execute an action inside a SQL transaction with a given isolation level.
137−withTransactionLevel :: IsolationLevel -> Connection -> IO a -> IO a
138−withTransactionLevel lvl
139− = withTransactionMode defaultTransactionMode { isolationLevel = lvl }
140−
141−-- | Execute an action inside a SQL transaction with a given transaction mode.
142−withTransactionMode :: TransactionMode -> Connection -> IO a -> IO a
143−withTransactionMode mode conn act =
144− mask $ \restore -> do
145− beginMode mode conn
146− r <- restore act `E.onException` rollback_ conn
147− commit conn
148− return r
149−
150−-- | 'withTransactionModeRetry'' but with the exception type pinned to 'SqlError'.
151−withTransactionModeRetry :: TransactionMode -> (SqlError -> Bool) -> Connection -> IO a -> IO a
152−withTransactionModeRetry = withTransactionModeRetry'
153−
154−-- | Like 'withTransactionMode', but also takes a custom callback to
155−-- determine if a transaction should be retried if an exception occurs.
156−-- If the callback returns 'True', then the transaction will be retried.
157−-- If the callback returns 'False', or an exception other than an @e@
158−-- occurs then the transaction will be rolled back and the exception rethrown.
159−--
160−-- This is used to implement 'withTransactionSerializable'.
161−withTransactionModeRetry' :: forall a e. E.Exception e => TransactionMode -> (e -> Bool) -> Connection -> IO a -> IO a
162−withTransactionModeRetry' mode shouldRetry conn act =
163− mask $ \restore ->
164− retryLoop $ E.try $ do
165− a <- restore act `E.onException` rollback_ conn
166− commit conn
167− return a
168− where
169− retryLoop :: IO (Either e a) -> IO a
170− retryLoop act' = do
171− beginMode mode conn
172− r <- act'
173− case r of
174− Left e ->
175− case shouldRetry e of
176− True -> retryLoop act'
177− False -> E.throwIO e
178− Right a ->
179− return a
180−
181−-- | Rollback a transaction.
182−rollback :: Connection -> IO ()
183−rollback conn = execute_ conn "ROLLBACK" >> return ()
184−
185−-- | Rollback a transaction, ignoring any @IOErrors@
186−rollback_ :: Connection -> IO ()
187−rollback_ conn = rollback conn `E.catch` \(_ :: IOError) -> return ()
188−
189−-- | Commit a transaction.
190−commit :: Connection -> IO ()
191−commit conn = execute_ conn "COMMIT" >> return ()
192−
193−-- | Begin a transaction.
194−begin :: Connection -> IO ()
195−begin = beginMode defaultTransactionMode
196−
197−-- | Begin a transaction with a given isolation level
198−beginLevel :: IsolationLevel -> Connection -> IO ()
199−beginLevel lvl = beginMode defaultTransactionMode { isolationLevel = lvl }
200−
201−-- | Begin a transaction with a given transaction mode
202−beginMode :: TransactionMode -> Connection -> IO ()
203−beginMode mode conn = do
204− _ <- execute_ conn $! Query (B.concat ["BEGIN", isolevel, readmode])
205− return ()
206− where
207− isolevel = case isolationLevel mode of
208− DefaultIsolationLevel -> ""
209− ReadCommitted -> " ISOLATION LEVEL READ COMMITTED"
210− RepeatableRead -> " ISOLATION LEVEL REPEATABLE READ"
211− Serializable -> " ISOLATION LEVEL SERIALIZABLE"
212− readmode = case readWriteMode mode of
213− DefaultReadWriteMode -> ""
214− ReadWrite -> " READ WRITE"
215− ReadOnly -> " READ ONLY"
216−
217−------------------------------------------------------------------------
218−-- Savepoint
219−
220−-- | Create a savepoint, and roll back to it if an error occurs. This may only
221−-- be used inside of a transaction, and provides a sort of
222−-- \"nested transaction\".
223−--
224−-- See <https://www.postgresql.org/docs/9.5/static/sql-savepoint.html>
225−withSavepoint :: Connection -> IO a -> IO a
226−withSavepoint conn body =
227− mask $ \restore -> do
228− sp <- newSavepoint conn
229− r <- restore body `E.onException` rollbackToAndReleaseSavepoint conn sp
230− releaseSavepoint conn sp `E.catch` \err ->
231− if isFailedTransactionError err
232− then rollbackToAndReleaseSavepoint conn sp
233− else E.throwIO err
234− return r
235−
236−-- | Create a new savepoint. This may only be used inside of a transaction.
237−newSavepoint :: Connection -> IO Savepoint
238−newSavepoint conn = do
239− name <- newTempName conn
240− _ <- execute_ conn ("SAVEPOINT " <> name)
241− return (Savepoint name)
242−
243−-- | Destroy a savepoint, but retain its effects.
244−--
245−-- Warning: this will throw a 'SqlError' matching 'isFailedTransactionError' if
246−-- the transaction is aborted due to an error. 'commit' would merely warn and
247−-- roll back.
248−releaseSavepoint :: Connection -> Savepoint -> IO ()
249−releaseSavepoint conn (Savepoint name) =
250− execute_ conn ("RELEASE SAVEPOINT " <> name) >> return ()
251−
252−-- | Roll back to a savepoint. This will not release the savepoint.
253−rollbackToSavepoint :: Connection -> Savepoint -> IO ()
254−rollbackToSavepoint conn (Savepoint name) =
255− execute_ conn ("ROLLBACK TO SAVEPOINT " <> name) >> return ()
256−
257−-- | Roll back to a savepoint and release it. This is like calling
258−-- 'rollbackToSavepoint' followed by 'releaseSavepoint', but avoids a
259−-- round trip to the database server.
260−rollbackToAndReleaseSavepoint :: Connection -> Savepoint -> IO ()
261−rollbackToAndReleaseSavepoint conn (Savepoint name) =
262− execute_ conn sql >> return ()
263− where
264− sql = "ROLLBACK TO SAVEPOINT " <> name <> "; RELEASE SAVEPOINT " <> name
src/Database/PostgreSQL/Simple/TypeInfo.hsdeleted−121
@@ -1,121 +0,0 @@
1−{-# LANGUAGE RecordWildCards #-}
2−
3−------------------------------------------------------------------------------
4−-- |
5−-- Module: Database.PostgreSQL.Simple.TypeInfo
6−-- Copyright: (c) 2013 Leon P Smith
7−-- License: BSD3
8−-- Maintainer: Leon P Smith <leon@melding-monads.com>
9−-- Stability: experimental
10−--
11−-- This module provides convenient and efficient access to parts of the
12−-- @pg_type@ metatable. At the moment, this requires PostgreSQL 8.4 if
13−-- you need to work with types that do not appear in
14−-- 'Database.PostgreSQL.Simple.TypeInfo.Static'.
15−--
16−-- The current scheme could be more efficient, especially for some use
17−-- cases. In particular, connection pools that use many user-added
18−-- types and connect to a set of servers with identical (or at least
19−-- compatible) @pg_type@ and associated tables could share a common
20−-- typeinfo cache, thus saving memory and communication between the
21−-- client and server.
22−--
23−------------------------------------------------------------------------------
24−
25−module Database.PostgreSQL.Simple.TypeInfo
26− ( getTypeInfo
27− , TypeInfo(..)
28− , Attribute(..)
29− ) where
30−
31−import qualified Data.ByteString as B
32−import qualified Data.IntMap as IntMap
33−import qualified Data.Vector as V
34−import qualified Data.Vector.Mutable as MV
35−import Control.Concurrent.MVar
36−import Control.Exception (throw)
37−
38−import qualified Database.PostgreSQL.LibPQ as PQ
39−import {-# SOURCE #-} Database.PostgreSQL.Simple
40−import Database.PostgreSQL.Simple.Internal
41−import Database.PostgreSQL.Simple.Types
42−import Database.PostgreSQL.Simple.TypeInfo.Types
43−import Database.PostgreSQL.Simple.TypeInfo.Static
44−
45−-- | Returns the metadata of the type with a particular oid. To find
46−-- this data, 'getTypeInfo' first consults postgresql-simple's
47−-- built-in 'staticTypeInfo' table, then checks the connection's
48−-- typeinfo cache. Finally, the database's 'pg_type' table will
49−-- be queried only if necessary, and the result will be stored
50−-- in the connections's cache.
51−
52−getTypeInfo :: Connection -> PQ.Oid -> IO TypeInfo
53−getTypeInfo conn@Connection{..} oid' =
54− case staticTypeInfo oid' of
55− Just name' -> return name'
56− Nothing -> modifyMVar connectionObjects $ getTypeInfo' conn oid'
57−
58−getTypeInfo' :: Connection -> PQ.Oid -> TypeInfoCache
59− -> IO (TypeInfoCache, TypeInfo)
60−getTypeInfo' conn oid' oidmap =
61− case IntMap.lookup (oid2int oid') oidmap of
62− Just typeinfo -> return (oidmap, typeinfo)
63− Nothing -> do
64− names <- query conn "SELECT oid, typcategory, typdelim, typname,\
65− \ typelem, typrelid\
66− \ FROM pg_type WHERE oid = ?"
67− (Only oid')
68− (oidmap', typeInfo) <-
69− case names of
70− [] -> return $ throw (fatalError "invalid type oid")
71− [(typoid, typcategory, typdelim, typname, typelem_, typrelid)] -> do
72− case typcategory of
73− 'A' -> do
74− (oidmap', typelem) <- getTypeInfo' conn typelem_ oidmap
75− let !typeInfo = Array{..}
76− return $! (oidmap', typeInfo)
77− 'R' -> do
78− rngsubtypeOids <- query conn "SELECT rngsubtype\
79− \ FROM pg_range\
80− \ WHERE rngtypid = ?"
81− (Only oid')
82− case rngsubtypeOids of
83− [Only rngsubtype_] -> do
84− (oidmap', rngsubtype) <-
85− getTypeInfo' conn rngsubtype_ oidmap
86− let !typeInfo = Range{..}
87− return $! (oidmap', typeInfo)
88− _ -> fail "range subtype query failed to return exactly one result"
89− 'C' -> do
90− cols <- query conn "SELECT attname, atttypid\
91− \ FROM pg_attribute\
92− \ WHERE attrelid = ?\
93− \ AND attnum > 0\
94− \ AND NOT attisdropped\
95− \ ORDER BY attnum"
96− (Only typrelid)
97− vec <- MV.new $! length cols
98− (oidmap', attributes) <- getAttInfos conn cols oidmap vec 0
99− let !typeInfo = Composite{..}
100− return $! (oidmap', typeInfo)
101− _ -> do
102− let !typeInfo = Basic{..}
103− return $! (oidmap, typeInfo)
104− _ -> fail "typename query returned more than one result"
105− -- oid is a primary key, so the query should
106− -- never return more than one result
107− let !oidmap'' = IntMap.insert (oid2int oid') typeInfo oidmap'
108− return $! (oidmap'', typeInfo)
109−
110−getAttInfos :: Connection -> [(B.ByteString, PQ.Oid)] -> TypeInfoCache
111− -> MV.IOVector Attribute -> Int
112− -> IO (TypeInfoCache, V.Vector Attribute)
113−getAttInfos conn cols oidmap vec n =
114− case cols of
115− [] -> do
116− !attributes <- V.unsafeFreeze vec
117− return $! (oidmap, attributes)
118− ((attname, attTypeOid):xs) -> do
119− (oidmap', atttype) <- getTypeInfo' conn attTypeOid oidmap
120− MV.write vec n $! Attribute{..}
121− getAttInfos conn xs oidmap' vec (n+1)
src/Database/PostgreSQL/Simple/TypeInfo/Macro.hsdeleted−53
@@ -1,53 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−#if __GLASGOW_HASKELL__ >= 800
3−{-# LANGUAGE TemplateHaskellQuotes #-}
4−#else
5−{-# LANGUAGE TemplateHaskell #-}
6−#endif
7−
8−------------------------------------------------------------------------------
9−-- |
10−-- Module: Database.PostgreSQL.Simple.TypeInfo.Macro
11−-- Copyright: (c) 2013 Leon P Smith
12−-- License: BSD3
13−-- Maintainer: Leon P Smith <leon@melding-monads.com>
14−-- Stability: experimental
15−--
16−-- A Template Haskell macro for efficiently checking membership in
17−-- a set of type oids.
18−--
19−------------------------------------------------------------------------------
20−
21−module Database.PostgreSQL.Simple.TypeInfo.Macro
22− ( mkCompats
23− , inlineTypoid
24− ) where
25−
26−import Database.PostgreSQL.Simple.TypeInfo.Static
27−import Database.PostgreSQL.Simple.Types (Oid(..))
28−import Language.Haskell.TH
29−
30−-- | Returns an expression that has type @'Oid' -> 'Bool'@, true if the
31−-- oid is equal to any one of the 'typoid's of the given 'TypeInfo's.
32−mkCompats :: [TypeInfo] -> ExpQ
33−mkCompats tys = do
34− x <- newName "x"
35− lamE [conP 'Oid [varP x]] $ caseE (varE x) (map alt tys ++ [catchAll])
36− where
37− alt :: TypeInfo -> MatchQ
38− alt ty = match (inlineTypoidP ty) (normalB [| True |]) []
39−
40− catchAll :: MatchQ
41− catchAll = match wildP (normalB [| False |]) []
42−
43−-- | Literally substitute the 'typoid' of a 'TypeInfo' expression.
44−-- Returns an expression of type 'Oid'. Useful because GHC tends
45−-- not to fold constants.
46−inlineTypoid :: TypeInfo -> ExpQ
47−inlineTypoid ty = conE 'Oid `appE` litE (getTypoid ty)
48−
49−inlineTypoidP :: TypeInfo -> PatQ
50−inlineTypoidP ty = litP (getTypoid ty)
51−
52−getTypoid :: TypeInfo -> Lit
53−getTypoid ty = let (Oid x) = typoid ty in integerL (fromIntegral x)
src/Database/PostgreSQL/Simple/TypeInfo/Static.hsdeleted−1802
@@ -1,1802 +0,0 @@
1−------------------------------------------------------------------------------
2−-- |
3−-- Module: Database.PostgreSQL.Simple.TypeInfo
4−-- Copyright: (c) 2011-2012 Leon P Smith
5−-- License: BSD3
6−-- Maintainer: Leon P Smith <leon@melding-monads.com>
7−-- Stability: experimental
8−--
9−-- This module contains portions of the @pg_type@ table that are relevant
10−-- to postgresql-simple and are believed to not change between PostgreSQL
11−-- versions.
12−--
13−------------------------------------------------------------------------------
14−
15−-- Note that this file is generated by tools/GenTypeInfo.hs, and should
16−-- not be edited directly
17−
18−module Database.PostgreSQL.Simple.TypeInfo.Static
19− ( TypeInfo(..)
20− , staticTypeInfo
21− , bool
22− , boolOid
23− , bytea
24− , byteaOid
25− , char
26− , charOid
27− , name
28− , nameOid
29− , int8
30− , int8Oid
31− , int2
32− , int2Oid
33− , int4
34− , int4Oid
35− , regproc
36− , regprocOid
37− , text
38− , textOid
39− , oid
40− , oidOid
41− , tid
42− , tidOid
43− , xid
44− , xidOid
45− , cid
46− , cidOid
47− , xml
48− , xmlOid
49− , point
50− , pointOid
51− , lseg
52− , lsegOid
53− , path
54− , pathOid
55− , box
56− , boxOid
57− , polygon
58− , polygonOid
59− , line
60− , lineOid
61− , cidr
62− , cidrOid
63− , float4
64− , float4Oid
65− , float8
66− , float8Oid
67− , unknown
68− , unknownOid
69− , circle
70− , circleOid
71− , money
72− , moneyOid
73− , macaddr
74− , macaddrOid
75− , inet
76− , inetOid
77− , bpchar
78− , bpcharOid
79− , varchar
80− , varcharOid
81− , date
82− , dateOid
83− , time
84− , timeOid
85− , timestamp
86− , timestampOid
87− , timestamptz
88− , timestamptzOid
89− , interval
90− , intervalOid
91− , timetz
92− , timetzOid
93− , bit
94− , bitOid
95− , varbit
96− , varbitOid
97− , numeric
98− , numericOid
99− , refcursor
100− , refcursorOid
101− , record
102− , recordOid
103− , void
104− , voidOid
105− , array_record
106− , array_recordOid
107− , regprocedure
108− , regprocedureOid
109− , regoper
110− , regoperOid
111− , regoperator
112− , regoperatorOid
113− , regclass
114− , regclassOid
115− , regtype
116− , regtypeOid
117− , uuid
118− , uuidOid
119− , json
120− , jsonOid
121− , jsonb
122− , jsonbOid
123− , int2vector
124− , int2vectorOid
125− , oidvector
126− , oidvectorOid
127− , array_xml
128− , array_xmlOid
129− , array_json
130− , array_jsonOid
131− , array_line
132− , array_lineOid
133− , array_cidr
134− , array_cidrOid
135− , array_circle
136− , array_circleOid
137− , array_money
138− , array_moneyOid
139− , array_bool
140− , array_boolOid
141− , array_bytea
142− , array_byteaOid
143− , array_char
144− , array_charOid
145− , array_name
146− , array_nameOid
147− , array_int2
148− , array_int2Oid
149− , array_int2vector
150− , array_int2vectorOid
151− , array_int4
152− , array_int4Oid
153− , array_regproc
154− , array_regprocOid
155− , array_text
156− , array_textOid
157− , array_tid
158− , array_tidOid
159− , array_xid
160− , array_xidOid
161− , array_cid
162− , array_cidOid
163− , array_oidvector
164− , array_oidvectorOid
165− , array_bpchar
166− , array_bpcharOid
167− , array_varchar
168− , array_varcharOid
169− , array_int8
170− , array_int8Oid
171− , array_point
172− , array_pointOid
173− , array_lseg
174− , array_lsegOid
175− , array_path
176− , array_pathOid
177− , array_box
178− , array_boxOid
179− , array_float4
180− , array_float4Oid
181− , array_float8
182− , array_float8Oid
183− , array_polygon
184− , array_polygonOid
185− , array_oid
186− , array_oidOid
187− , array_macaddr
188− , array_macaddrOid
189− , array_inet
190− , array_inetOid
191− , array_timestamp
192− , array_timestampOid
193− , array_date
194− , array_dateOid
195− , array_time
196− , array_timeOid
197− , array_timestamptz
198− , array_timestamptzOid
199− , array_interval
200− , array_intervalOid
201− , array_numeric
202− , array_numericOid
203− , array_timetz
204− , array_timetzOid
205− , array_bit
206− , array_bitOid
207− , array_varbit
208− , array_varbitOid
209− , array_refcursor
210− , array_refcursorOid
211− , array_regprocedure
212− , array_regprocedureOid
213− , array_regoper
214− , array_regoperOid
215− , array_regoperator
216− , array_regoperatorOid
217− , array_regclass
218− , array_regclassOid
219− , array_regtype
220− , array_regtypeOid
221− , array_uuid
222− , array_uuidOid
223− , array_jsonb
224− , array_jsonbOid
225− , int4range
226− , int4rangeOid
227− , _int4range
228− , _int4rangeOid
229− , numrange
230− , numrangeOid
231− , _numrange
232− , _numrangeOid
233− , tsrange
234− , tsrangeOid
235− , _tsrange
236− , _tsrangeOid
237− , tstzrange
238− , tstzrangeOid
239− , _tstzrange
240− , _tstzrangeOid
241− , daterange
242− , daterangeOid
243− , _daterange
244− , _daterangeOid
245− , int8range
246− , int8rangeOid
247− , _int8range
248− , _int8rangeOid
249− ) where
250−
251−import Database.PostgreSQL.LibPQ (Oid(..))
252−import Database.PostgreSQL.Simple.TypeInfo.Types
253−
254−staticTypeInfo :: Oid -> Maybe TypeInfo
255−staticTypeInfo (Oid x) = case x of
256− 16 -> Just bool
257− 17 -> Just bytea
258− 18 -> Just char
259− 19 -> Just name
260− 20 -> Just int8
261− 21 -> Just int2
262− 23 -> Just int4
263− 24 -> Just regproc
264− 25 -> Just text
265− 26 -> Just oid
266− 27 -> Just tid
267− 28 -> Just xid
268− 29 -> Just cid
269− 142 -> Just xml
270− 600 -> Just point
271− 601 -> Just lseg
272− 602 -> Just path
273− 603 -> Just box
274− 604 -> Just polygon
275− 628 -> Just line
276− 650 -> Just cidr
277− 700 -> Just float4
278− 701 -> Just float8
279− 705 -> Just unknown
280− 718 -> Just circle
281− 790 -> Just money
282− 829 -> Just macaddr
283− 869 -> Just inet
284− 1042 -> Just bpchar
285− 1043 -> Just varchar
286− 1082 -> Just date
287− 1083 -> Just time
288− 1114 -> Just timestamp
289− 1184 -> Just timestamptz
290− 1186 -> Just interval
291− 1266 -> Just timetz
292− 1560 -> Just bit
293− 1562 -> Just varbit
294− 1700 -> Just numeric
295− 1790 -> Just refcursor
296− 2249 -> Just record
297− 2278 -> Just void
298− 2287 -> Just array_record
299− 2202 -> Just regprocedure
300− 2203 -> Just regoper
301− 2204 -> Just regoperator
302− 2205 -> Just regclass
303− 2206 -> Just regtype
304− 2950 -> Just uuid
305− 114 -> Just json
306− 3802 -> Just jsonb
307− 22 -> Just int2vector
308− 30 -> Just oidvector
309− 143 -> Just array_xml
310− 199 -> Just array_json
311− 629 -> Just array_line
312− 651 -> Just array_cidr
313− 719 -> Just array_circle
314− 791 -> Just array_money
315− 1000 -> Just array_bool
316− 1001 -> Just array_bytea
317− 1002 -> Just array_char
318− 1003 -> Just array_name
319− 1005 -> Just array_int2
320− 1006 -> Just array_int2vector
321− 1007 -> Just array_int4
322− 1008 -> Just array_regproc
323− 1009 -> Just array_text
324− 1010 -> Just array_tid
325− 1011 -> Just array_xid
326− 1012 -> Just array_cid
327− 1013 -> Just array_oidvector
328− 1014 -> Just array_bpchar
329− 1015 -> Just array_varchar
330− 1016 -> Just array_int8
331− 1017 -> Just array_point
332− 1018 -> Just array_lseg
333− 1019 -> Just array_path
334− 1020 -> Just array_box
335− 1021 -> Just array_float4
336− 1022 -> Just array_float8
337− 1027 -> Just array_polygon
338− 1028 -> Just array_oid
339− 1040 -> Just array_macaddr
340− 1041 -> Just array_inet
341− 1115 -> Just array_timestamp
342− 1182 -> Just array_date
343− 1183 -> Just array_time
344− 1185 -> Just array_timestamptz
345− 1187 -> Just array_interval
346− 1231 -> Just array_numeric
347− 1270 -> Just array_timetz
348− 1561 -> Just array_bit
349− 1563 -> Just array_varbit
350− 2201 -> Just array_refcursor
351− 2207 -> Just array_regprocedure
352− 2208 -> Just array_regoper
353− 2209 -> Just array_regoperator
354− 2210 -> Just array_regclass
355− 2211 -> Just array_regtype
356− 2951 -> Just array_uuid
357− 3807 -> Just array_jsonb
358− 3904 -> Just int4range
359− 3905 -> Just _int4range
360− 3906 -> Just numrange
361− 3907 -> Just _numrange
362− 3908 -> Just tsrange
363− 3909 -> Just _tsrange
364− 3910 -> Just tstzrange
365− 3911 -> Just _tstzrange
366− 3912 -> Just daterange
367− 3913 -> Just _daterange
368− 3926 -> Just int8range
369− 3927 -> Just _int8range
370− _ -> Nothing
371−
372−bool :: TypeInfo
373−bool = Basic {
374− typoid = boolOid,
375− typcategory = 'B',
376− typdelim = ',',
377− typname = "bool"
378− }
379−
380−boolOid :: Oid
381−boolOid = Oid 16
382−{-# INLINE boolOid #-}
383−
384−bytea :: TypeInfo
385−bytea = Basic {
386− typoid = byteaOid,
387− typcategory = 'U',
388− typdelim = ',',
389− typname = "bytea"
390− }
391−
392−byteaOid :: Oid
393−byteaOid = Oid 17
394−{-# INLINE byteaOid #-}
395−
396−char :: TypeInfo
397−char = Basic {
398− typoid = charOid,
399− typcategory = 'S',
400− typdelim = ',',
401− typname = "char"
402− }
403−
404−charOid :: Oid
405−charOid = Oid 18
406−{-# INLINE charOid #-}
407−
408−name :: TypeInfo
409−name = Basic {
410− typoid = nameOid,
411− typcategory = 'S',
412− typdelim = ',',
413− typname = "name"
414− }
415−
416−nameOid :: Oid
417−nameOid = Oid 19
418−{-# INLINE nameOid #-}
419−
420−int8 :: TypeInfo
421−int8 = Basic {
422− typoid = int8Oid,
423− typcategory = 'N',
424− typdelim = ',',
425− typname = "int8"
426− }
427−
428−int8Oid :: Oid
429−int8Oid = Oid 20
430−{-# INLINE int8Oid #-}
431−
432−int2 :: TypeInfo
433−int2 = Basic {
434− typoid = int2Oid,
435− typcategory = 'N',
436− typdelim = ',',
437− typname = "int2"
438− }
439−
440−int2Oid :: Oid
441−int2Oid = Oid 21
442−{-# INLINE int2Oid #-}
443−
444−int4 :: TypeInfo
445−int4 = Basic {
446− typoid = int4Oid,
447− typcategory = 'N',
448− typdelim = ',',
449− typname = "int4"
450− }
451−
452−int4Oid :: Oid
453−int4Oid = Oid 23
454−{-# INLINE int4Oid #-}
455−
456−regproc :: TypeInfo
457−regproc = Basic {
458− typoid = regprocOid,
459− typcategory = 'N',
460− typdelim = ',',
461− typname = "regproc"
462− }
463−
464−regprocOid :: Oid
465−regprocOid = Oid 24
466−{-# INLINE regprocOid #-}
467−
468−text :: TypeInfo
469−text = Basic {
470− typoid = textOid,
471− typcategory = 'S',
472− typdelim = ',',
473− typname = "text"
474− }
475−
476−textOid :: Oid
477−textOid = Oid 25
478−{-# INLINE textOid #-}
479−
480−oid :: TypeInfo
481−oid = Basic {
482− typoid = oidOid,
483− typcategory = 'N',
484− typdelim = ',',
485− typname = "oid"
486− }
487−
488−oidOid :: Oid
489−oidOid = Oid 26
490−{-# INLINE oidOid #-}
491−
492−tid :: TypeInfo
493−tid = Basic {
494− typoid = tidOid,
495− typcategory = 'U',
496− typdelim = ',',
497− typname = "tid"
498− }
499−
500−tidOid :: Oid
501−tidOid = Oid 27
502−{-# INLINE tidOid #-}
503−
504−xid :: TypeInfo
505−xid = Basic {
506− typoid = xidOid,
507− typcategory = 'U',
508− typdelim = ',',
509− typname = "xid"
510− }
511−
512−xidOid :: Oid
513−xidOid = Oid 28
514−{-# INLINE xidOid #-}
515−
516−cid :: TypeInfo
517−cid = Basic {
518− typoid = cidOid,
519− typcategory = 'U',
520− typdelim = ',',
521− typname = "cid"
522− }
523−
524−cidOid :: Oid
525−cidOid = Oid 29
526−{-# INLINE cidOid #-}
527−
528−xml :: TypeInfo
529−xml = Basic {
530− typoid = xmlOid,
531− typcategory = 'U',
532− typdelim = ',',
533− typname = "xml"
534− }
535−
536−xmlOid :: Oid
537−xmlOid = Oid 142
538−{-# INLINE xmlOid #-}
539−
540−point :: TypeInfo
541−point = Basic {
542− typoid = pointOid,
543− typcategory = 'G',
544− typdelim = ',',
545− typname = "point"
546− }
547−
548−pointOid :: Oid
549−pointOid = Oid 600
550−{-# INLINE pointOid #-}
551−
552−lseg :: TypeInfo
553−lseg = Basic {
554− typoid = lsegOid,
555− typcategory = 'G',
556− typdelim = ',',
557− typname = "lseg"
558− }
559−
560−lsegOid :: Oid
561−lsegOid = Oid 601
562−{-# INLINE lsegOid #-}
563−
564−path :: TypeInfo
565−path = Basic {
566− typoid = pathOid,
567− typcategory = 'G',
568− typdelim = ',',
569− typname = "path"
570− }
571−
572−pathOid :: Oid
573−pathOid = Oid 602
574−{-# INLINE pathOid #-}
575−
576−box :: TypeInfo
577−box = Basic {
578− typoid = boxOid,
579− typcategory = 'G',
580− typdelim = ';',
581− typname = "box"
582− }
583−
584−boxOid :: Oid
585−boxOid = Oid 603
586−{-# INLINE boxOid #-}
587−
588−polygon :: TypeInfo
589−polygon = Basic {
590− typoid = polygonOid,
591− typcategory = 'G',
592− typdelim = ',',
593− typname = "polygon"
594− }
595−
596−polygonOid :: Oid
597−polygonOid = Oid 604
598−{-# INLINE polygonOid #-}
599−
600−line :: TypeInfo
601−line = Basic {
602− typoid = lineOid,
603− typcategory = 'G',
604− typdelim = ',',
605− typname = "line"
606− }
607−
608−lineOid :: Oid
609−lineOid = Oid 628
610−{-# INLINE lineOid #-}
611−
612−cidr :: TypeInfo
613−cidr = Basic {
614− typoid = cidrOid,
615− typcategory = 'I',
616− typdelim = ',',
617− typname = "cidr"
618− }
619−
620−cidrOid :: Oid
621−cidrOid = Oid 650
622−{-# INLINE cidrOid #-}
623−
624−float4 :: TypeInfo
625−float4 = Basic {
626− typoid = float4Oid,
627− typcategory = 'N',
628− typdelim = ',',
629− typname = "float4"
630− }
631−
632−float4Oid :: Oid
633−float4Oid = Oid 700
634−{-# INLINE float4Oid #-}
635−
636−float8 :: TypeInfo
637−float8 = Basic {
638− typoid = float8Oid,
639− typcategory = 'N',
640− typdelim = ',',
641− typname = "float8"
642− }
643−
644−float8Oid :: Oid
645−float8Oid = Oid 701
646−{-# INLINE float8Oid #-}
647−
648−unknown :: TypeInfo
649−unknown = Basic {
650− typoid = unknownOid,
651− typcategory = 'X',
652− typdelim = ',',
653− typname = "unknown"
654− }
655−
656−unknownOid :: Oid
657−unknownOid = Oid 705
658−{-# INLINE unknownOid #-}
659−
660−circle :: TypeInfo
661−circle = Basic {
662− typoid = circleOid,
663− typcategory = 'G',
664− typdelim = ',',
665− typname = "circle"
666− }
667−
668−circleOid :: Oid
669−circleOid = Oid 718
670−{-# INLINE circleOid #-}
671−
672−money :: TypeInfo
673−money = Basic {
674− typoid = moneyOid,
675− typcategory = 'N',
676− typdelim = ',',
677− typname = "money"
678− }
679−
680−moneyOid :: Oid
681−moneyOid = Oid 790
682−{-# INLINE moneyOid #-}
683−
684−macaddr :: TypeInfo
685−macaddr = Basic {
686− typoid = macaddrOid,
687− typcategory = 'U',
688− typdelim = ',',
689− typname = "macaddr"
690− }
691−
692−macaddrOid :: Oid
693−macaddrOid = Oid 829
694−{-# INLINE macaddrOid #-}
695−
696−inet :: TypeInfo
697−inet = Basic {
698− typoid = inetOid,
699− typcategory = 'I',
700− typdelim = ',',
701− typname = "inet"
702− }
703−
704−inetOid :: Oid
705−inetOid = Oid 869
706−{-# INLINE inetOid #-}
707−
708−bpchar :: TypeInfo
709−bpchar = Basic {
710− typoid = bpcharOid,
711− typcategory = 'S',
712− typdelim = ',',
713− typname = "bpchar"
714− }
715−
716−bpcharOid :: Oid
717−bpcharOid = Oid 1042
718−{-# INLINE bpcharOid #-}
719−
720−varchar :: TypeInfo
721−varchar = Basic {
722− typoid = varcharOid,
723− typcategory = 'S',
724− typdelim = ',',
725− typname = "varchar"
726− }
727−
728−varcharOid :: Oid
729−varcharOid = Oid 1043
730−{-# INLINE varcharOid #-}
731−
732−date :: TypeInfo
733−date = Basic {
734− typoid = dateOid,
735− typcategory = 'D',
736− typdelim = ',',
737− typname = "date"
738− }
739−
740−dateOid :: Oid
741−dateOid = Oid 1082
742−{-# INLINE dateOid #-}
743−
744−time :: TypeInfo
745−time = Basic {
746− typoid = timeOid,
747− typcategory = 'D',
748− typdelim = ',',
749− typname = "time"
750− }
751−
752−timeOid :: Oid
753−timeOid = Oid 1083
754−{-# INLINE timeOid #-}
755−
756−timestamp :: TypeInfo
757−timestamp = Basic {
758− typoid = timestampOid,
759− typcategory = 'D',
760− typdelim = ',',
761− typname = "timestamp"
762− }
763−
764−timestampOid :: Oid
765−timestampOid = Oid 1114
766−{-# INLINE timestampOid #-}
767−
768−timestamptz :: TypeInfo
769−timestamptz = Basic {
770− typoid = timestamptzOid,
771− typcategory = 'D',
772− typdelim = ',',
773− typname = "timestamptz"
774− }
775−
776−timestamptzOid :: Oid
777−timestamptzOid = Oid 1184
778−{-# INLINE timestamptzOid #-}
779−
780−interval :: TypeInfo
781−interval = Basic {
782− typoid = intervalOid,
783− typcategory = 'T',
784− typdelim = ',',
785− typname = "interval"
786− }
787−
788−intervalOid :: Oid
789−intervalOid = Oid 1186
790−{-# INLINE intervalOid #-}
791−
792−timetz :: TypeInfo
793−timetz = Basic {
794− typoid = timetzOid,
795− typcategory = 'D',
796− typdelim = ',',
797− typname = "timetz"
798− }
799−
800−timetzOid :: Oid
801−timetzOid = Oid 1266
802−{-# INLINE timetzOid #-}
803−
804−bit :: TypeInfo
805−bit = Basic {
806− typoid = bitOid,
807− typcategory = 'V',
808− typdelim = ',',
809− typname = "bit"
810− }
811−
812−bitOid :: Oid
813−bitOid = Oid 1560
814−{-# INLINE bitOid #-}
815−
816−varbit :: TypeInfo
817−varbit = Basic {
818− typoid = varbitOid,
819− typcategory = 'V',
820− typdelim = ',',
821− typname = "varbit"
822− }
823−
824−varbitOid :: Oid
825−varbitOid = Oid 1562
826−{-# INLINE varbitOid #-}
827−
828−numeric :: TypeInfo
829−numeric = Basic {
830− typoid = numericOid,
831− typcategory = 'N',
832− typdelim = ',',
833− typname = "numeric"
834− }
835−
836−numericOid :: Oid
837−numericOid = Oid 1700
838−{-# INLINE numericOid #-}
839−
840−refcursor :: TypeInfo
841−refcursor = Basic {
842− typoid = refcursorOid,
843− typcategory = 'U',
844− typdelim = ',',
845− typname = "refcursor"
846− }
847−
848−refcursorOid :: Oid
849−refcursorOid = Oid 1790
850−{-# INLINE refcursorOid #-}
851−
852−record :: TypeInfo
853−record = Basic {
854− typoid = recordOid,
855− typcategory = 'P',
856− typdelim = ',',
857− typname = "record"
858− }
859−
860−recordOid :: Oid
861−recordOid = Oid 2249
862−{-# INLINE recordOid #-}
863−
864−void :: TypeInfo
865−void = Basic {
866− typoid = voidOid,
867− typcategory = 'P',
868− typdelim = ',',
869− typname = "void"
870− }
871−
872−voidOid :: Oid
873−voidOid = Oid 2278
874−{-# INLINE voidOid #-}
875−
876−array_record :: TypeInfo
877−array_record = Array {
878− typoid = array_recordOid,
879− typcategory = 'P',
880− typdelim = ',',
881− typname = "_record",
882− typelem = record
883− }
884−
885−array_recordOid :: Oid
886−array_recordOid = Oid 2287
887−{-# INLINE array_recordOid #-}
888−
889−regprocedure :: TypeInfo
890−regprocedure = Basic {
891− typoid = regprocedureOid,
892− typcategory = 'N',
893− typdelim = ',',
894− typname = "regprocedure"
895− }
896−
897−regprocedureOid :: Oid
898−regprocedureOid = Oid 2202
899−{-# INLINE regprocedureOid #-}
900−
901−regoper :: TypeInfo
902−regoper = Basic {
903− typoid = regoperOid,
904− typcategory = 'N',
905− typdelim = ',',
906− typname = "regoper"
907− }
908−
909−regoperOid :: Oid
910−regoperOid = Oid 2203
911−{-# INLINE regoperOid #-}
912−
913−regoperator :: TypeInfo
914−regoperator = Basic {
915− typoid = regoperatorOid,
916− typcategory = 'N',
917− typdelim = ',',
918− typname = "regoperator"
919− }
920−
921−regoperatorOid :: Oid
922−regoperatorOid = Oid 2204
923−{-# INLINE regoperatorOid #-}
924−
925−regclass :: TypeInfo
926−regclass = Basic {
927− typoid = regclassOid,
928− typcategory = 'N',
929− typdelim = ',',
930− typname = "regclass"
931− }
932−
933−regclassOid :: Oid
934−regclassOid = Oid 2205
935−{-# INLINE regclassOid #-}
936−
937−regtype :: TypeInfo
938−regtype = Basic {
939− typoid = regtypeOid,
940− typcategory = 'N',
941− typdelim = ',',
942− typname = "regtype"
943− }
944−
945−regtypeOid :: Oid
946−regtypeOid = Oid 2206
947−{-# INLINE regtypeOid #-}
948−
949−uuid :: TypeInfo
950−uuid = Basic {
951− typoid = uuidOid,
952− typcategory = 'U',
953− typdelim = ',',
954− typname = "uuid"
955− }
956−
957−uuidOid :: Oid
958−uuidOid = Oid 2950
959−{-# INLINE uuidOid #-}
960−
961−json :: TypeInfo
962−json = Basic {
963− typoid = jsonOid,
964− typcategory = 'U',
965− typdelim = ',',
966− typname = "json"
967− }
968−
969−jsonOid :: Oid
970−jsonOid = Oid 114
971−{-# INLINE jsonOid #-}
972−
973−jsonb :: TypeInfo
974−jsonb = Basic {
975− typoid = jsonbOid,
976− typcategory = 'U',
977− typdelim = ',',
978− typname = "jsonb"
979− }
980−
981−jsonbOid :: Oid
982−jsonbOid = Oid 3802
983−{-# INLINE jsonbOid #-}
984−
985−int2vector :: TypeInfo
986−int2vector = Array {
987− typoid = int2vectorOid,
988− typcategory = 'A',
989− typdelim = ',',
990− typname = "int2vector",
991− typelem = int2
992− }
993−
994−int2vectorOid :: Oid
995−int2vectorOid = Oid 22
996−{-# INLINE int2vectorOid #-}
997−
998−oidvector :: TypeInfo
999−oidvector = Array {
1000− typoid = oidvectorOid,
1001− typcategory = 'A',
1002− typdelim = ',',
1003− typname = "oidvector",
1004− typelem = oid
1005− }
1006−
1007−oidvectorOid :: Oid
1008−oidvectorOid = Oid 30
1009−{-# INLINE oidvectorOid #-}
1010−
1011−array_xml :: TypeInfo
1012−array_xml = Array {
1013− typoid = array_xmlOid,
1014− typcategory = 'A',
1015− typdelim = ',',
1016− typname = "_xml",
1017− typelem = xml
1018− }
1019−
1020−array_xmlOid :: Oid
1021−array_xmlOid = Oid 143
1022−{-# INLINE array_xmlOid #-}
1023−
1024−array_json :: TypeInfo
1025−array_json = Array {
1026− typoid = array_jsonOid,
1027− typcategory = 'A',
1028− typdelim = ',',
1029− typname = "_json",
1030− typelem = json
1031− }
1032−
1033−array_jsonOid :: Oid
1034−array_jsonOid = Oid 199
1035−{-# INLINE array_jsonOid #-}
1036−
1037−array_line :: TypeInfo
1038−array_line = Array {
1039− typoid = array_lineOid,
1040− typcategory = 'A',
1041− typdelim = ',',
1042− typname = "_line",
1043− typelem = line
1044− }
1045−
1046−array_lineOid :: Oid
1047−array_lineOid = Oid 629
1048−{-# INLINE array_lineOid #-}
1049−
1050−array_cidr :: TypeInfo
1051−array_cidr = Array {
1052− typoid = array_cidrOid,
1053− typcategory = 'A',
1054− typdelim = ',',
1055− typname = "_cidr",
1056− typelem = cidr
1057− }
1058−
1059−array_cidrOid :: Oid
1060−array_cidrOid = Oid 651
1061−{-# INLINE array_cidrOid #-}
1062−
1063−array_circle :: TypeInfo
1064−array_circle = Array {
1065− typoid = array_circleOid,
1066− typcategory = 'A',
1067− typdelim = ',',
1068− typname = "_circle",
1069− typelem = circle
1070− }
1071−
1072−array_circleOid :: Oid
1073−array_circleOid = Oid 719
1074−{-# INLINE array_circleOid #-}
1075−
1076−array_money :: TypeInfo
1077−array_money = Array {
1078− typoid = array_moneyOid,
1079− typcategory = 'A',
1080− typdelim = ',',
1081− typname = "_money",
1082− typelem = money
1083− }
1084−
1085−array_moneyOid :: Oid
1086−array_moneyOid = Oid 791
1087−{-# INLINE array_moneyOid #-}
1088−
1089−array_bool :: TypeInfo
1090−array_bool = Array {
1091− typoid = array_boolOid,
1092− typcategory = 'A',
1093− typdelim = ',',
1094− typname = "_bool",
1095− typelem = bool
1096− }
1097−
1098−array_boolOid :: Oid
1099−array_boolOid = Oid 1000
1100−{-# INLINE array_boolOid #-}
1101−
1102−array_bytea :: TypeInfo
1103−array_bytea = Array {
1104− typoid = array_byteaOid,
1105− typcategory = 'A',
1106− typdelim = ',',
1107− typname = "_bytea",
1108− typelem = bytea
1109− }
1110−
1111−array_byteaOid :: Oid
1112−array_byteaOid = Oid 1001
1113−{-# INLINE array_byteaOid #-}
1114−
1115−array_char :: TypeInfo
1116−array_char = Array {
1117− typoid = array_charOid,
1118− typcategory = 'A',
1119− typdelim = ',',
1120− typname = "_char",
1121− typelem = char
1122− }
1123−
1124−array_charOid :: Oid
1125−array_charOid = Oid 1002
1126−{-# INLINE array_charOid #-}
1127−
1128−array_name :: TypeInfo
1129−array_name = Array {
1130− typoid = array_nameOid,
1131− typcategory = 'A',
1132− typdelim = ',',
1133− typname = "_name",
1134− typelem = name
1135− }
1136−
1137−array_nameOid :: Oid
1138−array_nameOid = Oid 1003
1139−{-# INLINE array_nameOid #-}
1140−
1141−array_int2 :: TypeInfo
1142−array_int2 = Array {
1143− typoid = array_int2Oid,
1144− typcategory = 'A',
1145− typdelim = ',',
1146− typname = "_int2",
1147− typelem = int2
1148− }
1149−
1150−array_int2Oid :: Oid
1151−array_int2Oid = Oid 1005
1152−{-# INLINE array_int2Oid #-}
1153−
1154−array_int2vector :: TypeInfo
1155−array_int2vector = Array {
1156− typoid = array_int2vectorOid,
1157− typcategory = 'A',
1158− typdelim = ',',
1159− typname = "_int2vector",
1160− typelem = int2vector
1161− }
1162−
1163−array_int2vectorOid :: Oid
1164−array_int2vectorOid = Oid 1006
1165−{-# INLINE array_int2vectorOid #-}
1166−
1167−array_int4 :: TypeInfo
1168−array_int4 = Array {
1169− typoid = array_int4Oid,
1170− typcategory = 'A',
1171− typdelim = ',',
1172− typname = "_int4",
1173− typelem = int4
1174− }
1175−
1176−array_int4Oid :: Oid
1177−array_int4Oid = Oid 1007
1178−{-# INLINE array_int4Oid #-}
1179−
1180−array_regproc :: TypeInfo
1181−array_regproc = Array {
1182− typoid = array_regprocOid,
1183− typcategory = 'A',
1184− typdelim = ',',
1185− typname = "_regproc",
1186− typelem = regproc
1187− }
1188−
1189−array_regprocOid :: Oid
1190−array_regprocOid = Oid 1008
1191−{-# INLINE array_regprocOid #-}
1192−
1193−array_text :: TypeInfo
1194−array_text = Array {
1195− typoid = array_textOid,
1196− typcategory = 'A',
1197− typdelim = ',',
1198− typname = "_text",
1199− typelem = text
1200− }
1201−
1202−array_textOid :: Oid
1203−array_textOid = Oid 1009
1204−{-# INLINE array_textOid #-}
1205−
1206−array_tid :: TypeInfo
1207−array_tid = Array {
1208− typoid = array_tidOid,
1209− typcategory = 'A',
1210− typdelim = ',',
1211− typname = "_tid",
1212− typelem = tid
1213− }
1214−
1215−array_tidOid :: Oid
1216−array_tidOid = Oid 1010
1217−{-# INLINE array_tidOid #-}
1218−
1219−array_xid :: TypeInfo
1220−array_xid = Array {
1221− typoid = array_xidOid,
1222− typcategory = 'A',
1223− typdelim = ',',
1224− typname = "_xid",
1225− typelem = xid
1226− }
1227−
1228−array_xidOid :: Oid
1229−array_xidOid = Oid 1011
1230−{-# INLINE array_xidOid #-}
1231−
1232−array_cid :: TypeInfo
1233−array_cid = Array {
1234− typoid = array_cidOid,
1235− typcategory = 'A',
1236− typdelim = ',',
1237− typname = "_cid",
1238− typelem = cid
1239− }
1240−
1241−array_cidOid :: Oid
1242−array_cidOid = Oid 1012
1243−{-# INLINE array_cidOid #-}
1244−
1245−array_oidvector :: TypeInfo
1246−array_oidvector = Array {
1247− typoid = array_oidvectorOid,
1248− typcategory = 'A',
1249− typdelim = ',',
1250− typname = "_oidvector",
1251− typelem = oidvector
1252− }
1253−
1254−array_oidvectorOid :: Oid
1255−array_oidvectorOid = Oid 1013
1256−{-# INLINE array_oidvectorOid #-}
1257−
1258−array_bpchar :: TypeInfo
1259−array_bpchar = Array {
1260− typoid = array_bpcharOid,
1261− typcategory = 'A',
1262− typdelim = ',',
1263− typname = "_bpchar",
1264− typelem = bpchar
1265− }
1266−
1267−array_bpcharOid :: Oid
1268−array_bpcharOid = Oid 1014
1269−{-# INLINE array_bpcharOid #-}
1270−
1271−array_varchar :: TypeInfo
1272−array_varchar = Array {
1273− typoid = array_varcharOid,
1274− typcategory = 'A',
1275− typdelim = ',',
1276− typname = "_varchar",
1277− typelem = varchar
1278− }
1279−
1280−array_varcharOid :: Oid
1281−array_varcharOid = Oid 1015
1282−{-# INLINE array_varcharOid #-}
1283−
1284−array_int8 :: TypeInfo
1285−array_int8 = Array {
1286− typoid = array_int8Oid,
1287− typcategory = 'A',
1288− typdelim = ',',
1289− typname = "_int8",
1290− typelem = int8
1291− }
1292−
1293−array_int8Oid :: Oid
1294−array_int8Oid = Oid 1016
1295−{-# INLINE array_int8Oid #-}
1296−
1297−array_point :: TypeInfo
1298−array_point = Array {
1299− typoid = array_pointOid,
1300− typcategory = 'A',
1301− typdelim = ',',
1302− typname = "_point",
1303− typelem = point
1304− }
1305−
1306−array_pointOid :: Oid
1307−array_pointOid = Oid 1017
1308−{-# INLINE array_pointOid #-}
1309−
1310−array_lseg :: TypeInfo
1311−array_lseg = Array {
1312− typoid = array_lsegOid,
1313− typcategory = 'A',
1314− typdelim = ',',
1315− typname = "_lseg",
1316− typelem = lseg
1317− }
1318−
1319−array_lsegOid :: Oid
1320−array_lsegOid = Oid 1018
1321−{-# INLINE array_lsegOid #-}
1322−
1323−array_path :: TypeInfo
1324−array_path = Array {
1325− typoid = array_pathOid,
1326− typcategory = 'A',
1327− typdelim = ',',
1328− typname = "_path",
1329− typelem = path
1330− }
1331−
1332−array_pathOid :: Oid
1333−array_pathOid = Oid 1019
1334−{-# INLINE array_pathOid #-}
1335−
1336−array_box :: TypeInfo
1337−array_box = Array {
1338− typoid = array_boxOid,
1339− typcategory = 'A',
1340− typdelim = ';',
1341− typname = "_box",
1342− typelem = box
1343− }
1344−
1345−array_boxOid :: Oid
1346−array_boxOid = Oid 1020
1347−{-# INLINE array_boxOid #-}
1348−
1349−array_float4 :: TypeInfo
1350−array_float4 = Array {
1351− typoid = array_float4Oid,
1352− typcategory = 'A',
1353− typdelim = ',',
1354− typname = "_float4",
1355− typelem = float4
1356− }
1357−
1358−array_float4Oid :: Oid
1359−array_float4Oid = Oid 1021
1360−{-# INLINE array_float4Oid #-}
1361−
1362−array_float8 :: TypeInfo
1363−array_float8 = Array {
1364− typoid = array_float8Oid,
1365− typcategory = 'A',
1366− typdelim = ',',
1367− typname = "_float8",
1368− typelem = float8
1369− }
1370−
1371−array_float8Oid :: Oid
1372−array_float8Oid = Oid 1022
1373−{-# INLINE array_float8Oid #-}
1374−
1375−array_polygon :: TypeInfo
1376−array_polygon = Array {
1377− typoid = array_polygonOid,
1378− typcategory = 'A',
1379− typdelim = ',',
1380− typname = "_polygon",
1381− typelem = polygon
1382− }
1383−
1384−array_polygonOid :: Oid
1385−array_polygonOid = Oid 1027
1386−{-# INLINE array_polygonOid #-}
1387−
1388−array_oid :: TypeInfo
1389−array_oid = Array {
1390− typoid = array_oidOid,
1391− typcategory = 'A',
1392− typdelim = ',',
1393− typname = "_oid",
1394− typelem = oid
1395− }
1396−
1397−array_oidOid :: Oid
1398−array_oidOid = Oid 1028
1399−{-# INLINE array_oidOid #-}
1400−
1401−array_macaddr :: TypeInfo
1402−array_macaddr = Array {
1403− typoid = array_macaddrOid,
1404− typcategory = 'A',
1405− typdelim = ',',
1406− typname = "_macaddr",
1407− typelem = macaddr
1408− }
1409−
1410−array_macaddrOid :: Oid
1411−array_macaddrOid = Oid 1040
1412−{-# INLINE array_macaddrOid #-}
1413−
1414−array_inet :: TypeInfo
1415−array_inet = Array {
1416− typoid = array_inetOid,
1417− typcategory = 'A',
1418− typdelim = ',',
1419− typname = "_inet",
1420− typelem = inet
1421− }
1422−
1423−array_inetOid :: Oid
1424−array_inetOid = Oid 1041
1425−{-# INLINE array_inetOid #-}
1426−
1427−array_timestamp :: TypeInfo
1428−array_timestamp = Array {
1429− typoid = array_timestampOid,
1430− typcategory = 'A',
1431− typdelim = ',',
1432− typname = "_timestamp",
1433− typelem = timestamp
1434− }
1435−
1436−array_timestampOid :: Oid
1437−array_timestampOid = Oid 1115
1438−{-# INLINE array_timestampOid #-}
1439−
1440−array_date :: TypeInfo
1441−array_date = Array {
1442− typoid = array_dateOid,
1443− typcategory = 'A',
1444− typdelim = ',',
1445− typname = "_date",
1446− typelem = date
1447− }
1448−
1449−array_dateOid :: Oid
1450−array_dateOid = Oid 1182
1451−{-# INLINE array_dateOid #-}
1452−
1453−array_time :: TypeInfo
1454−array_time = Array {
1455− typoid = array_timeOid,
1456− typcategory = 'A',
1457− typdelim = ',',
1458− typname = "_time",
1459− typelem = time
1460− }
1461−
1462−array_timeOid :: Oid
1463−array_timeOid = Oid 1183
1464−{-# INLINE array_timeOid #-}
1465−
1466−array_timestamptz :: TypeInfo
1467−array_timestamptz = Array {
1468− typoid = array_timestamptzOid,
1469− typcategory = 'A',
1470− typdelim = ',',
1471− typname = "_timestamptz",
1472− typelem = timestamptz
1473− }
1474−
1475−array_timestamptzOid :: Oid
1476−array_timestamptzOid = Oid 1185
1477−{-# INLINE array_timestamptzOid #-}
1478−
1479−array_interval :: TypeInfo
1480−array_interval = Array {
1481− typoid = array_intervalOid,
1482− typcategory = 'A',
1483− typdelim = ',',
1484− typname = "_interval",
1485− typelem = interval
1486− }
1487−
1488−array_intervalOid :: Oid
1489−array_intervalOid = Oid 1187
1490−{-# INLINE array_intervalOid #-}
1491−
1492−array_numeric :: TypeInfo
1493−array_numeric = Array {
1494− typoid = array_numericOid,
1495− typcategory = 'A',
1496− typdelim = ',',
1497− typname = "_numeric",
1498− typelem = numeric
1499− }
1500−
1501−array_numericOid :: Oid
1502−array_numericOid = Oid 1231
1503−{-# INLINE array_numericOid #-}
1504−
1505−array_timetz :: TypeInfo
1506−array_timetz = Array {
1507− typoid = array_timetzOid,
1508− typcategory = 'A',
1509− typdelim = ',',
1510− typname = "_timetz",
1511− typelem = timetz
1512− }
1513−
1514−array_timetzOid :: Oid
1515−array_timetzOid = Oid 1270
1516−{-# INLINE array_timetzOid #-}
1517−
1518−array_bit :: TypeInfo
1519−array_bit = Array {
1520− typoid = array_bitOid,
1521− typcategory = 'A',
1522− typdelim = ',',
1523− typname = "_bit",
1524− typelem = bit
1525− }
1526−
1527−array_bitOid :: Oid
1528−array_bitOid = Oid 1561
1529−{-# INLINE array_bitOid #-}
1530−
1531−array_varbit :: TypeInfo
1532−array_varbit = Array {
1533− typoid = array_varbitOid,
1534− typcategory = 'A',
1535− typdelim = ',',
1536− typname = "_varbit",
1537− typelem = varbit
1538− }
1539−
1540−array_varbitOid :: Oid
1541−array_varbitOid = Oid 1563
1542−{-# INLINE array_varbitOid #-}
1543−
1544−array_refcursor :: TypeInfo
1545−array_refcursor = Array {
1546− typoid = array_refcursorOid,
1547− typcategory = 'A',
1548− typdelim = ',',
1549− typname = "_refcursor",
1550− typelem = refcursor
1551− }
1552−
1553−array_refcursorOid :: Oid
1554−array_refcursorOid = Oid 2201
1555−{-# INLINE array_refcursorOid #-}
1556−
1557−array_regprocedure :: TypeInfo
1558−array_regprocedure = Array {
1559− typoid = array_regprocedureOid,
1560− typcategory = 'A',
1561− typdelim = ',',
1562− typname = "_regprocedure",
1563− typelem = regprocedure
1564− }
1565−
1566−array_regprocedureOid :: Oid
1567−array_regprocedureOid = Oid 2207
1568−{-# INLINE array_regprocedureOid #-}
1569−
1570−array_regoper :: TypeInfo
1571−array_regoper = Array {
1572− typoid = array_regoperOid,
1573− typcategory = 'A',
1574− typdelim = ',',
1575− typname = "_regoper",
1576− typelem = regoper
1577− }
1578−
1579−array_regoperOid :: Oid
1580−array_regoperOid = Oid 2208
1581−{-# INLINE array_regoperOid #-}
1582−
1583−array_regoperator :: TypeInfo
1584−array_regoperator = Array {
1585− typoid = array_regoperatorOid,
1586− typcategory = 'A',
1587− typdelim = ',',
1588− typname = "_regoperator",
1589− typelem = regoperator
1590− }
1591−
1592−array_regoperatorOid :: Oid
1593−array_regoperatorOid = Oid 2209
1594−{-# INLINE array_regoperatorOid #-}
1595−
1596−array_regclass :: TypeInfo
1597−array_regclass = Array {
1598− typoid = array_regclassOid,
1599− typcategory = 'A',
1600− typdelim = ',',
1601− typname = "_regclass",
1602− typelem = regclass
1603− }
1604−
1605−array_regclassOid :: Oid
1606−array_regclassOid = Oid 2210
1607−{-# INLINE array_regclassOid #-}
1608−
1609−array_regtype :: TypeInfo
1610−array_regtype = Array {
1611− typoid = array_regtypeOid,
1612− typcategory = 'A',
1613− typdelim = ',',
1614− typname = "_regtype",
1615− typelem = regtype
1616− }
1617−
1618−array_regtypeOid :: Oid
1619−array_regtypeOid = Oid 2211
1620−{-# INLINE array_regtypeOid #-}
1621−
1622−array_uuid :: TypeInfo
1623−array_uuid = Array {
1624− typoid = array_uuidOid,
1625− typcategory = 'A',
1626− typdelim = ',',
1627− typname = "_uuid",
1628− typelem = uuid
1629− }
1630−
1631−array_uuidOid :: Oid
1632−array_uuidOid = Oid 2951
1633−{-# INLINE array_uuidOid #-}
1634−
1635−array_jsonb :: TypeInfo
1636−array_jsonb = Array {
1637− typoid = array_jsonbOid,
1638− typcategory = 'A',
1639− typdelim = ',',
1640− typname = "_jsonb",
1641− typelem = jsonb
1642− }
1643−
1644−array_jsonbOid :: Oid
1645−array_jsonbOid = Oid 3807
1646−{-# INLINE array_jsonbOid #-}
1647−
1648−int4range :: TypeInfo
1649−int4range = Range {
1650− typoid = int4rangeOid,
1651− typcategory = 'R',
1652− typdelim = ',',
1653− typname = "int4range",
1654− rngsubtype = int4
1655− }
1656−
1657−int4rangeOid :: Oid
1658−int4rangeOid = Oid 3904
1659−{-# INLINE int4rangeOid #-}
1660−
1661−_int4range :: TypeInfo
1662−_int4range = Array {
1663− typoid = _int4rangeOid,
1664− typcategory = 'A',
1665− typdelim = ',',
1666− typname = "_int4range",
1667− typelem = int4range
1668− }
1669−
1670−_int4rangeOid :: Oid
1671−_int4rangeOid = Oid 3905
1672−{-# INLINE _int4rangeOid #-}
1673−
1674−numrange :: TypeInfo
1675−numrange = Range {
1676− typoid = numrangeOid,
1677− typcategory = 'R',
1678− typdelim = ',',
1679− typname = "numrange",
1680− rngsubtype = numeric
1681− }
1682−
1683−numrangeOid :: Oid
1684−numrangeOid = Oid 3906
1685−{-# INLINE numrangeOid #-}
1686−
1687−_numrange :: TypeInfo
1688−_numrange = Array {
1689− typoid = _numrangeOid,
1690− typcategory = 'A',
1691− typdelim = ',',
1692− typname = "_numrange",
1693− typelem = numrange
1694− }
1695−
1696−_numrangeOid :: Oid
1697−_numrangeOid = Oid 3907
1698−{-# INLINE _numrangeOid #-}
1699−
1700−tsrange :: TypeInfo
1701−tsrange = Range {
1702− typoid = tsrangeOid,
1703− typcategory = 'R',
1704− typdelim = ',',
1705− typname = "tsrange",
1706− rngsubtype = timestamp
1707− }
1708−
1709−tsrangeOid :: Oid
1710−tsrangeOid = Oid 3908
1711−{-# INLINE tsrangeOid #-}
1712−
1713−_tsrange :: TypeInfo
1714−_tsrange = Array {
1715− typoid = _tsrangeOid,
1716− typcategory = 'A',
1717− typdelim = ',',
1718− typname = "_tsrange",
1719− typelem = tsrange
1720− }
1721−
1722−_tsrangeOid :: Oid
1723−_tsrangeOid = Oid 3909
1724−{-# INLINE _tsrangeOid #-}
1725−
1726−tstzrange :: TypeInfo
1727−tstzrange = Range {
1728− typoid = tstzrangeOid,
1729− typcategory = 'R',
1730− typdelim = ',',
1731− typname = "tstzrange",
1732− rngsubtype = timestamptz
1733− }
1734−
1735−tstzrangeOid :: Oid
1736−tstzrangeOid = Oid 3910
1737−{-# INLINE tstzrangeOid #-}
1738−
1739−_tstzrange :: TypeInfo
1740−_tstzrange = Array {
1741− typoid = _tstzrangeOid,
1742− typcategory = 'A',
1743− typdelim = ',',
1744− typname = "_tstzrange",
1745− typelem = tstzrange
1746− }
1747−
1748−_tstzrangeOid :: Oid
1749−_tstzrangeOid = Oid 3911
1750−{-# INLINE _tstzrangeOid #-}
1751−
1752−daterange :: TypeInfo
1753−daterange = Range {
1754− typoid = daterangeOid,
1755− typcategory = 'R',
1756− typdelim = ',',
1757− typname = "daterange",
1758− rngsubtype = date
1759− }
1760−
1761−daterangeOid :: Oid
1762−daterangeOid = Oid 3912
1763−{-# INLINE daterangeOid #-}
1764−
1765−_daterange :: TypeInfo
1766−_daterange = Array {
1767− typoid = _daterangeOid,
1768− typcategory = 'A',
1769− typdelim = ',',
1770− typname = "_daterange",
1771− typelem = daterange
1772− }
1773−
1774−_daterangeOid :: Oid
1775−_daterangeOid = Oid 3913
1776−{-# INLINE _daterangeOid #-}
1777−
1778−int8range :: TypeInfo
1779−int8range = Range {
1780− typoid = int8rangeOid,
1781− typcategory = 'R',
1782− typdelim = ',',
1783− typname = "int8range",
1784− rngsubtype = int8
1785− }
1786−
1787−int8rangeOid :: Oid
1788−int8rangeOid = Oid 3926
1789−{-# INLINE int8rangeOid #-}
1790−
1791−_int8range :: TypeInfo
1792−_int8range = Array {
1793− typoid = _int8rangeOid,
1794− typcategory = 'A',
1795− typdelim = ',',
1796− typname = "_int8range",
1797− typelem = int8range
1798− }
1799−
1800−_int8rangeOid :: Oid
1801−_int8rangeOid = Oid 3927
1802−{-# INLINE _int8rangeOid #-}
src/Database/PostgreSQL/Simple/TypeInfo/Types.hsdeleted−56
@@ -1,56 +0,0 @@
1−------------------------------------------------------------------------------
2−-- |
3−-- Module: Database.PostgreSQL.Simple.TypeInfo.Types
4−-- Copyright: (c) 2013 Leon P Smith
5−-- License: BSD3
6−-- Maintainer: Leon P Smith <leon@melding-monads.com>
7−-- Stability: experimental
8−--
9−------------------------------------------------------------------------------
10−
11−module Database.PostgreSQL.Simple.TypeInfo.Types where
12−
13−import Data.ByteString(ByteString)
14−import Database.PostgreSQL.LibPQ(Oid)
15−import Data.Vector(Vector)
16−
17−-- | A structure representing some of the metadata regarding a PostgreSQL
18−-- type, mostly taken from the @pg_type@ table.
19−
20−data TypeInfo
21−
22− = Basic { typoid :: {-# UNPACK #-} !Oid
23− , typcategory :: {-# UNPACK #-} !Char
24− , typdelim :: {-# UNPACK #-} !Char
25− , typname :: !ByteString
26− }
27−
28− | Array { typoid :: {-# UNPACK #-} !Oid
29− , typcategory :: {-# UNPACK #-} !Char
30− , typdelim :: {-# UNPACK #-} !Char
31− , typname :: !ByteString
32− , typelem :: !TypeInfo
33− }
34−
35− | Range { typoid :: {-# UNPACK #-} !Oid
36− , typcategory :: {-# UNPACK #-} !Char
37− , typdelim :: {-# UNPACK #-} !Char
38− , typname :: !ByteString
39− , rngsubtype :: !TypeInfo
40− }
41−
42− | Composite { typoid :: {-# UNPACK #-} !Oid
43− , typcategory :: {-# UNPACK #-} !Char
44− , typdelim :: {-# UNPACK #-} !Char
45− , typname :: !ByteString
46− , typrelid :: {-# UNPACK #-} !Oid
47− , attributes :: !(Vector Attribute)
48− }
49−
50− deriving (Show)
51−
52−data Attribute
53− = Attribute { attname :: !ByteString
54− , atttype :: !TypeInfo
55− }
56− deriving (Show)
src/Database/PostgreSQL/Simple/Types.hs+37−187
@@ -1,58 +1,43 @@
1−{-# LANGUAGE CPP, DeriveDataTypeable, DeriveFunctor, GeneralizedNewtypeDeriving #-}
1+{-# LANGUAGE DeriveDataTypeable, DeriveFunctor, GeneralizedNewtypeDeriving #-}
22
3−------------------------------------------------------------------------------
43-- |
54-- Module: Database.PostgreSQL.Simple.Types
65-- Copyright: (c) 2011 MailRank, Inc.
7−-- (c) 2011-2012 Leon P Smith
6+-- (c) 2011 Leon P Smith
87-- License: BSD3
98-- Maintainer: Leon P Smith <leon@melding-monads.com>
109-- Stability: experimental
10+-- Portability: portable
1111--
1212-- Basic types.
13−--
14−------------------------------------------------------------------------------
1513
1614module Database.PostgreSQL.Simple.Types
1715 (
1816 Null(..)
19− , Default(..)
2017 , Only(..)
2118 , In(..)
2219 , Binary(..)
23− , Identifier(..)
24− , QualifiedIdentifier(..)
2520 , Query(..)
2621 , Oid(..)
27− , (:.)(..)
28− , Savepoint(..)
29− , PGArray(..)
30− , Values(..)
3122 ) where
3223
33−import Control.Arrow (first)
34−import Data.ByteString (ByteString)
35−import Data.Hashable (Hashable(hashWithSalt))
36−import Data.Foldable (toList)
37−import Data.Monoid (Monoid(..))
38−import Data.Semigroup
39−import Data.String (IsString(..))
40−import Data.Typeable (Typeable)
41−import Data.ByteString.Builder ( stringUtf8 )
24+import Blaze.ByteString.Builder (toByteString)
25+import Control.Arrow (first)
26+import Data.ByteString (ByteString)
27+import Data.Monoid (Monoid(..))
28+import Data.String (IsString(..))
29+import Data.Typeable (Typeable)
30+import qualified Blaze.ByteString.Builder.Char.Utf8 as Utf8
4231import qualified Data.ByteString as B
43−import Data.Text (Text)
44−import qualified Data.Text as T
45−import Data.Tuple.Only (Only(..))
46−import Database.PostgreSQL.LibPQ (Oid(..))
47−import Database.PostgreSQL.Simple.Compat (toByteString)
32+import Database.PostgreSQL.LibPQ (Oid(..))
4833
4934-- | A placeholder for the SQL @NULL@ value.
5035data Null = Null
5136 deriving (Read, Show, Typeable)
5237
53−-- | A placeholder for the PostgreSQL @DEFAULT@ value.
54−data Default = Default
55− deriving (Read, Show, Typeable)
38+instance Eq Null where
39+ _ == _ = False
40+ _ /= _ = False
5641
5742-- | A query string. This type is intended to make it difficult to
5843-- construct a SQL query by concatenating string fragments, as that is
@@ -65,7 +50,7 @@ data Default = Default
6550--
6651-- > {-# LANGUAGE OverloadedStrings #-}
6752-- >
68−-- > import Database.PostgreSQL.Simple
53+-- > import Database.MySQL.Simple
6954-- >
7055-- > q :: Query
7156-- > q = "select ?"
@@ -84,175 +69,40 @@ instance Read Query where
8469 readsPrec i = fmap (first Query) . readsPrec i
8570
8671instance IsString Query where
87− fromString = Query . toByteString . stringUtf8
88−
89−instance Semigroup Query where
90− Query a <> Query b = Query (B.append a b)
91− {-# INLINE (<>) #-}
92− sconcat xs = Query (B.concat $ map fromQuery $ toList xs)
72+ fromString = Query . toByteString . Utf8.fromString
9373
9474instance Monoid Query where
9575 mempty = Query B.empty
76+ mappend (Query a) (Query b) = Query (B.append a b)
77+ {-# INLINE mappend #-}
9678
97−-- | Wrap a list of values for use in an @IN@ clause. Replaces a
98−-- single \"@?@\" character with a parenthesized list of rendered
99−-- values.
100−--
101−-- Example:
102−--
103−-- > query c "select * from whatever where id in ?" (Only (In [3,4,5]))
104−--
105−-- Note that @In []@ expands to @(null)@, which works as expected in
106−-- the query above, but evaluates to the logical null value on every
107−-- row instead of @TRUE@. This means that changing the query above
108−-- to @... id NOT in ?@ and supplying the empty list as the parameter
109−-- returns zero rows, instead of all of them as one would expect.
79+-- | A single-value \"collection\".
11080--
111−-- Since postgresql doesn't seem to provide a syntax for actually specifying
112−-- an empty list, which could solve this completely, there are two
113−-- workarounds particularly worth mentioning, namely:
81+-- This is useful if you need to supply a single parameter to a SQL
82+-- query, or extract a single column from a SQL result.
11483--
115−-- 1. Use postgresql-simple's 'Values' type instead, which can handle the
116−-- empty case correctly. Note however that while specifying the
117−-- postgresql type @"int4"@ is mandatory in the empty case, specifying
118−-- the haskell type @Values (Only Int)@ would not normally be needed in
119−-- realistic use cases.
84+-- Parameter example:
12085--
121−-- > query c "select * from whatever where id not in ?"
122−-- > (Only (Values ["int4"] [] :: Values (Only Int)))
86+-- @query c \"select x from scores where x > ?\" ('Only' (42::Int))@
12387--
88+-- Result example:
12489--
125−-- 2. Use sql's @COALESCE@ operator to turn a logical @null@ into the correct
126−-- boolean. Note however that the correct boolean depends on the use
127−-- case:
128−--
129−-- > query c "select * from whatever where coalesce(id NOT in ?, TRUE)"
130−-- > (Only (In [] :: In [Int]))
90+-- @xs <- query_ c \"select id from users\"
91+--forM_ xs $ \\('Only' id) -> {- ... -}@
92+newtype Only a = Only {
93+ fromOnly :: a
94+ } deriving (Eq, Ord, Read, Show, Typeable, Functor)
95+
96+-- | Wrap a list of values for use in an @IN@ clause. Replaces a
97+-- single \"@?@\" character with a parenthesized list of rendered
98+-- values.
13199--
132−-- > query c "select * from whatever where coalesce(id IN ?, FALSE)"
133−-- > (Only (In [] :: In [Int]))
100+-- Example:
134101--
135−-- Note that at as of PostgreSQL 9.4, the query planner cannot see inside
136−-- the @COALESCE@ operator, so if you have an index on @id@ then you
137−-- probably don't want to write the last example with @COALESCE@, which
138−-- would result in a table scan. There are further caveats if @id@ can
139−-- be null or you want null treated sensibly as a component of @IN@ or
140−-- @NOT IN@.
141−
102+-- > query c "select * from whatever where id in ?" (In [3,4,5])
142103newtype In a = In a
143104 deriving (Eq, Ord, Read, Show, Typeable, Functor)
144105
145−-- | Wrap binary data for use as a @bytea@ value.
146−newtype Binary a = Binary {fromBinary :: a}
106+-- | Wrap a mostly-binary string to be escaped in hexadecimal.
107+newtype Binary a = Binary a
147108 deriving (Eq, Ord, Read, Show, Typeable, Functor)
148−
149−-- | Wrap text for use as sql identifier, i.e. a table or column name.
150−newtype Identifier = Identifier {fromIdentifier :: Text}
151− deriving (Eq, Ord, Read, Show, Typeable, IsString)
152−
153−instance Hashable Identifier where
154− hashWithSalt i (Identifier t) = hashWithSalt i t
155−
156−-- | Wrap text for use as (maybe) qualified identifier, i.e. a table
157−-- with schema, or column with table.
158−data QualifiedIdentifier = QualifiedIdentifier (Maybe Text) Text
159− deriving (Eq, Ord, Read, Show, Typeable)
160−
161−instance Hashable QualifiedIdentifier where
162− hashWithSalt i (QualifiedIdentifier q t) = hashWithSalt i (q, t)
163−
164−-- | @\"foo.bar\"@ will get turned into
165−-- @QualifiedIdentifier (Just \"foo\") \"bar\"@, while @\"foo\"@ will get
166−-- turned into @QualifiedIdentifier Nothing \"foo\"@. Note this instance
167−-- is for convenience, and does not match postgres syntax. It
168−-- only examines the first period character, and thus cannot be used if the
169−-- qualifying identifier contains a period for example.
170−
171−instance IsString QualifiedIdentifier where
172− fromString str = let (x,y) = T.break (== '.') (fromString str)
173− in if T.null y
174− then QualifiedIdentifier Nothing x
175− else QualifiedIdentifier (Just x) (T.tail y)
176−
177−-- | Wrap a list for use as a PostgreSQL array.
178−newtype PGArray a = PGArray {fromPGArray :: [a]}
179− deriving (Eq, Ord, Read, Show, Typeable, Functor)
180−
181−-- | A composite type to parse your custom data structures without
182−-- having to define dummy newtype wrappers every time.
183−--
184−--
185−-- > instance FromRow MyData where ...
186−--
187−-- > instance FromRow MyData2 where ...
188−--
189−--
190−-- then I can do the following for free:
191−--
192−-- @
193−-- res <- query' c "..."
194−-- forM res $ \\(MyData{..} :. MyData2{..}) -> do
195−-- ....
196−-- @
197−data h :. t = h :. t deriving (Eq,Ord,Show,Read,Typeable)
198−
199−infixr 3 :.
200−
201−newtype Savepoint = Savepoint Query
202− deriving (Eq, Ord, Show, Read, Typeable)
203−
204−-- | Represents a @VALUES@ table literal, usable as an alternative to
205−-- 'Database.PostgreSQL.Simple.executeMany' and
206−-- 'Database.PostgreSQL.Simple.returning'. The main advantage is that
207−-- you can parametrize more than just a single @VALUES@ expression.
208−-- For example, here's a query to insert a thing into one table
209−-- and some attributes of that thing into another, returning the
210−-- new id generated by the database:
211−--
212−--
213−-- > query c [sql|
214−-- > WITH new_thing AS (
215−-- > INSERT INTO thing (name) VALUES (?) RETURNING id
216−-- > ), new_attributes AS (
217−-- > INSERT INTO thing_attributes
218−-- > SELECT new_thing.id, attrs.*
219−-- > FROM new_thing JOIN ? attrs ON TRUE
220−-- > ) SELECT * FROM new_thing
221−-- > |] ("foo", Values [ "int4", "text" ]
222−-- > [ ( 1 , "hello" )
223−-- > , ( 2 , "world" ) ])
224−--
225−-- (Note this example uses writable common table expressions,
226−-- which were added in PostgreSQL 9.1)
227−--
228−-- The second parameter gets expanded into the following SQL syntax:
229−--
230−-- > (VALUES (1::"int4",'hello'::"text"),(2,'world'))
231−--
232−-- When the list of attributes is empty, the second parameter expands to:
233−--
234−-- > (VALUES (null::"int4",null::"text") LIMIT 0)
235−--
236−-- By contrast, @executeMany@ and @returning@ don't issue the query
237−-- in the empty case, and simply return @0@ and @[]@ respectively.
238−-- This behavior is usually correct given their intended use cases,
239−-- but would certainly be wrong in the example above.
240−--
241−-- The first argument is a list of postgresql type names. Because this
242−-- is turned into a properly quoted identifier, the type name is case
243−-- sensitive and must be as it appears in the @pg_type@ table. Thus,
244−-- you must write @timestamptz@ instead of @timestamp with time zone@,
245−-- @int4@ instead of @integer@ or @serial@, @_int8@ instead of @bigint[]@,
246−-- etcetera.
247−--
248−-- You may omit the type names, however, if you do so the list
249−-- of values must be non-empty, and postgresql must be able to infer
250−-- the types of the columns from the surrounding context. If the first
251−-- condition is not met, postgresql-simple will throw an exception
252−-- without issuing the query. In the second case, the postgres server
253−-- will return an error which will be turned into a @SqlError@ exception.
254−--
255−-- See <https://www.postgresql.org/docs/9.5/static/sql-values.html> for
256−-- more information.
257−data Values a = Values [QualifiedIdentifier] [a]
258− deriving (Eq, Ord, Show, Read, Typeable)
src/Database/PostgreSQL/Simple/Vector.hsdeleted−45
@@ -1,45 +0,0 @@
1−-- | 'query' variants returning 'V.Vector'.
2−module Database.PostgreSQL.Simple.Vector where
3−
4−import Database.PostgreSQL.Simple (Connection, formatQuery, formatMany)
5−import Database.PostgreSQL.Simple.FromRow (FromRow(..))
6−import Database.PostgreSQL.Simple.ToRow (ToRow(..))
7−import Database.PostgreSQL.Simple.Internal (RowParser, exec)
8−import Database.PostgreSQL.Simple.Internal.PQResultUtils
9−import Database.PostgreSQL.Simple.Types ( Query (..) )
10−
11−import qualified Data.Vector as V
12−
13−-- | Perform a @SELECT@ or other SQL query that is expected to return
14−-- results. All results are retrieved and converted before this
15−-- function returns.
16−query :: (ToRow q, FromRow r) => Connection -> Query -> q -> IO (V.Vector r)
17−query = queryWith fromRow
18−
19−-- | A version of 'query' that does not perform query substitution.
20−query_ :: (FromRow r) => Connection -> Query -> IO (V.Vector r)
21−query_ = queryWith_ fromRow
22−
23−-- | A version of 'query' taking parser as argument
24−queryWith :: ToRow q => RowParser r -> Connection -> Query -> q -> IO (V.Vector r)
25−queryWith parser conn template qs = do
26− result <- exec conn =<< formatQuery conn template qs
27− finishQueryWithV parser conn template result
28−
29−-- | A version of 'query_' taking parser as argument
30−queryWith_ :: RowParser r -> Connection -> Query -> IO (V.Vector r)
31−queryWith_ parser conn q@(Query que) = do
32− result <- exec conn que
33− finishQueryWithV parser conn q result
34−
35−-- | Execute @INSERT ... RETURNING@, @UPDATE ... RETURNING@, or other SQL
36−-- query that accepts multi-row input and is expected to return results.
37−returning :: (ToRow q, FromRow r) => Connection -> Query -> [q] -> IO (V.Vector r)
38−returning = returningWith fromRow
39−
40−-- | A version of 'returning' taking parser as argument
41−returningWith :: (ToRow q) => RowParser r -> Connection -> Query -> [q] -> IO (V.Vector r)
42−returningWith _ _ _ [] = return V.empty
43−returningWith parser conn q qs = do
44− result <- exec conn =<< formatMany conn q qs
45− finishQueryWithV parser conn q result
src/Database/PostgreSQL/Simple/Vector/Unboxed.hsdeleted−44
@@ -1,44 +0,0 @@
1−module Database.PostgreSQL.Simple.Vector.Unboxed where
2−
3−import Database.PostgreSQL.Simple (Connection, formatQuery, formatMany)
4−import Database.PostgreSQL.Simple.FromRow (FromRow(..))
5−import Database.PostgreSQL.Simple.ToRow (ToRow(..))
6−import Database.PostgreSQL.Simple.Internal (RowParser, exec)
7−import Database.PostgreSQL.Simple.Internal.PQResultUtils
8−import Database.PostgreSQL.Simple.Types ( Query (..) )
9−
10−import qualified Data.Vector.Unboxed as VU
11−
12−-- | Perform a @SELECT@ or other SQL query that is expected to return
13−-- results. All results are retrieved and converted before this
14−-- function returns.
15−query :: (ToRow q, FromRow r, VU.Unbox r) => Connection -> Query -> q -> IO (VU.Vector r)
16−query = queryWith fromRow
17−
18−-- | A version of 'query' that does not perform query substitution.
19−query_ :: (FromRow r, VU.Unbox r) => Connection -> Query -> IO (VU.Vector r)
20−query_ = queryWith_ fromRow
21−
22−-- | A version of 'query' taking parser as argument
23−queryWith :: (ToRow q, VU.Unbox r) => RowParser r -> Connection -> Query -> q -> IO (VU.Vector r)
24−queryWith parser conn template qs = do
25− result <- exec conn =<< formatQuery conn template qs
26− finishQueryWithVU parser conn template result
27−
28−-- | A version of 'query_' taking parser as argument
29−queryWith_ :: VU.Unbox r => RowParser r -> Connection -> Query -> IO (VU.Vector r)
30−queryWith_ parser conn q@(Query que) = do
31− result <- exec conn que
32− finishQueryWithVU parser conn q result
33−
34−-- | Execute @INSERT ... RETURNING@, @UPDATE ... RETURNING@, or other SQL
35−-- query that accepts multi-row input and is expected to return results.
36−returning :: (ToRow q, FromRow r, VU.Unbox r) => Connection -> Query -> [q] -> IO (VU.Vector r)
37−returning = returningWith fromRow
38−
39−-- | A version of 'returning' taking parser as argument
40−returningWith :: (ToRow q, VU.Unbox r) => RowParser r -> Connection -> Query -> [q] -> IO (VU.Vector r)
41−returningWith _ _ _ [] = return VU.empty
42−returningWith parser conn q qs = do
43− result <- exec conn =<< formatMany conn q qs
44− finishQueryWithVU parser conn q result
test/Common.hsdeleted−32
@@ -1,32 +0,0 @@
1−module Common (
2− module Database.PostgreSQL.Simple,
3− module Test.Tasty.HUnit,
4− TestEnv(..),
5− md5,
6−) where
7−
8−import Data.ByteString (ByteString)
9−import Data.Text (Text)
10−import Database.PostgreSQL.Simple
11−import Test.Tasty.HUnit
12−
13−import qualified Crypto.Hash.MD5 as MD5
14−import qualified Data.ByteString.Base16 as Base16
15−import qualified Data.Text.Encoding as TE
16−
17−data TestEnv
18− = TestEnv
19− { conn :: Connection
20− -- ^ Connection shared by all the tests
21− , withConn :: forall a. (Connection -> IO a) -> IO a
22− -- ^ Bracket for spawning additional connections
23− }
24−
25−-- | Return the MD5 hash of a 'ByteString', in lowercase hex format.
26−--
27−-- Example:
28−--
29−-- >[Only hash] <- query_ conn "SELECT md5('hi')"
30−-- >assertEqual "md5('hi')" (md5 "hi") hash
31−md5 :: ByteString -> Text
32−md5 = TE.decodeUtf8 . Base16.encode . MD5.hash
test/Exception.hsdeleted−70
@@ -1,70 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
4−
5−module Exception (testExceptions) where
6−
7−import Database.PostgreSQL.Simple
8−import Test.Tasty.HUnit (Assertion, assertBool)
9−import Common (TestEnv)
10−import Control.Exception (Exception (..), SomeException)
11−import Data.Maybe (isJust)
12−import Data.Either (isLeft)
13−import Control.Exception (throwIO, try)
14−
15−testExceptions :: TestEnv -> Assertion
16−testExceptions _ = do
17− let sqlError = SqlError
18− { sqlState = ""
19− , sqlExecStatus = FatalError
20− , sqlErrorMsg = ""
21− , sqlErrorDetail = ""
22− , sqlErrorHint = ""
23− }
24− let sqlEx :: SomeException = toException sqlError
25− assertBool "SqlError is SomePostgreSqlException" $ isJust (fromException sqlEx :: Maybe SomePostgreSqlException)
26− assertBool "SqlError is SqlError" $ isJust (fromException sqlEx :: Maybe SqlError)
27− eSqlError :: Either SqlError () <- try $ throwIO sqlEx
28− assertBool "Can catch SqlError" $ isLeft eSqlError
29− eSqlPostgreSqlEx :: Either SomePostgreSqlException () <- try $ throwIO sqlEx
30− assertBool "Can catch SomePostgreSqlException from SqlError" $ isLeft eSqlPostgreSqlEx
31−
32− let formatError = FormatError
33− { fmtMessage = ""
34− , fmtQuery = ""
35− , fmtParams = []
36− }
37− let formatEx :: SomeException = toException formatError
38− assertBool "FormatError is SomePostgreSqlException" $ isJust (fromException formatEx :: Maybe SomePostgreSqlException)
39− assertBool "FormatError is FormatError" $ isJust (fromException formatEx :: Maybe FormatError)
40− eFormatError :: Either FormatError () <- try $ throwIO formatEx
41− assertBool "Can catch FormatError" $ isLeft eFormatError
42− eFormatPostreSqlEx :: Either SomePostgreSqlException () <- try $ throwIO formatEx
43− assertBool "Can catch SomePostgreSqlException from FormatError" $ isLeft eFormatPostreSqlEx
44−
45− let queryError = QueryError
46− { qeMessage = ""
47− , qeQuery = ""
48− }
49− let queryEx :: SomeException = toException queryError
50− assertBool "QueryError is SomePostgreSqlException" $ isJust (fromException queryEx :: Maybe SomePostgreSqlException)
51− assertBool "QueryError is QueryError" $ isJust (fromException queryEx :: Maybe QueryError)
52− eQueryError :: Either QueryError () <- try $ throwIO queryEx
53− assertBool "Can catch QueryError" $ isLeft eQueryError
54− eQueryPostgreSqlEx :: Either SomePostgreSqlException () <- try $ throwIO queryEx
55− assertBool "Can catch SomePostgreSqlException from QueryError" $ isLeft eQueryPostgreSqlEx
56−
57− let resultError = Incompatible
58− { errSQLType = ""
59− , errSQLTableOid = Nothing
60− , errSQLField = ""
61− , errHaskellType = ""
62− , errMessage = ""
63− }
64− let resultEx :: SomeException = toException resultError
65− assertBool "ResultError is SomePostgreSqlException" $ isJust (fromException resultEx :: Maybe SomePostgreSqlException)
66− assertBool "ResultError is ResultError" $ isJust (fromException resultEx :: Maybe ResultError)
67− eResultEx :: Either ResultError () <- try $ throwIO resultEx
68− assertBool "Can catch ResultError" $ isLeft eResultEx
69− eResultPostgreSqlEx :: Either SomePostgreSqlException () <- try $ throwIO resultEx
70− assertBool "Can catch SomePostgreSqlException from ResultError" $ isLeft eResultPostgreSqlEx
test/Inspection.hsdeleted−79
@@ -1,79 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE TemplateHaskell #-}
3−{-# OPTIONS_GHC -dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications -dsuppress-module-prefixes -dsuppress-type-signatures #-}
4−-- {-# OPTIONS_GHC -dsuppress-uniques #-}
5−{-# OPTIONS_GHC -fplugin=Test.Inspection.Plugin #-}
6−module Main where
7−
8−import Test.Inspection
9−import Test.Tasty
10−import Test.Tasty.HUnit
11−
12−import qualified Database.PostgreSQL.LibPQ as PQ
13−import Database.PostgreSQL.Simple.FromField
14−import Database.PostgreSQL.Simple.TypeInfo as TI
15−import Database.PostgreSQL.Simple.TypeInfo.Macro
16−import qualified Database.PostgreSQL.Simple.TypeInfo.Static as TI
17−
18−-------------------------------------------------------------------------------
19−-- Inspection tests
20−-------------------------------------------------------------------------------
21−
22−-- # doesn't work :(
23−#define TH_MKCOMPATS3(a,b,c) $(mkCompats [TI.a,TI.b,TI.c])
24−#define IN_MKCOMPATS3(a,b,c) (eq TI.a \/ eq TI.b \/ eq TI.c)
25−
26−#define TH_INLINETYPOID(n) eq $(inlineTypoid TI.n)
27−#define IN_INLINETYPOID(n) eq TI.n
28−
29−-- eta-expansion is required
30−lhs01, rhs01 :: PQ.Oid -> Bool
31−lhs01 = TH_MKCOMPATS3(name,text,char)
32−rhs01 = IN_MKCOMPATS3(nameOid,textOid,charOid)
33−
34−lhs02, rhs02 :: PQ.Oid -> Bool
35−lhs02 = TH_INLINETYPOID(name)
36−rhs02 = IN_INLINETYPOID(nameOid)
37−
38−eq :: PQ.Oid -> PQ.Oid -> Bool
39−eq = (==)
40−{-# INLINE eq #-}
41−
42−infixr 2 \/
43−(\/) :: (PQ.Oid -> Bool)
44− -> (PQ.Oid -> Bool)
45− -> (PQ.Oid -> Bool)
46−f \/ g = \x -> f x || g x
47−{-# INLINE (\/) #-}
48−
49−inspectionTests :: TestTree
50−inspectionTests = testGroup "inspection"
51− [ testCase "mkCompats" $
52− assertSuccess $(inspectTest $ 'lhs01 === 'rhs01)
53−
54− -- byteaOid isn't inlined?
55− , testCase "inlineTypoid" $
56−#if __GLASGOW_HASKELL__ >= 808
57− assertSuccess
58−#else
59− assertFailure'
60−#endif
61− $(inspectTest $ 'lhs02 ==- 'rhs02)
62− ]
63−
64−assertSuccess :: Result -> IO ()
65−assertSuccess (Success _) = return ()
66−assertSuccess (Failure err) = assertFailure err
67−
68−assertFailure' :: Result -> IO ()
69−assertFailure' (Success err) = assertFailure err
70−assertFailure' (Failure _) = return ()
71−
72−-------------------------------------------------------------------------------
73−-- Main
74−-------------------------------------------------------------------------------
75−
76−main :: IO ()
77−main = defaultMain $ testGroup "tests"
78− [ inspectionTests
79− ]
test/Interval.hsdeleted−176
@@ -1,176 +0,0 @@
1−{-# LANGUAGE QuasiQuotes #-}
2−
3−{-
4−
5−Testing strategies:
6−
7−fromString . toString == id ** Todo?
8−
9−toString . fromString == almost id ** Todo?
10−
11−postgresql -> haskell -> postgresql * Done
12−
13−haskell -> postgresql -> haskell ** Todo?
14−
15−But still, what we really want to establish is that the two values
16−correspond; for example, a conversion that consistently added hour
17−when printed to a string and subtracted an hour when parsed from string
18−would still pass these tests.
19−
20−
21−Right now, we are checking that 1400+ timestamps in the range of 1860 to
22−2060 round trip from postgresql to haskell and back in 5 different timezones.
23−In addition to UTC, the four timezones were selected so that 2 have a positive
24−offset, and 2 have a negative offset, and that 2 have an offset of a
25−whole number of hours, while the other two do not.
26−
27−It may be worth adding a few more timezones to ensure better test coverage.
28−
29−We are checking a handful of selected timestamps to ensure we hit
30−various corner-cases in the code, in addition to 1400 timestamps randomly
31−generated with granularity of seconds down to microseconds in powers of ten.
32−
33−-}
34−
35−module Interval (testInterval) where
36−
37−import Common
38−import Control.Monad(forM_, replicateM_)
39−import Data.Time.Compat
40−import Data.Time.LocalTime.Compat (CalendarDiffTime(..))
41−import Data.ByteString(ByteString)
42−import Database.PostgreSQL.Simple.SqlQQ
43−
44−data IntervalTestCase = IntervalTestCase
45− { label :: String
46− , inputMonths :: Integer
47− , inputSeconds :: NominalDiffTime
48− , asText :: String
49− }
50− deriving (Eq, Show)
51−
52−testInterval :: TestEnv -> Assertion
53−testInterval env@TestEnv{..} = do
54−
55− initializeTable env
56−
57− let milliseconds = 0.001
58− seconds = 1
59− minutes = 60 * seconds
60− hours = 60 * minutes
61− days = 24 * hours
62− weeks = 7 * days
63− months = 1
64− years = 12 * months
65−
66− mapM (checkRoundTrip env)
67− [ IntervalTestCase
68− { label = "zero"
69− , inputMonths = 0
70− , inputSeconds = 0
71− , asText = "PT0"
72− }
73− , IntervalTestCase
74− { label = "1 year"
75− , inputMonths = 1 * years
76− , inputSeconds = 0
77− , asText = "P1Y"
78− }
79− , IntervalTestCase
80− { label = "2 months"
81− , inputMonths = 2 * months
82− , inputSeconds = 0
83− , asText = "P2M"
84− }
85− , IntervalTestCase
86− { label = "3 weeks"
87− , inputMonths = 0
88− , inputSeconds = 3 * weeks
89− , asText = "P3W"
90− }
91− , IntervalTestCase
92− { label = "4 days"
93− , inputMonths = 0
94− , inputSeconds = 4 * days
95− , asText = "P4D"
96− }
97− , IntervalTestCase
98− { label = "5 hours"
99− , inputMonths = 0
100− , inputSeconds = 5 * hours
101− , asText = "PT5H"
102− }
103− , IntervalTestCase
104− { label = "6 minutes"
105− , inputMonths = 0
106− , inputSeconds = 6 * minutes
107− , asText = "PT6M"
108− }
109− , IntervalTestCase
110− { label = "7 seconds"
111− , inputMonths = 0
112− , inputSeconds = 7 * seconds
113− , asText = "PT7S"
114− }
115− , IntervalTestCase
116− { label = "8 milliseconds"
117− , inputMonths = 0
118− , inputSeconds = 8 * milliseconds
119− , asText = "PT0.008S"
120− }
121− , IntervalTestCase
122− { label = "combination of intervals (day-size or bigger)"
123− , inputMonths = 2 * years + 4 * months
124− , inputSeconds = 3 * weeks + 5 * days
125− , asText = "P2Y4M3W5D"
126− }
127− , IntervalTestCase
128− { label = "combination of intervals (smaller than day-size)"
129− , inputMonths = 0
130− , inputSeconds = 18 * hours + 56 * minutes + 23 * seconds + 563 * milliseconds
131− , asText = "PT18H56M23.563S"
132− }
133− , IntervalTestCase
134− { label = "full combination of intervals"
135− , inputMonths = 2 * years + 4 * months
136− , inputSeconds = 3 * weeks + 5 * days + 18 * hours + 56 * minutes + 23 * seconds + 563 * milliseconds
137− , asText = "P2Y4M3W5DT18H56M23.563S"
138− }
139− ]
140−
141− return ()
142−
143−initializeTable :: TestEnv -> IO ()
144−initializeTable TestEnv{..} = withTransaction conn $ do
145− execute_ conn
146− [sql| CREATE TEMPORARY TABLE testinterval
147− ( id serial, sample interval, PRIMARY KEY(id) ) |]
148−
149− return ()
150−
151−checkRoundTrip :: TestEnv -> IntervalTestCase -> IO ()
152−checkRoundTrip TestEnv{..} IntervalTestCase{..} = do
153−
154− let input = CalendarDiffTime
155− { ctMonths = inputMonths
156− , ctTime = inputSeconds
157− }
158−
159− [(returnedId :: Int, output :: CalendarDiffTime)] <- query conn [sql|
160− INSERT INTO testinterval (sample)
161− VALUES (?)
162− RETURNING id, sample
163− |] (Only input)
164−
165− assertBool ("CalendarDiffTime did not round-trip from Haskell to SQL and back (" ++ label ++ ")") $
166− output == input
167−
168− [(Only isExpectedIso)] <- query conn [sql|
169− SELECT sample = (?)::interval
170− FROM testinterval
171− WHERE id = ?
172− |] (asText, returnedId)
173−
174− assertBool ("CalendarDiffTime inserted did not match ISO8601 equivalent \"" ++ asText ++ "\". (" ++ label ++ ")")
175− isExpectedIso
176−
test/Main.hsdeleted−648
@@ -1,648 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveGeneric #-}
3−{-# LANGUAGE DeriveDataTypeable #-}
4−{-# LANGUAGE DoAndIfThenElse #-}
5−{-# LANGUAGE ScopedTypeVariables #-}
6−#if __GLASGOW_HASKELL__ >= 806
7−{-# LANGUAGE DerivingStrategies #-}
8−{-# LANGUAGE DerivingVia #-}
9−{-# LANGUAGE DeriveAnyClass #-}
10−#endif
11−module Main (main) where
12−
13−import Common
14−import Database.PostgreSQL.Simple.Copy
15−import Database.PostgreSQL.Simple.ToField (ToField)
16−import Database.PostgreSQL.Simple.FromField (FromField)
17−import Database.PostgreSQL.Simple.HStore
18−import Database.PostgreSQL.Simple.Newtypes
19−import Database.PostgreSQL.Simple.Internal (breakOnSingleQuestionMark)
20−import Database.PostgreSQL.Simple.Types(Query(..),Values(..), PGArray(..))
21−import qualified Database.PostgreSQL.Simple.Transaction as ST
22−
23−import Control.Applicative
24−import Control.Exception as E
25−import Control.Monad
26−import Data.Char
27−import Data.Foldable (toList)
28−import Data.List (concat, sort)
29−import Data.IORef
30−import Data.Monoid ((<>))
31−import Data.String (fromString)
32−import Data.Typeable
33−import GHC.Generics (Generic)
34−
35−import Data.Aeson
36−import Data.ByteString (ByteString)
37−import qualified Data.ByteString as B
38−import qualified Data.ByteString.Char8 as BS8
39−import qualified Data.ByteString.Lazy.Char8 as BL
40−import Data.CaseInsensitive (CI)
41−import qualified Data.CaseInsensitive as CI
42−import Data.Map (Map)
43−import qualified Data.Map as Map
44−import Data.Text(Text)
45−import qualified Data.Text.Encoding as T
46−import qualified Data.Vector as V
47−import System.FilePath
48−import System.Timeout(timeout)
49−import Data.Time.Compat (getCurrentTime, diffUTCTime)
50−import System.Environment (getEnvironment)
51−
52−import Test.Tasty
53−import Test.Tasty.Golden
54−import Notify
55−import Serializable
56−import Time
57−import Interval
58−import Exception (testExceptions)
59−
60−tests :: TestEnv -> TestTree
61−tests env = testGroup "tests"
62− $ map ($ env)
63− [ testBytea
64− , testCase "ExecuteMany" . testExecuteMany
65− , testCase "Fold" . testFold
66− , testCase "Notify" . testNotify
67− , testCase "Serializable" . testSerializable
68− , testCase "Time" . testTime
69− , testCase "Interval" . testInterval
70− , testCase "Array" . testArray
71− , testCase "Array of nullables" . testNullableArray
72− , testCase "HStore" . testHStore
73− , testCase "citext" . testCIText
74− , testCase "JSON" . testJSON
75− , testCase "Aeson newtype" . testAeson
76− , testCase "DerivingVia" . testDerivingVia
77− , testCase "Question mark escape" . testQM
78− , testCase "Savepoint" . testSavepoint
79− , testCase "Unicode" . testUnicode
80− , testCase "Values" . testValues
81− , testCase "Copy" . testCopy
82− , testCopyFailures
83− , testCase "Double" . testDouble
84− , testCase "1-ary generic" . testGeneric1
85− , testCase "2-ary generic" . testGeneric2
86− , testCase "3-ary generic" . testGeneric3
87− , testCase "Timeout" . testTimeout
88− , testCase "Exceptions" . testExceptions
89− ]
90−
91−testBytea :: TestEnv -> TestTree
92−testBytea TestEnv{..} = testGroup "Bytea"
93− [ testStr "empty" []
94− , testStr "\"hello\"" $ map (fromIntegral . fromEnum) ("hello" :: String)
95− , testStr "ascending" [0..255]
96− , testStr "descending" [255,254..0]
97− , testStr "ascending, doubled up" $ doubleUp [0..255]
98− , testStr "descending, doubled up" $ doubleUp [255,254..0]
99− ]
100− where
101− testStr label bytes = testCase label $ do
102− let bs = B.pack bytes
103−
104− [Only h] <- query conn "SELECT md5(?::bytea)" [Binary bs]
105− assertBool "Haskell -> SQL conversion altered the string" $ md5 bs == h
106−
107− [Only (Binary r)] <- query conn "SELECT ?::bytea" [Binary bs]
108− assertBool "SQL -> Haskell conversion altered the string" $ bs == r
109−
110− doubleUp = concatMap (\x -> [x, x])
111−
112−testExecuteMany :: TestEnv -> Assertion
113−testExecuteMany TestEnv{..} = do
114− execute_ conn "CREATE TEMPORARY TABLE tmp_executeMany (i INT, t TEXT, b BYTEA)"
115−
116− let rows :: [(Int, String, Binary ByteString)]
117− rows = [ (1, "hello", Binary "bye")
118− , (2, "world", Binary "\0\r\t\n")
119− , (3, "?", Binary "")
120− ]
121−
122− count <- executeMany conn "INSERT INTO tmp_executeMany VALUES (?, ?, ?)" rows
123− count @?= fromIntegral (length rows)
124−
125− rows' <- query_ conn "SELECT * FROM tmp_executeMany"
126− rows' @?= rows
127−
128− return ()
129−
130−testFold :: TestEnv -> Assertion
131−testFold TestEnv{..} = do
132− xs <- fold_ conn "SELECT generate_series(1,10000)"
133− [] $ \xs (Only x) -> return (x:xs)
134− reverse xs @?= ([1..10000] :: [Int])
135−
136− ref <- newIORef []
137− forEach conn "SELECT * FROM generate_series(1,?) a, generate_series(1,?) b"
138− (100 :: Int, 50 :: Int) $ \(a :: Int, b :: Int) -> do
139− xs <- readIORef ref
140− writeIORef ref $! (a,b):xs
141− xs <- readIORef ref
142− reverse xs @?= [(a,b) | b <- [1..50], a <- [1..100]]
143−
144− -- Make sure fold propagates our exception.
145− ref <- newIORef []
146− True <- expectError (== TestException) $
147− forEach_ conn "SELECT generate_series(1,10)" $ \(Only a) ->
148− if a == 5 then do
149− -- Cause a SQL error to trip up CLOSE.
150− True <- expectError isSyntaxError $
151− execute_ conn "asdf"
152− True <- expectError ST.isFailedTransactionError $
153− (query_ conn "SELECT 1" :: IO [(Only Int)])
154− throwIO TestException
155− else do
156− xs <- readIORef ref
157− writeIORef ref $! (a :: Int) : xs
158− xs <- readIORef ref
159− reverse xs @?= [1..4]
160−
161− withTransaction conn $ replicateM_ 2 $ do
162− xs <- fold_ conn "VALUES (1), (2), (3), (4), (5)"
163− [] $ \xs (Only x) -> return (x:xs)
164− reverse xs @?= ([1..5] :: [Int])
165−
166− ref <- newIORef []
167− forEach_ conn "SELECT generate_series(1,101)" $ \(Only a) ->
168− forEach_ conn "SELECT generate_series(1,55)" $ \(Only b) -> do
169− xs <- readIORef ref
170− writeIORef ref $! (a :: Int, b :: Int) : xs
171− xs <- readIORef ref
172− reverse xs @?= [(a,b) | a <- [1..101], b <- [1..55]]
173−
174− xs <- fold_ conn "SELECT 1 WHERE FALSE"
175− [] $ \xs (Only x) -> return (x:xs)
176− xs @?= ([] :: [Int])
177−
178− -- TODO: add more complete tests, e.g.:
179− --
180− -- * Fold in a transaction
181− --
182− -- * Fold in a transaction after a previous fold has been performed
183− --
184− -- * Nested fold
185−
186− return ()
187−
188−queryFailure :: forall a. (FromField a, Typeable a, Show a)
189− => Connection -> Query -> a -> Assertion
190−queryFailure conn q resultType = do
191− x :: Either SomeException [Only a] <- E.try $ query_ conn q
192− case x of
193− Left _ -> return ()
194− Right val -> assertFailure ("Did not fail as expected: "
195− ++ show q
196− ++ " :: "
197− ++ show (typeOf resultType)
198− ++ " -> " ++ show val)
199−
200−testArray :: TestEnv -> Assertion
201−testArray TestEnv{..} = do
202− xs <- query_ conn "SELECT '{1,2,3,4}'::_int4"
203− xs @?= [Only (V.fromList [1,2,3,4 :: Int])]
204− xs <- query_ conn "SELECT '{{1,2},{3,4}}'::_int4"
205− xs @?= [Only (V.fromList [V.fromList [1,2],
206− V.fromList [3,4 :: Int]])]
207− queryFailure conn "SELECT '{1,2,3,4}'::_int4" (undefined :: V.Vector Bool)
208− queryFailure conn "SELECT '{{1,2},{3,4}}'::_int4" (undefined :: V.Vector Int)
209−
210−testNullableArray :: TestEnv -> Assertion
211−testNullableArray TestEnv{..} = do
212− xs <- query_ conn "SELECT '{sometext, \"NULL\"}'::_text"
213− xs @?= [Only (V.fromList ["sometext", "NULL" :: Text])]
214− xs <- query_ conn "SELECT '{sometext, NULL}'::_text"
215− xs @?= [Only (V.fromList [Just "sometext", Nothing :: Maybe Text])]
216− queryFailure conn "SELECT '{sometext, NULL}'::_text" (undefined :: V.Vector Text)
217−
218−testHStore :: TestEnv -> Assertion
219−testHStore TestEnv{..} = do
220− execute_ conn "CREATE EXTENSION IF NOT EXISTS hstore"
221− roundTrip []
222− roundTrip [("foo","bar"),("bar","baz"),("baz","hello")]
223− roundTrip [("fo\"o","bar"),("b\\ar","baz"),("baz","\"value\\with\"escapes")]
224− where
225− roundTrip :: [(Text,Text)] -> Assertion
226− roundTrip xs = do
227− let m = Only (HStoreMap (Map.fromList xs))
228− m' <- query conn "SELECT ?::hstore" m
229− [m] @?= m'
230−
231−testCIText :: TestEnv -> Assertion
232−testCIText TestEnv{..} = do
233− execute_ conn "CREATE EXTENSION IF NOT EXISTS citext"
234− roundTrip (CI.mk "")
235− roundTrip (CI.mk "UPPERCASE")
236− roundTrip (CI.mk "lowercase")
237− where
238− roundTrip :: (CI Text) -> Assertion
239− roundTrip cit = do
240− let toPostgres = Only cit
241− fromPostgres <- query conn "SELECT ?::citext" toPostgres
242− [toPostgres] @?= fromPostgres
243−
244−testJSON :: TestEnv -> Assertion
245−testJSON TestEnv{..} = do
246− roundTrip (Map.fromList [] :: Map Text Text)
247− roundTrip (Map.fromList [("foo","bar"),("bar","baz"),("baz","hello")] :: Map Text Text)
248− roundTrip (Map.fromList [("fo\"o","bar"),("b\\ar","baz"),("baz","\"value\\with\"escapes")] :: Map Text Text)
249− roundTrip (V.fromList [1,2,3,4,5::Int])
250− roundTrip ("foo" :: Text)
251− roundTrip (42 :: Int)
252− where
253− roundTrip :: ToJSON a => a -> Assertion
254− roundTrip a = do
255− let js = Only (toJSON a)
256− js' <- query conn "SELECT ?::json" js
257− [js] @?= js'
258−
259−testAeson :: TestEnv -> Assertion
260−testAeson TestEnv{..} = do
261− roundTrip (Map.fromList [] :: Map Text Text)
262− roundTrip (Map.fromList [("foo","bar"),("bar","baz"),("baz","hello")] :: Map Text Text)
263− roundTrip (Map.fromList [("fo\"o","bar"),("b\\ar","baz"),("baz","\"value\\with\"escapes")] :: Map Text Text)
264− roundTrip (V.fromList [1,2,3,4,5::Int])
265− roundTrip ("foo" :: Text)
266− roundTrip (42 :: Int)
267− where
268− roundTrip :: (Eq a, Show a, Typeable a, ToJSON a, FromJSON a)=> a -> Assertion
269− roundTrip x = do
270− y <- query conn "SELECT ?::json" (Only (Aeson x))
271− [Only (Aeson x)] @?= y
272−
273−testDerivingVia :: TestEnv -> Assertion
274−testDerivingVia TestEnv{..} = do
275−#if __GLASGOW_HASKELL__ <806
276− return ()
277−#else
278− roundTrip $ DerivingVia1 42 "Meaning of Life"
279− where
280− roundTrip :: (Eq a, Show a, Typeable a, ToField a, FromField a)=> a -> Assertion
281− roundTrip x = do
282− y <- query conn "SELECT ?::json" (Only x)
283− [Only x] @?= y
284−
285−data DerivingVia1 = DerivingVia1 Int String
286− deriving stock (Eq, Show, Generic)
287− deriving anyclass (FromJSON, ToJSON)
288− deriving (ToField, FromField) via Aeson DerivingVia1
289−
290−#endif
291−
292−
293−testQM :: TestEnv -> Assertion
294−testQM TestEnv{..} = do
295− -- Just test on a single string
296− let testQuery' b = "testing for ?" <> b <> " and making sure "
297− testQueryDoubleQM = testQuery' "?"
298− testQueryRest = "? is substituted"
299− testQuery = fromString $ testQueryDoubleQM <> testQueryRest
300− -- expect the entire first part with double QMs replaced with literal '?'
301− expected = (fromString $ testQuery' "", fromString testQueryRest)
302− tried = breakOnSingleQuestionMark testQuery
303− errMsg = concat
304− [ "Failed to break on single question mark exclusively:\n"
305− , "expected: ", show expected
306− , "result: ", show tried
307− ]
308− assertBool errMsg $ tried == expected
309−
310− -- Let's also test the question mark operators in action
311− -- ? -> Does the string exist as a top-level key within the JSON value?
312− positiveQuery "SELECT ?::jsonb ?? ?" (testObj, "foo" :: Text)
313− negativeQuery "SELECT ?::jsonb ?? ?" (testObj, "baz" :: Text)
314− negativeQuery "SELECT ?::jsonb ?? ?" (toJSON numArray, "1" :: Text)
315− -- ?| -> Do any of these array strings exist as top-level keys?
316− positiveQuery "SELECT ?::jsonb ??| ?" (testObj, PGArray ["nope","bar","6" :: Text])
317− negativeQuery "SELECT ?::jsonb ??| ?" (testObj, PGArray ["nope","6" :: Text])
318− negativeQuery "SELECT ?::jsonb ??| ?" (toJSON numArray, PGArray ["1","2","6" :: Text])
319− -- ?& -> Do all of these array strings exist as top-level keys?
320− positiveQuery "SELECT ?::jsonb ??& ?" (testObj, PGArray ["foo","bar","quux" :: Text])
321− positiveQuery "SELECT ?::jsonb ??& ?" (testObj, PGArray ["foo","bar" :: Text])
322− negativeQuery "SELECT ?::jsonb ??& ?" (testObj, PGArray ["foo","bar","baz" :: Text])
323− negativeQuery "SELECT ?::jsonb ??& ?" (toJSON numArray, PGArray ["1","2","3","4","5" :: Text])
324− -- Format error for 2 question marks, not 4
325− True <- expectError (isFormatError 2) $
326− (query conn "SELECT ?::jsonb ?? ?" $ Only testObj :: IO [Only Bool])
327− return ()
328− where positiveQuery :: ToRow a => Query -> a -> Assertion
329− positiveQuery = boolQuery True
330− negativeQuery :: ToRow a => Query -> a -> Assertion
331− negativeQuery = boolQuery False
332− numArray :: [Int]
333− numArray = [1,2,3,4,5]
334− boolQuery :: ToRow a => Bool -> Query -> a -> Assertion
335− boolQuery b t x = do
336− a <- query conn t x
337− [Only b] @?= a
338− testObj = toJSON (Map.fromList [("foo",toJSON (1 :: Int))
339− ,("bar",String "baz")
340− ,("quux",toJSON [1 :: Int,2,3,4,5])] :: Map Text Value
341− )
342−
343−testSavepoint :: TestEnv -> Assertion
344−testSavepoint TestEnv{..} = do
345− True <- expectError ST.isNoActiveTransactionError $
346− withSavepoint conn $ return ()
347−
348− let getRows :: IO [Int]
349− getRows = map fromOnly <$> query_ conn "SELECT a FROM tmp_savepoint ORDER BY a"
350− withTransaction conn $ do
351− execute_ conn "CREATE TEMPORARY TABLE tmp_savepoint (a INT UNIQUE)"
352− execute_ conn "INSERT INTO tmp_savepoint VALUES (1)"
353− [1] <- getRows
354−
355− withSavepoint conn $ do
356− execute_ conn "INSERT INTO tmp_savepoint VALUES (2)"
357− [1,2] <- getRows
358− return ()
359− [1,2] <- getRows
360−
361− withSavepoint conn $ do
362− execute_ conn "INSERT INTO tmp_savepoint VALUES (3)"
363− [1,2,3] <- getRows
364− True <- expectError isUniqueViolation $
365− execute_ conn "INSERT INTO tmp_savepoint VALUES (2)"
366− True <- expectError ST.isFailedTransactionError getRows
367−
368− -- Body returning successfully after handling error,
369− -- but 'withSavepoint' will roll back without complaining.
370− return ()
371− -- Rolling back clears the error condition.
372− [1,2] <- getRows
373−
374− -- 'withSavepoint' will roll back after an exception, even if the
375− -- exception wasn't SQL-related.
376− True <- expectError (== TestException) $
377− withSavepoint conn $ do
378− execute_ conn "INSERT INTO tmp_savepoint VALUES (3)"
379− [1,2,3] <- getRows
380− throwIO TestException
381− [1,2] <- getRows
382−
383− -- Nested savepoint can be rolled back while the
384− -- outer effects are retained.
385− withSavepoint conn $ do
386− execute_ conn "INSERT INTO tmp_savepoint VALUES (3)"
387− True <- expectError isUniqueViolation $
388− withSavepoint conn $ do
389− execute_ conn "INSERT INTO tmp_savepoint VALUES (4)"
390− [1,2,3,4] <- getRows
391− execute_ conn "INSERT INTO tmp_savepoint VALUES (4)"
392− [1,2,3] <- getRows
393− return ()
394− [1,2,3] <- getRows
395−
396− return ()
397−
398− -- Transaction committed successfully, even though there were errors
399− -- (but we rolled them back).
400− [1,2,3] <- getRows
401−
402− return ()
403−
404−testUnicode :: TestEnv -> Assertion
405−testUnicode TestEnv{..} = do
406− let q = Query . T.encodeUtf8 -- Handle encoding ourselves to ensure
407− -- the table gets created correctly.
408− let messages = map Only ["привет","мир"] :: [Only Text]
409− execute_ conn (q "CREATE TEMPORARY TABLE ру́сский (сообщение TEXT)")
410− executeMany conn "INSERT INTO ру́сский (сообщение) VALUES (?)" messages
411− messages' <- query_ conn "SELECT сообщение FROM ру́сский"
412− sort messages @?= sort messages'
413−
414−testValues :: TestEnv -> Assertion
415−testValues TestEnv{..} = do
416− execute_ conn "CREATE TEMPORARY TABLE values_test (x int, y text)"
417− test (Values ["int4","text"] [])
418− test (Values ["int4","text"] [(1,"hello")])
419− test (Values ["int4","text"] [(1,"hello"),(2,"world")])
420− test (Values ["int4","text"] [(1,"hello"),(2,"world"),(3,"goodbye")])
421− test (Values [] [(1,"hello")])
422− test (Values [] [(1,"hello"),(2,"world")])
423− test (Values [] [(1,"hello"),(2,"world"),(3,"goodbye")])
424− where
425− test :: Values (Int, Text) -> Assertion
426− test table@(Values _ vals) = do
427− execute conn "INSERT INTO values_test ?" (Only table)
428− vals' <- query_ conn "DELETE FROM values_test RETURNING *"
429− sort vals @?= sort vals'
430−
431−
432−testCopy :: TestEnv -> Assertion
433−testCopy TestEnv{..} = do
434− execute_ conn "CREATE TEMPORARY TABLE copy_test (x int, y text)"
435− copy_ conn "COPY copy_test FROM STDIN (FORMAT CSV)"
436− mapM_ (putCopyData conn) copyRows
437− putCopyEnd conn
438− copy_ conn "COPY copy_test FROM STDIN (FORMAT CSV)"
439− mapM_ (putCopyData conn) abortRows
440− putCopyError conn "aborted"
441− -- Hmm, does postgres always produce \n as an end-of-line here, or
442− -- are there cases where it will use a \r\n as well?
443− copy_ conn "COPY copy_test TO STDOUT (FORMAT CSV)"
444− rows <- loop []
445− sort rows @?= sort copyRows
446− -- Now, let's just verify that the connection state is back to ready,
447− -- so that we can issue more queries:
448− [Only (x::Int)] <- query_ conn "SELECT 2 + 2"
449− x @?= 4
450− -- foldCopyData
451− copy_ conn "COPY copy_test TO STDOUT (FORMAT CSV)"
452− (acc, count) <- foldCopyData conn
453− (\acc row -> return (row:acc))
454− (\acc count -> return (acc, count))
455− []
456− sort acc @?= sort copyRows
457− count @?= 2
458− where
459− copyRows = ["1,foo\n"
460− ,"2,bar\n"]
461− abortRows = ["3,baz\n"]
462− loop rows = do
463− mrow <- getCopyData conn
464− case mrow of
465− CopyOutDone _ -> return rows
466− CopyOutRow row -> loop (row:rows)
467−
468−testCopyFailures :: TestEnv -> TestTree
469−testCopyFailures env = testGroup "Copy failures"
470− $ map ($ env)
471− [ testCopyUniqueConstraintError
472− , testCopyMalformedError
473− ]
474−
475−goldenTest :: TestName -> IO BL.ByteString -> TestTree
476−goldenTest testName =
477− goldenVsStringDiff testName diff (resultsDir </> fileName<.>"expected")
478− where
479− resultsDir = "test" </> "results"
480− fileName = map normalize testName
481− normalize c | not (isAlpha c) = '-'
482− | otherwise = c
483− diff ref new = ["diff", "-u", ref, new]
484−
485−-- | Test that we provide a sensible error message on failure
486−testCopyUniqueConstraintError :: TestEnv -> TestTree
487−testCopyUniqueConstraintError TestEnv{..} =
488− goldenTest "unique constraint violation"
489− $ handle (\(SomeException exc) -> return $ BL.pack $ show exc) $ do
490− execute_ conn "CREATE TEMPORARY TABLE copy_unique_constraint_error_test (x int PRIMARY KEY, y text)"
491− copy_ conn "COPY copy_unique_constraint_error_test FROM STDIN (FORMAT CSV)"
492− mapM_ (putCopyData conn) copyRows
493− _n <- putCopyEnd conn
494− return BL.empty
495− where
496− copyRows = ["1,foo\n"
497− ,"2,bar\n"
498− ,"1,baz\n"]
499−
500−testCopyMalformedError :: TestEnv -> TestTree
501−testCopyMalformedError TestEnv{..} =
502− goldenTest "malformed input"
503− $ handle (\(SomeException exc) -> return $ BL.pack $ show exc) $ do
504− execute_ conn "CREATE TEMPORARY TABLE copy_malformed_input_error_test (x int PRIMARY KEY, y text)"
505− copy_ conn "COPY copy_unique_constraint_error_test FROM STDIN (FORMAT CSV)"
506− mapM_ (putCopyData conn) copyRows
507− _n <- putCopyEnd conn
508− return BL.empty
509− where
510− copyRows = ["1,foo\n"
511− ,"2,bar\n"
512− ,"z,baz\n"]
513−
514−testTimeout :: TestEnv -> Assertion
515−testTimeout TestEnv{..} =
516− withConn $ \c -> do
517− start_t <- getCurrentTime
518− res <- timeout 200000 $ do
519− withTransaction c $ do
520− query_ c "SELECT pg_sleep(1)" :: IO [Only ()]
521− end_t <- getCurrentTime
522− assertBool "Timeout did not occur" (res == Nothing)
523−#if !defined(mingw32_HOST_OS)
524−-- At the moment, you cannot timely abandon queries with async exceptions on
525−-- Windows.
526− let d = end_t `diffUTCTime` start_t
527− assertBool "Timeout didn't work in a timely fashion" (0.1 < d && d < 0.6)
528−#endif
529−
530−testDouble :: TestEnv -> Assertion
531−testDouble TestEnv{..} = do
532− [Only (x :: Double)] <- query_ conn "SELECT 'NaN'::float8"
533− assertBool "expected NaN" (isNaN x)
534− [Only (x :: Double)] <- query_ conn "SELECT 'Infinity'::float8"
535− x @?= (1 / 0)
536− [Only (x :: Double)] <- query_ conn "SELECT '-Infinity'::float8"
537− x @?= (-1 / 0)
538−
539−
540−testGeneric1 :: TestEnv -> Assertion
541−testGeneric1 TestEnv{..} = do
542− roundTrip conn (Gen1 123)
543− where
544− roundTrip conn x0 = do
545− r <- query conn "SELECT ?::int" (x0 :: Gen1)
546− r @?= [x0]
547−
548−testGeneric2 :: TestEnv -> Assertion
549−testGeneric2 TestEnv{..} = do
550− roundTrip conn (Gen2 123 "asdf")
551− where
552− roundTrip conn x0 = do
553− r <- query conn "SELECT ?::int, ?::text" x0
554− r @?= [x0]
555−
556−testGeneric3 :: TestEnv -> Assertion
557−testGeneric3 TestEnv{..} = do
558− roundTrip conn (Gen3 123 "asdf" True)
559− where
560− roundTrip conn x0 = do
561− r <- query conn "SELECT ?::int, ?::text, ?::bool" x0
562− r @?= [x0]
563−
564−data Gen1 = Gen1 Int
565− deriving (Show,Eq,Generic)
566−instance FromRow Gen1
567−instance ToRow Gen1
568−
569−data Gen2 = Gen2 Int Text
570− deriving (Show,Eq,Generic)
571−instance FromRow Gen2
572−instance ToRow Gen2
573−
574−data Gen3 = Gen3 Int Text Bool
575− deriving (Show,Eq,Generic)
576−instance FromRow Gen3
577−instance ToRow Gen3
578−
579−data TestException
580− = TestException
581− deriving (Eq, Show, Typeable)
582−
583−instance Exception TestException
584−
585−expectError :: Exception e => (e -> Bool) -> IO a -> IO Bool
586−expectError p io =
587− (io >> return False) `E.catch` \ex ->
588− if p ex then return True else throwIO ex
589−
590−isUniqueViolation :: SqlError -> Bool
591−isUniqueViolation SqlError{..} = sqlState == "23505"
592−
593−isSyntaxError :: SqlError -> Bool
594−isSyntaxError SqlError{..} = sqlState == "42601"
595−
596−isFormatError :: Int -> FormatError -> Bool
597−isFormatError i FormatError{..}
598− | null fmtMessage = False
599− | otherwise = fmtMessage == concat [ show i
600− , " single '?' characters, but "
601− , show (length fmtParams)
602− , " parameters"
603− ]
604−------------------------------------------------------------------------
605−
606−-- | Action for connecting to the database that will be used for testing.
607−--
608−-- Note that some tests, such as Notify, use multiple connections, and assume
609−-- that 'testConnect' connects to the same database every time it is called.
610−withTestEnv :: ByteString -> (TestEnv -> IO a) -> IO a
611−withTestEnv connstr cb =
612− withConn $ \conn -> do
613− -- currently required for interval to work.
614− -- we also test that this doesn't interfere with anything else
615− execute_ conn "SET intervalstyle TO 'iso_8601'"
616−
617− cb TestEnv
618− { conn = conn
619− , withConn = withConn
620− }
621− where
622− withConn = bracket (connectPostgreSQL connstr) close
623−
624−main :: IO ()
625−main = withConnstring $ \connstring -> do
626− withTestEnv connstring (defaultMain . tests)
627−
628−withConnstring :: (BS8.ByteString -> IO ()) -> IO ()
629−withConnstring kont = do
630− env <- getEnvironment
631− case lookup "DATABASE_CONNSTRING" env of
632− Just s -> kont (BS8.pack (special s))
633− Nothing -> case lookup "GITHUB_ACTIONS" env of
634− Just "true" -> kont (BS8.pack gha)
635− _ -> putStrLn "Set DATABASE_CONNSTRING environment variable"
636− where
637− -- https://www.appveyor.com/docs/services-databases/
638− special "appveyor" = "dbname='TestDb' user='postgres' password='Password12!'"
639− special "travis" = ""
640− special s = s
641−
642− gha = unwords
643− [ "dbname='postgres'"
644− , "user='postgres'"
645− , "password='postgres'"
646− , "host='postgres'"
647− , "port=5432"
648− ]
test/Notify.hsdeleted−41
@@ -1,41 +0,0 @@
1−module Notify (testNotify) where
2−
3−import Common
4−
5−import Control.Applicative
6−import Control.Concurrent
7−import Control.Monad
8−import Data.Function
9−import Data.List
10−import Database.PostgreSQL.Simple.Notification
11−
12−import qualified Data.ByteString as B
13−
14−-- TODO: Test with payload, but only for PostgreSQL >= 9.0
15−-- (when that feature was introduced).
16−
17−testNotify :: TestEnv -> Assertion
18−testNotify TestEnv{..} =
19− withConn $ \conn2 -> do
20− execute_ conn "LISTEN foo"
21− execute_ conn "LISTEN bar"
22−
23− results_mv <- newEmptyMVar
24− forkIO $ replicateM 2 (getNotification conn)
25− >>= putMVar results_mv
26−
27− threadDelay 100000
28−
29− execute_ conn2 "NOTIFY foo"
30− execute_ conn2 "NOTIFY bar"
31−
32− [n1, n2] <- sortBy (compare `on` notificationChannel)
33− <$> takeMVar results_mv
34−
35− assertEqual "n1" "bar" (notificationChannel n1)
36− assertEqual "n2" "foo" (notificationChannel n2)
37−
38− -- Other sanity checks
39− assertEqual "Server PIDs match" (notificationPid n1) (notificationPid n2)
40− assertBool "notificationData is empty" $
41− all (B.null . notificationData) [n1, n2]
test/Serializable.hsdeleted−68
@@ -1,68 +0,0 @@
1−module Serializable (testSerializable) where
2−
3−import Common
4−import Control.Concurrent
5−import Control.Exception as E
6−import Data.IORef
7−import Database.PostgreSQL.Simple.Transaction
8−
9−initCounter :: Connection -> IO ()
10−initCounter conn = do
11− 0 <- execute_ conn "DROP TABLE IF EXISTS testSerializableCounter;\
12− \ CREATE TABLE testSerializableCounter (n INT)"
13− 1 <- execute_ conn "INSERT INTO testSerializableCounter VALUES (0)"
14− return ()
15−
16−getCounter :: Connection -> IO Int
17−getCounter conn = do
18− [Only n] <- query_ conn "SELECT n FROM testSerializableCounter"
19− return n
20−
21−putCounter :: Connection -> Int -> IO ()
22−putCounter conn n = do
23− 1 <- execute conn "UPDATE testSerializableCounter SET n=?" (Only n)
24− return ()
25−
26−testSerializable :: TestEnv -> Assertion
27−testSerializable TestEnv{..} =
28− withConn $ \conn2 -> do
29− initCounter conn
30−
31− attemptCounter <- newIORef (0 :: Int)
32− readyToBother <- newEmptyMVar
33− bothered <- newEmptyMVar
34− finished <- newEmptyMVar
35−
36− _ <- forkIO $ do
37− withTransactionSerializable conn2 $ do
38− modifyIORef attemptCounter (+1)
39− n <- getCounter conn2
40− True <- tryPutMVar readyToBother ()
41− readMVar bothered
42− putCounter conn2 (n+1)
43− putMVar finished ()
44−
45− takeMVar readyToBother
46− withTransactionSerializable conn $ do
47− n <- getCounter conn
48− putCounter conn (n+1)
49− True <- tryPutMVar bothered ()
50−
51− takeMVar finished
52−
53− ac <- readIORef attemptCounter
54− assertEqual "attemptCounter" 2 ac
55−
56− ok <- E.catch (do withTransactionSerializable conn (fail "Whoops")
57− return False)
58− (\(_ :: IOException) -> return True)
59− assertBool "Exceptions (besides serialization failure) should be\
60− \ propagated through withTransactionSerializable"
61− ok
62−
63− -- Make sure transaction isn't dangling
64− 1 <- execute_ conn "UPDATE testSerializableCounter SET n=12345"
65− 0 <- execute_ conn "ROLLBACK"
66− -- This prints "NOTICE: there is no transaction in progress"
67− [Only (12345 :: Int)] <- query_ conn "SELECT n FROM testSerializableCounter"
68− return ()
test/Time.hsdeleted−119
@@ -1,119 +0,0 @@
1−{-# LANGUAGE QuasiQuotes #-}
2−
3−{-
4−
5−Testing strategies:
6−
7−fromString . toString == id ** Todo?
8−
9−toString . fromString == almost id ** Todo?
10−
11−postgresql -> haskell -> postgresql * Done
12−
13−haskell -> postgresql -> haskell ** Todo?
14−
15−But still, what we really want to establish is that the two values
16−correspond; for example, a conversion that consistently added hour
17−when printed to a string and subtracted an hour when parsed from string
18−would still pass these tests.
19−
20−
21−Right now, we are checking that 1400+ timestamps in the range of 1860 to
22−2060 round trip from postgresql to haskell and back in 5 different timezones.
23−In addition to UTC, the four timezones were selected so that 2 have a positive
24−offset, and 2 have a negative offset, and that 2 have an offset of a
25−whole number of hours, while the other two do not.
26−
27−It may be worth adding a few more timezones to ensure better test coverage.
28−
29−We are checking a handful of selected timestamps to ensure we hit
30−various corner-cases in the code, in addition to 1400 timestamps randomly
31−generated with granularity of seconds down to microseconds in powers of ten.
32−
33−-}
34−
35−module Time (testTime) where
36−
37−import Common
38−import Control.Monad(forM_, replicateM_)
39−import Data.Time.Compat
40−import Data.ByteString(ByteString)
41−import Database.PostgreSQL.Simple.SqlQQ
42−
43−numTests :: Int
44−numTests = 200
45−
46−testTime :: TestEnv -> Assertion
47−testTime env@TestEnv{..} = do
48− initializeTable env
49− execute_ conn "SET timezone TO 'UTC'"
50− checkRoundTrips env "1860-01-01 00:00:00+00"
51− execute_ conn "SET timezone TO 'America/Chicago'" -- -5:00
52− checkRoundTrips env "1883-11-18 12:00:00-06"
53− execute_ conn "SET timezone TO 'Asia/Tokyo'" -- +9:00
54− checkRoundTrips env "1888-01-01 00:00:00+09"
55− execute_ conn "SET timezone TO 'Asia/Kathmandu'" -- +5:45
56− checkRoundTrips env "1919-12-31 23:48:44+05:30"
57− execute_ conn "SET timezone TO 'America/St_Johns'" -- -3:30
58− checkRoundTrips env "1935-03-30 00:00:52-03:30"
59−
60−initializeTable :: TestEnv -> IO ()
61−initializeTable TestEnv{..} = withTransaction conn $ do
62− execute_ conn
63− [sql| CREATE TEMPORARY TABLE testtime
64− ( x serial, y timestamptz, PRIMARY KEY(x) ) |]
65−
66− let test :: ByteString -> IO () = \x -> do
67− execute conn [sql|
68− INSERT INTO testtime (y) VALUES (?)
69− |] (Only x)
70− return ()
71− -- America/Chicago
72− test "1883-11-18 11:59:59-05:50:36"
73− test "1883-11-18 12:09:23-05:50:36"
74− test "1883-11-18 12:00:00-06"
75− -- Asia/Tokyo
76− test "1887-12-31 23:59:59+09:18:59"
77− test "1888-01-01 00:18:58+09:18:59"
78− test "1888-01-01 00:00:00+09"
79− -- Asia/Kathmandu
80− test "1919-12-31 23:59:59+05:41:16"
81− test "1919-12-31 23:48:44+05:30"
82− test "1985-12-31 23:59:59+05:30"
83− test "1986-01-01 00:15:00+05:45"
84− -- America/St_Johns
85− test "1935-03-29 23:59:59-03:30:52"
86− test "1935-03-30 00:00:52-03:30"
87−
88− -- While the above special cases are probably a decent start, there
89− -- are probably more that are well worth adding to ensure better
90− -- coverage.
91−
92− let pop :: ByteString -> Double -> IO () = \x y ->
93− replicateM_ numTests $ execute conn
94− [sql| INSERT INTO testtime (y) VALUES
95− ('1860-01-01 00:00:00+00'::timestamptz
96− + ?::interval * ROUND(RANDOM() * ?)) |] (x,y)
97− pop "1 microsecond" 6.3113904e15
98− pop "10 microseconds" 6.3113904e14
99− pop "100 microseconds" 6.3113904e13
100− pop "1 millisecond" 6.3113904e12
101− pop "10 milliseconds" 6.3113904e11
102− pop "100 milliseconds" 6.3113904e10
103− pop "1 second" 6.3113904e9
104−
105−checkRoundTrips :: TestEnv -> ByteString -> IO ()
106−checkRoundTrips TestEnv{..} limit = do
107− yxs :: [(UTCTime, Int)] <- query_ conn [sql| SELECT y, x FROM testtime |]
108− forM_ yxs $ \yx -> do
109− res <- query conn [sql| SELECT y=? FROM testtime WHERE x=? |] yx
110− assertBool "UTCTime did not round-trip from SQL to Haskell and back" $
111− res == [Only True]
112−
113− yxs :: [(ZonedTime, Int)] <- query conn [sql|
114− SELECT y, x FROM testtime WHERE y > ?
115− |] (Only limit)
116− forM_ yxs $ \yx -> do
117− res <- query conn [sql| SELECT y=? FROM testtime WHERE x=? |] yx
118− assertBool "ZonedTime did not round-trip from SQL to Haskell and back" $
119− res == [Only True]
test/WRITING-TESTSdeleted−42
@@ -1,42 +0,0 @@
1−Do what is EASIEST. Having a test at all is more important than,
2−say, pretty failure output.
3−
4−To add a test, open Main.hs, and add an entry to tests, e.g.:
5−
6− tests :: [TestEnv -> Test]
7− tests =
8− [ ...
9− , TestLabel "Foo" . testFoo
10− ]
11−
12− testFoo :: TestEnv -> Test
13− testFoo TestEnv{..} = TestCase $ do
14− meaningOfLife @?= 42
15− 2013 <- getCurrentYear
16− return ()
17−
18−Mnemonic for HUnit @=? and @?= operators:
19−
20− * `?` is the value whose correctness is in question
21−
22− * `=` is the expected value
23−
24−If you move a test to a separate module, don't forget to add it to
25−"other-modules" in postgresql-simple.cabal . Otherwise, the module will be
26−left out of the tarball generated by `cabal sdist`, and tests will fail to
27−build when installing from Hackage.
28−
29−'TestEnv' (defined in Common.hs) contains information for accessing the
30−database. `TestEnv{..}` is a record wildcard which brings the following
31−definitions into scope:
32−
33− conn :: Connection
34− withConn :: (Connection -> IO a) -> IO a
35−
36−'conn' is a database connection shared by all the tests.
37−'withConn' can be used to open additional connections (to the same database).
38−
39−'Test' is basically a tree of 'Assertion's, where 'Assertion' is just a type
40−alias for IO (). See the [HUnit documentation][1] for more information.
41−
42− [1]: http://hackage.haskell.org/packages/archive/HUnit/latest/doc/html/Test-HUnit-Base.html
test/results/malformed-input.expecteddeleted−4
@@ -1,4 +0,0 @@
1−user error (Database.PostgreSQL.Simple.Copy.putCopyEnd: failed to parse command status
2−Connection error: ERROR: invalid input syntax for type integer: "z"
3−CONTEXT: COPY copy_unique_constraint_error_test, line 3, column x: "z"
4−)
test/results/unique-constraint-violation.expecteddeleted−5
@@ -1,5 +0,0 @@
1−user error (Database.PostgreSQL.Simple.Copy.putCopyEnd: failed to parse command status
2−Connection error: ERROR: duplicate key value violates unique constraint "copy_unique_constraint_error_test_pkey"
3−DETAIL: Key (x)=(1) already exists.
4−CONTEXT: COPY copy_unique_constraint_error_test, line 3
5−)
tools/GenBuiltinTypes.hsnew file+185
@@ -0,0 +1,185 @@
1+------------------------------------------------------------------------------
2+-- |
3+-- Module: GenBuiltinTypes
4+-- Copyright: (c) 2011 Leon P Smith
5+-- License: BSD3
6+-- Maintainer: Leon P Smith <leon@melding-monads.com>
7+-- Stability: experimental
8+--
9+-- Queries a PostgreSQL for the Object IDs associated with built-in typenames,
10+-- and generates Haskell boilerplate to handle this mapping. The subset
11+-- of types handled this way is easily modifiable, but note that
12+-- user-defined types do not have stable oids, and can differ between
13+-- databases or dumps/reloads.
14+------------------------------------------------------------------------------
15+
16+{-# LANGUAGE OverloadedStrings, QuasiQuotes, ViewPatterns #-}
17+module GenBuiltinTypes where
18+
19+import Prelude hiding ((++))
20+
21+import StringsQQ
22+import Database.PostgreSQL.Simple
23+import qualified Data.ByteString.Char8 as B
24+import qualified Data.ByteString.Lazy as L
25+import qualified Blaze.ByteString.Builder as Blaze
26+import qualified Blaze.ByteString.Builder.ByteString as Blaze
27+import qualified Blaze.ByteString.Builder.Char8 as Blaze
28+import Data.String
29+import Data.List ( sort, intersperse )
30+import qualified Data.Map as Map
31+
32+import Data.Monoid
33+
34+(++) :: Monoid a => a -> a -> a
35+(++) = mappend
36+infixr 5 ++
37+
38+type Map = Map.Map B.ByteString Int
39+builtins :: [(B.ByteString, B.ByteString)]
40+builtins = [strings|
41+bool
42+bytea
43+char
44+name
45+int8
46+int2
47+int4
48+regproc
49+text
50+oid
51+tid
52+xid
53+cid
54+xml
55+point
56+lseg
57+path
58+box
59+polygon
60+line
61+cidr
62+float4
63+float8
64+abstime
65+reltime
66+tinterval
67+unknown
68+circle
69+money
70+macaddr
71+inet
72+bpchar
73+varchar
74+date
75+time
76+timestamp
77+timestamptz TimestampWithTimeZone
78+interval
79+timetz TimeWithTimeZone
80+bit
81+varbit
82+numeric
83+refcursor
84+record
85+void
86+|]
87+
88+instance IsString Blaze.Builder where
89+ fromString = Blaze.fromByteString . fromString
90+
91+main = do
92+ conn <- connect ConnectInfo {
93+ connectHost = "127.0.0.1",
94+ connectPort = 5432,
95+ connectUser = "aliadmin",
96+ connectPassword = "aliadmin",
97+ connectDatabase = "postgres"
98+ }
99+ xs <- query conn "SELECT typname::text, oid::int\
100+ \ FROM pg_type\
101+ \ WHERE typname IN ?"
102+ (Only (In (sort (map fst builtins))))
103+ close conn
104+ let oidmap = Map.fromList xs :: Map
105+ L.writeFile "../src/Database/PostgreSQL/Simple/BuiltinTypes.hs"
106+ (Blaze.toLazyByteString (renderFile oidmap builtins))
107+
108+padCons str = fromString (replicate (len - B.length str) ' ')
109+ where
110+ len = maximum (map (B.length . snd) builtins)
111+
112+renderOid oidmap x
113+ = case Map.lookup x oidmap of
114+ Nothing -> error (B.unpack x)
115+ Just (show -> n) -> fromString (replicate (4 - length n) ' ')
116+ ++ fromString n
117+
118+renderOid' oidmap x
119+ = case Map.lookup x oidmap of
120+ Nothing -> error (B.unpack x)
121+ Just (show -> n) -> fromString n
122+ ++ fromString (replicate (4 - length n) ' ')
123+
124+bs = Blaze.fromByteString
125+
126+renderFile :: Map -> [(B.ByteString,B.ByteString)] -> Blaze.Builder
127+renderFile oidmap builtins = ([longstring|
128+{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}
129+
130+------------------------------------------------------------------------------
131+-- |
132+-- Module: Database.PostgreSQL.Simple.BuiltinTypes
133+-- Copyright: (c) 2011 Leon P Smith
134+-- License: BSD3
135+-- Maintainer: Leon P Smith <leon@melding-monads.com>
136+-- Stability: experimental
137+--
138+------------------------------------------------------------------------------
139+
140+-- Note that this file is generated by tools/GenBuiltinTypes.hs, and should
141+-- not be edited directly
142+
143+module Database.PostgreSQL.Simple.BuiltinTypes
144+ ( BuiltinType (..)
145+ , builtin2oid
146+ , oid2builtin
147+ , builtin2typname
148+ , oid2typname
149+ ) where
150+
151+import Data.Typeable
152+import Data.ByteString (ByteString)
153+import qualified Database.PostgreSQL.LibPQ as PQ
154+
155+data BuiltinType
156+ = |] ++ mconcat (intersperse "\n | " (map (Blaze.fromByteString . snd) builtins)) ++ [longstring|
157+
158+ deriving (Eq, Ord, Enum, Bounded, Read, Show, Typeable)
159+
160+builtin2oid :: BuiltinType -> PQ.Oid
161+builtin2oid typ = PQ.Oid $ case typ of
162+|] ++ mconcat [mconcat [ " ", bs y, padCons y, " -> "
163+ , renderOid oidmap x, "\n" ]
164+ | (x,y) <- builtins ] ++ [longstring|
165+
166+oid2builtin :: PQ.Oid -> Maybe BuiltinType
167+oid2builtin (PQ.Oid x) = case x of
168+|] ++ mconcat [mconcat [ " ", renderOid' oidmap x, " -> Just ", bs y, "\n" ]
169+ | (x,y) <- builtins ] ++ [longstring|
170+ _ -> Nothing
171+
172+builtin2typname :: BuiltinType -> ByteString
173+builtin2typname typ = case typ of
174+|] ++ mconcat [mconcat [ " ", bs y, padCons y, " -> ", bs x, "\n"]
175+ | (x,y) <- builtins] ++ [longstring|
176+
177+oid2typname :: PQ.Oid -> Maybe ByteString
178+oid2typname (PQ.Oid x) = case x of
179+|] ++ mconcat [mconcat [ " ", renderOid' oidmap x, " -> Just ", bs x, "\n" ]
180+ | (x,y) <- builtins ]
181+ ++ " _ -> Nothing\n"
182+ ++ mconcat [mconcat [ "\n"
183+ , bs x, " :: ByteString\n"
184+ , bs x, " = \"", bs x, "\"\n"]
185+ | (x,_y) <- builtins ])
tools/GenTypeInfo.hsdeleted−381
@@ -1,381 +0,0 @@
1−------------------------------------------------------------------------------
2−-- |
3−-- Module: GenTypeInfo
4−-- Copyright: (c) 2011-2012 Leon P Smith
5−-- License: BSD3
6−-- Maintainer: Leon P Smith <leon@melding-monads.com>
7−-- Stability: experimental
8−--
9−-- Queries a PostgreSQL database for the Object IDs and other type
10−-- information associated with typenames, and generates a module with
11−-- data constants representing part of the pg_type table.
12−--
13−-- Note that only some of the built-in types have stable type OIDs, and
14−-- thus a TypeInfo module that contains user-defined types is liable to
15−-- not work across different database instances. For these types, it is
16−-- better for `FromField` to use the `typeinfo` operator that works for
17−-- any type, whether or not it is in this module.
18−--
19−-- `typeinfo` works because postgresql-simple will dynamically query
20−-- the pg_types table the first time it receives a type OID it doesn't
21−-- know about. It then constructs a TypeInfo record and stores it in
22−-- a per-connection cache for later use.
23−--
24−-- @
25−-- runghc -itools tools/GenTypeInfo.hs
26−-- @
27−------------------------------------------------------------------------------
28−
29−{-# LANGUAGE OverloadedStrings, QuasiQuotes, ViewPatterns, RecordWildCards #-}
30−
31−module GenBuiltinTypes where
32−
33−import Prelude hiding ((++), concat)
34−
35−import StringsQQ
36−import Control.Arrow((&&&))
37−import Control.Applicative
38−import Control.Exception(bracket)
39−import Database.PostgreSQL.Simple
40−import Database.PostgreSQL.Simple.FromRow
41−import Database.PostgreSQL.Simple.Ok
42−import Database.PostgreSQL.Simple.Types(Oid(..))
43−import Database.PostgreSQL.Simple.SqlQQ
44−import qualified Data.ByteString.Char8 as B8
45−import Data.ByteString(ByteString)
46−import qualified Data.ByteString.Lazy as L
47−import qualified Data.ByteString.Builder as Blaze
48−import Data.String
49−import Data.List ( sort, intersperse )
50−import qualified Data.Map as Map
51−
52−import Data.Monoid
53−
54−(++) :: Monoid a => a -> a -> a
55−(++) = mappend
56−infixr 5 ++
57−
58−concat :: Monoid a => [a] -> a
59−concat = mconcat
60−
61−data TypeInfo = TypeInfo
62− { typoid :: Oid
63− , typcategory :: Char
64− , typdelim :: Char
65− , typname :: ByteString
66− , typelem :: Oid
67− , rngsubtype :: Maybe Oid
68− }
69−
70−instance FromRow TypeInfo where
71− fromRow = TypeInfo <$> field <*> field <*> field <*> field <*> field <*> field
72−
73−type NameMap = Map.Map B8.ByteString TypeInfo
74−
75−type OidMap = Map.Map Oid TypeInfo
76−
77−type TypeName = (B8.ByteString, B8.ByteString)
78−
79−type TypeNames = [TypeName]
80−
81−-- Note that the following syntax is "pgName <whitespace> hsName", though
82−-- they default to the same thing if there is only one identifier
83−typeNames :: TypeNames
84−typeNames = [typenames|
85−bool
86−bytea
87−char
88−name
89−int8
90−int2
91−int4
92−regproc
93−text
94−oid
95−tid
96−xid
97−cid
98−xml
99−point
100−lseg
101−path
102−box
103−polygon
104−line
105−cidr
106−float4
107−float8
108−unknown
109−circle
110−money
111−macaddr
112−inet
113−bpchar
114−varchar
115−date
116−time
117−timestamp
118−timestamptz
119−interval
120−timetz
121−bit
122−varbit
123−numeric
124−refcursor
125−record
126−void
127−_record array_record
128−regprocedure
129−regoper
130−regoperator
131−regclass
132−regtype
133−uuid
134−json
135−jsonb
136−int2vector
137−oidvector
138−_xml array_xml
139−_json array_json
140−_line array_line
141−_cidr array_cidr
142−_circle array_circle
143−_money array_money
144−_bool array_bool
145−_bytea array_bytea
146−_char array_char
147−_name array_name
148−_int2 array_int2
149−_int2vector array_int2vector
150−_int4 array_int4
151−_regproc array_regproc
152−_text array_text
153−_tid array_tid
154−_xid array_xid
155−_cid array_cid
156−_oidvector array_oidvector
157−_bpchar array_bpchar
158−_varchar array_varchar
159−_int8 array_int8
160−_point array_point
161−_lseg array_lseg
162−_path array_path
163−_box array_box
164−_float4 array_float4
165−_float8 array_float8
166−_polygon array_polygon
167−_oid array_oid
168−_macaddr array_macaddr
169−_inet array_inet
170−_timestamp array_timestamp
171−_date array_date
172−_time array_time
173−_timestamptz array_timestamptz
174−_interval array_interval
175−_numeric array_numeric
176−_timetz array_timetz
177−_bit array_bit
178−_varbit array_varbit
179−_refcursor array_refcursor
180−_regprocedure array_regprocedure
181−_regoper array_regoper
182−_regoperator array_regoperator
183−_regclass array_regclass
184−_regtype array_regtype
185−_uuid array_uuid
186−_jsonb array_jsonb
187−int4range
188−_int4range
189−numrange
190−_numrange
191−tsrange
192−_tsrange
193−tstzrange
194−_tstzrange
195−daterange
196−_daterange
197−int8range
198−_int8range
199−|]
200−
201−connectionString = "dbname=postgres"
202−
203−withPostgreSQL = bracket (connectPostgreSQL connectionString) close
204−
205−getTypeInfos :: TypeNames -> IO (OidMap, NameMap)
206−getTypeInfos typnames = withPostgreSQL $ \conn -> do
207− infos <- query conn [sql|
208− WITH types AS
209− (SELECT oid, typcategory, typdelim, typname, typelem
210− FROM pg_type WHERE typname IN ?)
211− SELECT types.*, rngsubtype FROM types LEFT JOIN pg_range ON oid = rngtypid
212− |] (Only (In (sort (map pg typnames))))
213− loop conn (oidMap infos) (nameMap infos) infos
214− where
215− oidMap = Map.fromList . map (typoid &&& id)
216− nameMap = Map.fromList . map (typname &&& id)
217− loop conn oids names infos = do
218− let unknowns = [ x | x <- map typelem infos ++
219− [ x | Just x <- map rngsubtype infos ],
220− x /= Oid 0,
221− not (Map.member x oids) ]
222− case unknowns of
223− [] -> return (oids, names)
224− (_:_) -> do
225− infos' <- query conn [sql|
226− WITH types AS
227− (SELECT oid, typcategory, typdelim, typname, typelem
228− FROM pg_type WHERE oid IN ?)
229− SELECT types.*, rngsubtype
230− FROM types LEFT JOIN pg_range ON oid = rngtypid
231− |] (Only (In (sort unknowns)))
232− let oids' = oids `Map.union` oidMap infos'
233− names' = names `Map.union` nameMap infos'
234− loop conn oids' names' infos'
235−
236−main = do
237− (oidmap, namemap) <- getTypeInfos typeNames
238− L.writeFile "src/Database/PostgreSQL/Simple/TypeInfo/Static.hs"
239− (Blaze.toLazyByteString (renderFile oidmap namemap typeNames))
240−
241−
242−showOid (Oid n) = show n
243−
244−renderOid :: NameMap -> TypeName -> Blaze.Builder
245−renderOid byName name
246− = case Map.lookup (pg name) byName of
247− Nothing -> error (B8.unpack (pg name))
248− Just (showOid . typoid -> n) -> fromString n
249− ++ fromString (replicate (4 - length n) ' ')
250−
251−renderElem :: OidMap -> Oid -> Blaze.Builder
252−renderElem byOid elemOid
253− | elemOid == Oid 0 = "Nothing"
254− | otherwise = case Map.lookup elemOid byOid of
255− Nothing -> error ("oid not found: " ++ show elemOid)
256− Just x -> "Just " ++ bs (typname x)
257−
258−renderTypeInfo :: OidMap -> TypeInfo -> TypeName -> Blaze.Builder
259−renderTypeInfo byOid info name
260− | typcategory info == 'A' || typname info == "_record" =
261− let (Just typelem_info) = Map.lookup (typelem info) byOid
262− typelem_hs_name =
263− case lookup (typname typelem_info) typeNames of
264− Nothing -> error ( "type not found: "
265− ++ B8.unpack( typname typelem_info)
266− ++ " (typelem of " ++ B8.unpack (typname info)
267− ++ ")")
268− Just x -> x
269− in concat
270− [ "\n"
271− , bs (hs name), " :: TypeInfo\n"
272− , bs (hs name), " = Array {\n"
273− , " typoid = ", bs (hs name), "Oid,\n"
274− , " typcategory = '", bs $ B8.singleton (typcategory info), "',\n"
275− , " typdelim = '", bs $ B8.singleton (typdelim info), "',\n"
276− , " typname = \"", bs (typname info), "\",\n"
277− , " typelem = ", bs typelem_hs_name, "\n"
278− , " }\n"
279− , "\n"
280− , bs (hs name), "Oid :: Oid\n"
281− , bs (hs name), "Oid = ", fromString (show (typoid info)), "\n"
282− , "{-# INLINE ", bs (hs name), "Oid #-}"
283− , "\n"
284− ]
285− | typcategory info == 'R' =
286− let (Just rngsubtype_oid) = rngsubtype info
287− (Just rngsubtype_info) = Map.lookup rngsubtype_oid byOid
288− rngsubtype_hs_name =
289− case lookup (typname rngsubtype_info) typeNames of
290− Nothing -> error ( "type not found: "
291− ++ B8.unpack (typname rngsubtype_info)
292− ++ " (rngsubtype of "
293− ++ B8.unpack (typname info) ++ ")")
294− Just x -> x
295− in concat
296− [ "\n"
297− , bs (hs name), " :: TypeInfo\n"
298− , bs (hs name), " = Range {\n"
299− , " typoid = ", bs (hs name), "Oid,\n"
300− , " typcategory = '", bs $ B8.singleton (typcategory info), "',\n"
301− , " typdelim = '", bs $ B8.singleton (typdelim info), "',\n"
302− , " typname = \"", bs (typname info), "\",\n"
303− , " rngsubtype = ", bs rngsubtype_hs_name, "\n"
304− , " }\n"
305− , "\n"
306− , bs (hs name), "Oid :: Oid\n"
307− , bs (hs name), "Oid = ", fromString (show (typoid info)), "\n"
308− , "{-# INLINE ", bs (hs name), "Oid #-}"
309− , "\n"
310− ]
311− | otherwise =
312− concat
313− [ "\n"
314− , bs (hs name), " :: TypeInfo\n"
315− , bs (hs name), " = Basic {\n"
316− , " typoid = ", bs (hs name), "Oid,\n"
317− , " typcategory = '", bs $ B8.singleton (typcategory info), "',\n"
318− , " typdelim = '", bs $ B8.singleton (typdelim info), "',\n"
319− , " typname = \"", bs (typname info), "\"\n"
320− , " }\n"
321− , "\n"
322− , bs (hs name), "Oid :: Oid\n"
323− , bs (hs name), "Oid = ", fromString (show (typoid info)), "\n"
324− , "{-# INLINE ", bs (hs name), "Oid #-}"
325− , "\n"
326− ]
327−
328−-- FIXME: add in any names that we need that we didn't specify, (i.e.
329−-- the "unknowns" in getTypeInfos
330−-- and munge them into a valid haskell identifier if needed.
331−getNames :: NameMap -> TypeNames -> TypeNames
332−getNames _ x = x
333−
334−bs = Blaze.byteString
335−
336−pg = fst
337−
338−hs = snd
339−
340−renderFile :: OidMap -> NameMap -> TypeNames -> Blaze.Builder
341−renderFile byOid byName names = ([longstring|
342−------------------------------------------------------------------------------
343−-- |
344−-- Module: Database.PostgreSQL.Simple.TypeInfo
345−-- Copyright: (c) 2011-2012 Leon P Smith
346−-- License: BSD3
347−-- Maintainer: Leon P Smith <leon@melding-monads.com>
348−-- Stability: experimental
349−--
350−-- This module contains portions of the @pg_type@ table that are relevant
351−-- to postgresql-simple and are believed to not change between PostgreSQL
352−-- versions.
353−--
354−------------------------------------------------------------------------------
355−
356−-- Note that this file is generated by tools/GenTypeInfo.hs, and should
357−-- not be edited directly
358−
359−module Database.PostgreSQL.Simple.TypeInfo.Static
360− ( TypeInfo(..)
361− , staticTypeInfo
362−|] ++ concat [ " , " ++ bs (hs name) ++ "\n"
363− ++ " , " ++ bs (hs name) ++ "Oid\n"
364− | name <- names ] ++ [longstring|
365− ) where
366−
367−import Database.PostgreSQL.LibPQ (Oid(..))
368−import Database.PostgreSQL.Simple.TypeInfo.Types
369−
370−staticTypeInfo :: Oid -> Maybe TypeInfo
371−staticTypeInfo (Oid x) = case x of
372−|] ++ concat [concat [ " ", renderOid byName name,
373− " -> Just ", bs (hs name), "\n"
374− ]
375− | name <- names ]
376− ++ [longstring|
377− _ -> Nothing
378−|]
379− ++ concat [ renderTypeInfo byOid typeInfo name
380− | name <- getNames byName names
381− , let (Just typeInfo) = Map.lookup (pg name) byName])
tools/StringsQQ.hs+1−12
@@ -1,6 +1,6 @@
11{-# LANGUAGE TemplateHaskell #-}
22
3−module StringsQQ (strings, longstring, typenames) where
3+module StringsQQ (strings, longstring) where
44
55import Language.Haskell.TH
66import Language.Haskell.TH.Quote
@@ -28,14 +28,3 @@ longstring = QuasiQuoter { quotePat = undefined,
2828
2929longstringExp ('\n':xs) = stringE xs
3030longstringExp xs = stringE xs
31−
32−typenames = QuasiQuoter { quotePat = undefined,
33− quoteType = undefined,
34− quoteExp = typenamesExp,
35− quoteDec = undefined }
36−
37−typenamesExp :: String -> Q Exp
38−typenamesExp = foldr (\x xs -> [e| $(x) : $(xs) |]) [e| [] |]
39− . map delta . filter (not . null) . map words . lines
40− where delta [xs] = delta [xs,xs]
41− delta [xs,ys] = [e| ( $(stringE xs) , $(stringE ys) ) |]