From e59266406b7c6aabee2f28534366fd79f9a164c9 Mon Sep 17 00:00:00 2001 From: Orion Kindel Date: Tue, 30 Apr 2024 15:46:10 -0500 Subject: [PATCH] feat: streaming STDIO support --- bun.lockb | Bin 8531 -> 9248 bytes package.json | 1 + spago.lock | 520 ++---------------------- spago.yaml | 3 +- src/Control.Monad.Postgres.Base.purs | 4 +- src/Control.Monad.Postgres.Cursor.purs | 4 +- src/Control.Monad.Postgres.Session.purs | 26 ++ src/Effect.Aff.Postgres.Client.js | 8 + src/Effect.Aff.Postgres.Client.purs | 15 +- 9 files changed, 93 insertions(+), 488 deletions(-) diff --git a/bun.lockb b/bun.lockb index ba2175d0c79503682b7a885cee65823a93249a40..9e01eec4014c4363cafe55ad33f8e482eb561a35 100755 GIT binary patch delta 1754 zcmbtVdr(wm6#u@vdto2keJCtwLdGyZq(CPDe)G_4;oHn=~grJ(Om8PpXgYsFn-X8OB}=`Tp9s^iuQvovBO_xht5Q zCulkR99EcRw9l^>_JO;nH+T7Z8{XUeZf_}{jJr*1EZ&U zvJOK&ALA<^L-0#`aF$gV8AFQzr+6o%2pVY^K{k4^4CCNbDPH~$R0@7!Cx<@4#|L3t z@bh0GL*>_|;1pI0%qQi$nz7|jqh6{_6!9yXAv`2PRg6#Di_ScBim)xl!zW-o z#xE?4fy~$?t+BXpQWCPX5e)$pvRYI;Xhq1<_?#K2SggzvBmU@J-hypa3R)utZ`D%T z>^9KK*erK2yA8kxnp3S6qZiKGJ5q9f*?)Uc&ld-$gv+mfY2Vjn{$$cqkkwFH{`dY} z9Xhl5ndY^}yY*iuJU!CB?xpsD7TY@)`Zg3Dy_TUI|Fm%$?*{wje^D=cJT#b}#?MZ8 zk8dqadi}u9&7GI5D|pXGeHSOMT${T7!mfMc=0MJ6g#_ z!xTp0_zW37YNaRWDyR&VqjV~dgr>YIA`w~_^&U-0)6+6=V?T4SF}BK z?%0H7}UD%+w2eeYK`?fP9KEdpNBfq4{(U3_?lSNzX;DzulWV#G4G$>GPExvc0KrEa^U}!oImKw9_7LsNb@w t2E=)xg*Q9Gi|mW=AK<2tu-%P|bi=#J$!0nW3?O5t<8hIo delta 1326 zcmb_cTS!!45dQ!F>~TG3J$p%Ax8rI{m{6do*(zmcYYEcU>;kpjFQAzfx~75^>5@SR zJ!I2EL`XeIm#0bssX)8<;3j2RK1xEV^bi;sJ!vz?-IpH1pn+ljZ*Kq0`Df0p^3mXx z{FVNJ$?nZdU-@Epo}FK1ubSzZ$+$66e5t%KYw&Tyi*4?gK4y};E4(bna4!JHA}yzz z=$wmeXDk}2U9e2CuKU?L=Q(38)V4O))K{b7J7Nv-XO!eU7MGFQyt?}4QZ|Y{HZx0J zq@m#y>qnf1_z_Bq>tUdCKOEuHycjA(fVYAo1;h^qTOuHTDqK z;31+tb`8=UA?|>qjxZ!UG#EtW0+%xkeTZ6|A)X4)5gm7Gu)-DM>Aj7v*Bq)-J$!HP z`P97@H5z}cYi7(pwll96dDRAk%&mV%61Von2Y5nFvGZMclS4V)PmhCU{A8aW#d_{g=Q{CB`hGXz{X(X9^iD0w745GT*+k=3%cVc#k zb#$HlDR@zSL4E;hA2Ar#J))n%q+TROal11sz2j)WyQ3W+pL}&6&WWXQWBqCjC}dL% zE@W1UF%Bb{9)IE|QZeFH4xAfn6TGc(A#dV0^QG$Bz4zS{EN&^rUO8lYE7eH6g$Ft` pdpAIzw?KE{zG3&#i=}IZd8>WyQ{3gL4@A~hIFwc28_XV;e*?UH;_Uzc diff --git a/package.json b/package.json index 305ab9f..4fe6d0e 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dependencies": { "decimal.js": "^10.4.3", "pg": "^8.11.3", + "pg-copy-streams": "^6.0.6", "postgres-range": "^1.1.4" } } diff --git a/spago.lock b/spago.lock index c636903..809e3c5 100644 --- a/spago.lock +++ b/spago.lock @@ -8,6 +8,7 @@ workspace: - arrays: ">=7.3.0 <8.0.0" - bifunctors: ">=6.0.0 <7.0.0" - control: ">=6.0.0 <7.0.0" + - csv-stream: ">=1.0.2 <2.0.0" - datetime: ">=6.1.0 <7.0.0" - effect: ">=4.0.0 <5.0.0" - either: ">=6.1.0 <7.0.0" @@ -59,6 +60,7 @@ workspace: - const - contravariant - control + - csv-stream - datetime - decimals - distributive @@ -133,487 +135,6 @@ workspace: - unlift - unsafe-coerce - variant - package_set: - address: - registry: 50.5.0 - compiler: ">=0.15.15 <0.16.0" - content: - abc-parser: 2.0.1 - ace: 9.1.0 - aff: 7.1.0 - aff-bus: 6.0.0 - aff-coroutines: 9.0.0 - aff-promise: 4.0.0 - aff-retry: 2.0.0 - affjax: 13.0.0 - affjax-node: 1.0.0 - affjax-web: 1.0.0 - ansi: 7.0.0 - applicative-phases: 1.0.0 - argonaut: 9.0.0 - argonaut-aeson-generic: 0.4.1 - argonaut-codecs: 9.1.0 - argonaut-core: 7.0.0 - argonaut-generic: 8.0.0 - argonaut-traversals: 10.0.0 - argparse-basic: 2.0.0 - array-builder: 0.1.2 - array-search: 0.5.6 - arraybuffer: 13.2.0 - arraybuffer-builder: 3.1.0 - arraybuffer-types: 3.0.2 - arrays: 7.3.0 - arrays-extra: 0.6.1 - arrays-zipper: 2.0.1 - ask: 1.0.0 - assert: 6.0.0 - assert-multiple: 0.3.4 - avar: 5.0.0 - b64: 0.0.8 - barbies: 1.0.1 - barlow-lens: 0.9.0 - bifunctors: 6.0.0 - bigints: 7.0.1 - bolson: 0.3.9 - bookhound: 0.1.7 - bower-json: 3.0.0 - call-by-name: 4.0.1 - canvas: 6.0.0 - canvas-action: 9.0.0 - cartesian: 1.0.6 - catenable-lists: 7.0.0 - chameleon: 1.0.0 - chameleon-halogen: 1.0.3 - chameleon-react-basic: 1.1.0 - chameleon-styled: 2.5.0 - chameleon-transformers: 1.0.0 - channel: 1.0.0 - checked-exceptions: 3.1.1 - choku: 1.0.1 - classless: 0.1.1 - classless-arbitrary: 0.1.1 - classless-decode-json: 0.1.1 - classless-encode-json: 0.1.3 - classnames: 2.0.0 - codec: 6.1.0 - codec-argonaut: 10.0.0 - codec-json: 1.1.0 - colors: 7.0.1 - concur-core: 0.5.0 - concur-react: 0.5.0 - concurrent-queues: 3.0.0 - console: 6.1.0 - const: 6.0.0 - contravariant: 6.0.0 - control: 6.0.0 - convertable-options: 1.0.0 - coroutines: 7.0.0 - css: 6.0.0 - css-frameworks: 1.0.1 - data-mvc: 0.0.2 - datetime: 6.1.0 - datetime-parsing: 0.2.0 - debug: 6.0.2 - decimals: 7.1.0 - default-values: 1.0.1 - deku: 0.9.23 - deno: 0.0.5 - dissect: 1.0.0 - distributive: 6.0.0 - dom-filereader: 7.0.0 - dom-indexed: 12.0.0 - dotenv: 4.0.3 - droplet: 0.6.0 - dts: 1.0.0 - dual-numbers: 1.0.2 - dynamic-buffer: 3.0.1 - echarts-simple: 0.0.1 - effect: 4.0.0 - either: 6.1.0 - elmish: 0.11.3 - elmish-enzyme: 0.1.1 - elmish-hooks: 0.10.0 - elmish-html: 0.8.2 - elmish-testing-library: 0.3.2 - email-validate: 7.0.0 - encoding: 0.0.9 - enums: 6.0.1 - env-names: 0.3.4 - error: 2.0.0 - eta-conversion: 0.3.2 - exceptions: 6.0.0 - exists: 6.0.0 - exitcodes: 4.0.0 - expect-inferred: 3.0.0 - fahrtwind: 2.0.0 - fallback: 0.1.0 - fast-vect: 1.2.0 - fetch: 4.1.0 - fetch-argonaut: 1.0.1 - fetch-core: 5.1.0 - fetch-yoga-json: 1.1.0 - fft-js: 0.1.0 - filterable: 5.0.0 - fix-functor: 0.1.0 - fixed-points: 7.0.0 - fixed-precision: 5.0.0 - flame: 1.3.0 - float32: 2.0.0 - fmt: 0.2.1 - foldable-traversable: 6.0.0 - foldable-traversable-extra: 0.0.6 - foreign: 7.0.0 - foreign-object: 4.1.0 - foreign-readwrite: 3.4.0 - forgetmenot: 0.1.0 - fork: 6.0.0 - form-urlencoded: 7.0.0 - formatters: 7.0.0 - framer-motion: 1.0.1 - free: 7.1.0 - freeap: 7.0.0 - freer-free: 0.0.1 - freet: 7.0.0 - functions: 6.0.0 - functor1: 3.0.0 - functors: 5.0.0 - fuzzy: 0.4.0 - gen: 4.0.0 - generate-values: 1.0.1 - generic-router: 0.0.1 - geojson: 0.0.5 - geometry-plane: 1.0.3 - gojs: 0.1.1 - grain: 3.0.0 - grain-router: 3.0.0 - grain-virtualized: 3.0.0 - graphs: 8.1.0 - group: 4.1.1 - halogen: 7.0.0 - halogen-bootstrap5: 5.3.2 - halogen-canvas: 1.0.0 - halogen-css: 10.0.0 - halogen-echarts-simple: 0.0.4 - halogen-formless: 4.0.3 - halogen-helix: 1.0.0 - halogen-hooks: 0.6.3 - halogen-hooks-extra: 0.9.0 - halogen-infinite-scroll: 1.1.0 - halogen-store: 0.5.4 - halogen-storybook: 2.0.0 - halogen-subscriptions: 2.0.0 - halogen-svg-elems: 8.0.0 - halogen-typewriter: 1.0.4 - halogen-vdom: 8.0.0 - halogen-vdom-string-renderer: 0.5.0 - halogen-xterm: 2.0.0 - heckin: 2.0.1 - heterogeneous: 0.6.0 - homogeneous: 0.4.0 - http-methods: 6.0.0 - httpurple: 4.0.0 - humdrum: 0.0.1 - hyrule: 2.3.8 - identity: 6.0.0 - identy: 4.0.1 - indexed-db: 1.0.0 - indexed-monad: 3.0.0 - int64: 3.0.0 - integers: 6.0.0 - interpolate: 5.0.2 - intersection-observer: 1.0.1 - invariant: 6.0.0 - jarilo: 1.0.1 - jelly: 0.10.0 - jelly-router: 0.3.0 - jelly-signal: 0.4.0 - jest: 1.0.0 - js-abort-controller: 1.0.0 - js-bigints: 2.2.1 - js-date: 8.0.0 - js-fetch: 0.2.1 - js-fileio: 3.0.0 - js-intl: 1.0.4 - js-iterators: 0.1.1 - js-maps: 0.1.2 - js-promise: 1.0.0 - js-promise-aff: 1.0.0 - js-timers: 6.1.0 - js-uri: 3.1.0 - json: 1.0.0 - json-codecs: 5.0.0 - justifill: 0.5.0 - jwt: 0.0.9 - labeled-data: 0.2.0 - language-cst-parser: 0.14.0 - lazy: 6.0.0 - lazy-joe: 1.0.0 - lcg: 4.0.0 - leibniz: 5.0.0 - leveldb: 1.0.1 - liminal: 1.0.1 - linalg: 6.0.0 - lists: 7.0.0 - literals: 1.0.2 - logging: 3.0.0 - logging-journald: 0.4.0 - lumi-components: 18.0.0 - machines: 7.0.0 - maps-eager: 0.4.1 - marionette: 1.0.0 - marionette-react-basic-hooks: 0.1.1 - marked: 0.1.0 - matrices: 5.0.1 - matryoshka: 1.0.0 - maybe: 6.0.0 - media-types: 6.0.0 - meowclient: 1.0.0 - midi: 4.0.0 - milkis: 9.0.0 - minibench: 4.0.1 - mmorph: 7.0.0 - monad-control: 5.0.0 - monad-logger: 1.3.1 - monad-loops: 0.5.0 - monad-unlift: 1.0.1 - monoid-extras: 0.0.1 - monoidal: 0.16.0 - morello: 0.4.0 - mote: 3.0.0 - motsunabe: 2.0.0 - mvc: 0.0.1 - mysql: 6.0.1 - n3: 0.1.0 - nano-id: 1.1.0 - nanoid: 0.1.0 - naturals: 3.0.0 - nested-functor: 0.2.1 - newtype: 5.0.0 - nextjs: 0.1.1 - nextui: 0.2.0 - node-buffer: 9.0.0 - node-child-process: 11.1.0 - node-event-emitter: 3.0.0 - node-execa: 5.0.0 - node-fs: 9.1.0 - node-glob-basic: 1.3.0 - node-http: 9.1.0 - node-http2: 1.1.1 - node-human-signals: 1.0.0 - node-net: 5.1.0 - node-os: 5.1.0 - node-path: 5.0.0 - node-process: 11.2.0 - node-readline: 8.1.0 - node-sqlite3: 8.0.0 - node-streams: 9.0.0 - node-tls: 0.3.1 - node-url: 7.0.1 - node-zlib: 0.4.0 - nonempty: 7.0.0 - now: 6.0.0 - npm-package-json: 2.0.0 - nullable: 6.0.0 - numberfield: 0.1.0 - numbers: 9.0.1 - oak: 3.1.1 - oak-debug: 1.2.2 - object-maps: 0.3.0 - ocarina: 1.5.4 - open-folds: 6.3.0 - open-memoize: 6.1.0 - open-pairing: 6.1.0 - options: 7.0.0 - optparse: 5.0.1 - ordered-collections: 3.2.0 - ordered-set: 0.4.0 - orders: 6.0.0 - owoify: 1.2.0 - pairs: 9.0.1 - parallel: 7.0.0 - parsing: 10.2.0 - parsing-dataview: 3.2.4 - partial: 4.0.0 - pathy: 9.0.0 - pha: 0.13.0 - phaser: 0.7.0 - phylio: 1.1.2 - pipes: 8.0.0 - pirates-charm: 0.0.1 - pmock: 0.9.0 - point-free: 1.0.0 - pointed-list: 0.5.1 - polymorphic-vectors: 4.0.0 - posix-types: 6.0.0 - precise: 6.0.0 - precise-datetime: 7.0.0 - prelude: 6.0.1 - prettier-printer: 3.0.0 - profunctor: 6.0.1 - profunctor-lenses: 8.0.0 - protobuf: 4.3.0 - psa-utils: 8.0.0 - psci-support: 6.0.0 - punycode: 1.0.0 - qualified-do: 2.2.0 - quantities: 12.2.0 - quickcheck: 8.0.1 - quickcheck-combinators: 0.1.3 - quickcheck-laws: 7.0.0 - quickcheck-utf8: 0.0.0 - random: 6.0.0 - rationals: 6.0.0 - rdf: 0.1.0 - react: 11.0.0 - react-aria: 0.2.0 - react-basic: 17.0.0 - react-basic-classic: 3.0.0 - react-basic-dnd: 10.1.0 - react-basic-dom: 6.1.0 - react-basic-emotion: 7.1.0 - react-basic-hooks: 8.2.0 - react-basic-storybook: 2.0.0 - react-dom: 8.0.0 - react-halo: 3.0.0 - react-icons: 1.1.4 - react-markdown: 0.1.0 - react-testing-library: 4.0.1 - react-virtuoso: 1.0.0 - read: 1.0.1 - recharts: 1.1.0 - record: 4.0.0 - record-extra: 5.0.1 - record-ptional-fields: 0.1.2 - record-studio: 1.0.4 - refs: 6.0.0 - remotedata: 5.0.1 - resize-observer: 1.0.0 - resource: 2.0.1 - resourcet: 1.0.0 - result: 1.0.3 - return: 0.2.0 - ring-modules: 5.0.1 - rito: 0.3.4 - rough-notation: 1.0.2 - routing: 11.0.0 - routing-duplex: 0.7.0 - run: 5.0.0 - safe-coerce: 2.0.0 - safely: 4.0.1 - school-of-music: 1.3.0 - selection-foldable: 0.2.0 - selective-functors: 1.0.1 - semirings: 7.0.0 - signal: 13.0.0 - simple-emitter: 3.0.1 - simple-i18n: 2.0.1 - simple-json: 9.0.0 - simple-ulid: 3.0.0 - sized-matrices: 1.0.0 - sized-vectors: 5.0.2 - slug: 3.0.8 - small-ffi: 4.0.1 - soundfonts: 4.1.0 - sparse-matrices: 1.3.0 - sparse-polynomials: 2.0.5 - spec: 7.6.0 - spec-mocha: 5.1.0 - spec-quickcheck: 5.0.0 - splitmix: 2.1.0 - ssrs: 1.0.0 - st: 6.2.0 - statistics: 0.3.2 - strictlypositiveint: 1.0.1 - string-parsers: 8.0.0 - strings: 6.0.1 - strings-extra: 4.0.0 - stringutils: 0.0.12 - substitute: 0.2.3 - supply: 0.2.0 - svg-parser: 3.0.0 - systemd-journald: 0.3.0 - tagged: 4.0.2 - tailrec: 6.1.0 - tecton: 0.2.1 - tecton-halogen: 0.2.0 - test-unit: 17.0.0 - thermite: 6.3.1 - thermite-dom: 0.3.1 - these: 6.0.0 - transformation-matrix: 1.0.1 - transformers: 6.0.0 - tree-rose: 4.0.2 - ts-bridge: 4.0.0 - tuples: 7.0.0 - two-or-more: 1.0.0 - type-equality: 4.0.1 - typedenv: 2.0.1 - typelevel: 6.0.0 - typelevel-lists: 2.1.0 - typelevel-peano: 1.0.1 - typelevel-prelude: 7.0.0 - typelevel-regex: 0.0.3 - typelevel-rows: 0.1.0 - uint: 7.0.0 - ulid: 3.0.1 - uncurried-transformers: 1.1.0 - undefined: 2.0.0 - undefined-is-not-a-problem: 1.1.0 - unfoldable: 6.0.0 - unicode: 6.0.0 - unique: 0.6.1 - unlift: 1.0.1 - unordered-collections: 3.1.0 - unsafe-coerce: 6.0.0 - unsafe-reference: 5.0.0 - untagged-to-tagged: 0.1.4 - untagged-union: 1.0.0 - uri: 9.0.0 - uuid: 9.0.0 - uuidv4: 1.0.0 - validation: 6.0.0 - variant: 8.0.0 - variant-encodings: 2.0.0 - vectorfield: 1.0.1 - vectors: 2.1.0 - versions: 7.0.0 - visx: 0.0.2 - web-clipboard: 5.0.0 - web-cssom: 2.0.0 - web-cssom-view: 0.1.0 - web-dom: 6.0.0 - web-dom-parser: 8.0.0 - web-dom-xpath: 3.0.0 - web-encoding: 3.0.0 - web-events: 4.0.0 - web-fetch: 4.0.1 - web-file: 4.0.0 - web-geometry: 0.1.0 - web-html: 4.1.0 - web-pointerevents: 2.0.0 - web-proletarian: 1.0.0 - web-promise: 3.2.0 - web-resize-observer: 2.1.0 - web-router: 1.0.0 - web-socket: 4.0.0 - web-storage: 5.0.0 - web-streams: 4.0.0 - web-touchevents: 4.0.0 - web-uievents: 5.0.0 - web-url: 2.0.0 - web-workers: 1.1.0 - web-xhr: 5.0.1 - webextension-polyfill: 0.1.0 - webgpu: 0.0.1 - which: 2.0.0 - xterm: 1.0.0 - yoga-fetch: 1.0.1 - yoga-json: 5.1.0 - yoga-om: 0.1.0 - yoga-postgres: 6.0.0 - yoga-tree: 1.0.0 - z3: 0.0.2 - zipperarray: 2.0.0 extra_packages: {} packages: aff: @@ -742,6 +263,39 @@ packages: dependencies: - newtype - prelude + csv-stream: + type: registry + version: 1.0.2 + integrity: sha256-2HTFDcoyTrf3TjzGL0fgNaRMSRlc4adXEOgXtEVzNY4= + dependencies: + - aff + - arrays + - bifunctors + - datetime + - effect + - either + - exceptions + - foldable-traversable + - foreign + - foreign-object + - integers + - lists + - maybe + - newtype + - node-buffer + - node-event-emitter + - node-streams + - nullable + - numbers + - precise-datetime + - prelude + - record + - st + - strings + - tailrec + - transformers + - typelevel-prelude + - unsafe-coerce datetime: type: registry version: 6.1.0 @@ -1271,8 +825,8 @@ packages: - prelude parallel: type: registry - version: 7.0.0 - integrity: sha256-gUC9i4Txnx9K9RcMLsjujbwZz6BB1bnE2MLvw4GIw5o= + version: 6.0.0 + integrity: sha256-VJbkGD0rAKX+NUEeBJbYJ78bEKaZbgow+QwQEfPB6ko= dependencies: - control - effect diff --git a/spago.yaml b/spago.yaml index 2b04da8..9295bb8 100644 --- a/spago.yaml +++ b/spago.yaml @@ -13,6 +13,7 @@ package: strict: true pedanticPackages: true dependencies: + - csv-stream: ">=1.0.2 <2.0.0" - aff: ">=7.1.0 <8.0.0" - aff-promise: ">=4.0.0 <5.0.0" - arrays: ">=7.3.0 <8.0.0" @@ -60,5 +61,3 @@ package: - spec-quickcheck workspace: extraPackages: {} - packageSet: - registry: 50.5.0 diff --git a/src/Control.Monad.Postgres.Base.purs b/src/Control.Monad.Postgres.Base.purs index 669f412..f705eb6 100644 --- a/src/Control.Monad.Postgres.Base.purs +++ b/src/Control.Monad.Postgres.Base.purs @@ -8,7 +8,7 @@ import Control.Monad.Error.Class (class MonadError, class MonadThrow, catchError import Control.Monad.Fork.Class (class MonadBracket, class MonadFork, class MonadKill, bracket, kill, never, uninterruptible) import Control.Monad.Morph (class MFunctor, class MMonad) import Control.Monad.Postgres.Cursor (class MonadCursor, CursorT) -import Control.Monad.Postgres.Session (class MonadSession, SessionT, exec, exec_, query) +import Control.Monad.Postgres.Session (class MonadSession, SessionT, exec, exec_, query, streamIn, streamOut) import Control.Monad.Reader (class MonadAsk, class MonadReader, ReaderT, ask, local, runReaderT) import Control.Monad.Rec.Class (class MonadRec) import Control.Monad.Trans.Class (class MonadTrans, lift) @@ -95,6 +95,8 @@ instance (MonadBracket e f m, MonadAff m) => MonadSession (PostgresT m) where query = session <<< query exec = session <<< exec exec_ = session <<< exec_ + streamIn = session <<< streamIn + streamOut = session <<< streamOut -- | Typeclass generalizing `PostgresT`. Allows for dependency-injecting different -- | implementations of the idea of a postgres connection. diff --git a/src/Control.Monad.Postgres.Cursor.purs b/src/Control.Monad.Postgres.Cursor.purs index c2e49ae..953d199 100644 --- a/src/Control.Monad.Postgres.Cursor.purs +++ b/src/Control.Monad.Postgres.Cursor.purs @@ -6,7 +6,7 @@ import Control.Alt (class Alt) import Control.Alternative (class Plus) import Control.Monad.Error.Class (class MonadError, class MonadThrow) import Control.Monad.Fork.Class (class MonadBracket, class MonadFork, class MonadKill, bracket, kill, never, uninterruptible) -import Control.Monad.Postgres.Session (class MonadSession, exec, exec_, query) +import Control.Monad.Postgres.Session (class MonadSession, exec, exec_, query, streamIn, streamOut) import Control.Monad.Reader (class MonadAsk, class MonadReader, ReaderT, ask, local) import Control.Monad.Rec.Class (class MonadRec) import Control.Monad.Trans.Class (class MonadTrans, lift) @@ -121,6 +121,8 @@ instance (MonadSession m) => MonadSession (CursorT t m) where query = lift <<< query exec = lift <<< exec exec_ = lift <<< exec_ + streamIn = lift <<< streamIn + streamOut = lift <<< streamOut -- | Fetch the next row from the cursor fetchOne :: forall m t. MonadCursor m t => m (Maybe t) diff --git a/src/Control.Monad.Postgres.Session.purs b/src/Control.Monad.Postgres.Session.purs index 40b0626..724f180 100644 --- a/src/Control.Monad.Postgres.Session.purs +++ b/src/Control.Monad.Postgres.Session.purs @@ -8,6 +8,8 @@ import Data.Postgres.Result (class FromRows) import Effect.Aff.Class (class MonadAff, liftAff) import Effect.Aff.Postgres.Client (Client) import Effect.Aff.Postgres.Client as Client +import Effect.Class (liftEffect) +import Node.Stream (Writable, Readable) type SessionT :: forall k. (k -> Type) -> k -> Type type SessionT = ReaderT Client @@ -20,6 +22,24 @@ class MonadAff m <= MonadSession m where exec :: forall q. AsQuery q => q -> m Int -- | Executes a query and discards the result exec_ :: forall q. AsQuery q => q -> m Unit + -- | Execute a query with a `Writable` stream to `STDIN` + -- | + -- | Use with `COPY .. FROM` like so: + -- | + -- | ```purescript + -- | w <- streamIn "COPY foo FROM STDIN WITH (FORMAT CSV, HEADER true)" + -- | liftEffect $ Stream.writeString "bar\n\"my bar column\"" UTF8 w + -- | ``` + streamIn :: String -> m (Writable ()) + -- | Execute a query with a `Readable` stream from `STDOUT` + -- | + -- | Use with `COPY .. TO` like so: + -- | + -- | ```purescript + -- | r <- streamIn "COPY foo TO STDIN WITH (FORMAT CSV, HEADER true)" + -- | liftEffect $ Stream.readString r -- "bar\n\"my bar column\"" + -- | ``` + streamOut :: String -> m (Readable ()) instance MonadAff m => MonadSession (SessionT m) where query q = do @@ -29,3 +49,9 @@ instance MonadAff m => MonadSession (SessionT m) where client <- ask liftAff $ Client.exec q client exec_ = void <<< exec + streamIn q = do + client <- ask + liftEffect $ Client.execWithStdin q client + streamOut q = do + client <- ask + liftEffect $ Client.queryWithStdout q client diff --git a/src/Effect.Aff.Postgres.Client.js b/src/Effect.Aff.Postgres.Client.js index 45bf35c..4b0e7ad 100644 --- a/src/Effect.Aff.Postgres.Client.js +++ b/src/Effect.Aff.Postgres.Client.js @@ -1,3 +1,5 @@ +import QueryStream from 'pg-copy-streams' + /** @type {(c: import('pg').Client) => () => Promise} */ export const __connect = c => () => c.connect() @@ -6,3 +8,9 @@ export const __end = c => () => c.end() /** @type {(q: import('pg').QueryConfig) => (c: import('pg').Client) => () => Promise} */ export const __query = q => c => () => c.query(q) + +/** @type {(q: string) => (c: import('pg').Client) => () => import('stream').Readable} */ +export const __execStreamStdout = q => c => () => c.query(QueryStream.to(q)) + +/** @type {(q: string) => (c: import('pg').Client) => () => import('stream').Writable} */ +export const __execStreamStdin = q => c => () => c.query(QueryStream.from(q)) diff --git a/src/Effect.Aff.Postgres.Client.purs b/src/Effect.Aff.Postgres.Client.purs index d5a283a..1f3e24d 100644 --- a/src/Effect.Aff.Postgres.Client.purs +++ b/src/Effect.Aff.Postgres.Client.purs @@ -1,4 +1,4 @@ -module Effect.Aff.Postgres.Client (connected, connect, end, exec, query, queryRaw, __connect, __end, __query, module X) where +module Effect.Aff.Postgres.Client (connected, connect, end, exec, execWithStdin, queryWithStdout, query, queryRaw, __connect, __end, __query, __execStreamStdin, __execStreamStdout, module X) where import Prelude @@ -15,6 +15,7 @@ import Effect.Aff (Aff) import Effect.Class (liftEffect) import Effect.Postgres.Client (Client, ClientConfigRaw, Config, Notification, NotificationRaw, __make, __uncfg, endE, errorE, make, noticeE, notificationE) as X import Effect.Postgres.Client (Client, Config, make) +import Node.Stream (Readable, Writable) import Prim.Row (class Union) -- | Create a client and immediately connect it to the database @@ -64,6 +65,12 @@ query q c = do rows' = rows raw liftEffect $ smash $ fromRows (wrap $ fromMaybe 0 affected) rows' +execWithStdin :: String -> Client -> Effect (Writable ()) +execWithStdin q c = __execStreamStdin q c + +queryWithStdout :: String -> Client -> Effect (Readable ()) +queryWithStdout q c = __execStreamStdout q c + -- | FFI binding to `Client#connect` foreign import __connect :: Client -> Effect (Promise Unit) @@ -72,3 +79,9 @@ foreign import __end :: Client -> Effect (Promise Unit) -- | FFI binding to `Client#query` foreign import __query :: QueryRaw -> Client -> Effect (Promise Result) + +-- | FFI binding to `import('pg-copy-streams').to` +foreign import __execStreamStdout :: String -> Client -> Effect (Readable ()) + +-- | FFI binding to `import('pg-copy-streams').from` +foreign import __execStreamStdin :: String -> Client -> Effect (Writable ())