├── .cspell.json ├── .github ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ └── bug-report.md └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── eslint.config.js ├── examples ├── advanced.js ├── monitor.js ├── query.js ├── select-insert.md └── transaction.js ├── jsdoc ├── README.md ├── fixLinks.js ├── jsdoc.js ├── layout.html ├── shortLinks.js ├── style.css └── templates │ └── custom │ ├── publish.js │ ├── static │ ├── fonts │ │ ├── OpenSans-Bold-webfont.eot │ │ ├── OpenSans-Bold-webfont.svg │ │ ├── OpenSans-Bold-webfont.woff │ │ ├── OpenSans-BoldItalic-webfont.eot │ │ ├── OpenSans-BoldItalic-webfont.svg │ │ ├── OpenSans-BoldItalic-webfont.woff │ │ ├── OpenSans-Italic-webfont.eot │ │ ├── OpenSans-Italic-webfont.svg │ │ ├── OpenSans-Italic-webfont.woff │ │ ├── OpenSans-Light-webfont.eot │ │ ├── OpenSans-Light-webfont.svg │ │ ├── OpenSans-Light-webfont.woff │ │ ├── OpenSans-LightItalic-webfont.eot │ │ ├── OpenSans-LightItalic-webfont.svg │ │ ├── OpenSans-LightItalic-webfont.woff │ │ ├── OpenSans-Regular-webfont.eot │ │ ├── OpenSans-Regular-webfont.svg │ │ └── OpenSans-Regular-webfont.woff │ ├── scripts │ │ ├── linenumber.js │ │ └── prettify │ │ │ ├── Apache-License-2.0.txt │ │ │ ├── lang-css.js │ │ │ └── prettify.js │ └── styles │ │ ├── jsdoc-default.css │ │ ├── prettify-jsdoc.css │ │ └── prettify-tomorrow.css │ └── tmpl │ ├── augments.tmpl │ ├── container.tmpl │ ├── details.tmpl │ ├── example.tmpl │ ├── examples.tmpl │ ├── exceptions.tmpl │ ├── layout.tmpl │ ├── mainpage.tmpl │ ├── members.tmpl │ ├── method.tmpl │ ├── modifies.tmpl │ ├── params.tmpl │ ├── properties.tmpl │ ├── returns.tmpl │ ├── source.tmpl │ ├── tutorial.tmpl │ └── type.tmpl ├── lib ├── assert.js ├── connect.js ├── context.js ├── database-pool.js ├── database.js ├── errors │ ├── README.md │ ├── index.js │ ├── parameterized-query-error.js │ ├── prepared-statement-error.js │ ├── query-file-error.js │ └── query-result-error.js ├── events.js ├── formatting.js ├── helpers │ ├── README.md │ ├── column-set.js │ ├── column.js │ ├── index.js │ ├── methods │ │ ├── concat.js │ │ ├── index.js │ │ ├── insert.js │ │ ├── sets.js │ │ ├── update.js │ │ └── values.js │ └── table-name.js ├── index.js ├── inner-state.js ├── main.js ├── patterns.js ├── promise-adapter.js ├── promise-parser.js ├── query-file.js ├── query-result.js ├── query.js ├── special-query.js ├── stream.js ├── task.js ├── text.js ├── tx-mode.js ├── types │ ├── index.js │ ├── parameterized-query.js │ ├── prepared-statement.js │ └── server-formatting.js └── utils │ ├── README.md │ ├── color.js │ ├── index.js │ └── public.js ├── package.json ├── test ├── adapter.spec.js ├── color.spec.js ├── db.spec.js ├── db │ ├── capture.js │ ├── header.js │ ├── init.js │ └── tools.js ├── entry.spec.js ├── event.spec.js ├── file.spec.js ├── format.spec.js ├── generator.spec.js ├── help.spec.js ├── parameterized.spec.js ├── prepared.spec.js ├── protocol.spec.js ├── sql-special │ ├── dup-files │ │ ├── 1-one.sql │ │ └── one.sql │ ├── dup-folders │ │ ├── 1-sub │ │ │ └── first.sql │ │ └── sub │ │ │ └── first.sql │ ├── folders-only │ │ ├── one │ │ │ └── first.sql │ │ └── two │ │ │ └── first.sql │ ├── one.sql │ ├── sql-config.json │ ├── sql-invalid.json │ ├── sql-simple.json │ └── two.sql ├── sql │ ├── allUsers.sql │ ├── invalid.sql │ ├── params.sql │ ├── simple.sql │ └── sub │ │ ├── one.sql │ │ ├── one.two.three.sql │ │ ├── third │ │ └── notSql.txt │ │ └── two.sql ├── stream.spec.js ├── tx-mode.spec.js ├── typescript.spec.js ├── typescript │ ├── adapter.ts │ ├── build.bat │ ├── clean.bat │ ├── config.ts │ ├── connection.ts │ ├── errors.ts │ ├── events.ts │ ├── extensions.ts │ ├── formatting.ts │ ├── help.ts │ ├── init.ts │ ├── parameterized.ts │ ├── pg.ts │ ├── pool.ts │ ├── prepared.ts │ ├── queries.ts │ ├── tasks.ts │ ├── tsconfig.json │ ├── tx-mode.ts │ └── utils.ts └── utils.spec.js └── typescript ├── README.md ├── pg-promise.d.ts ├── pg-subset.d.ts └── tslint.json /.cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2", 3 | "language": "en", 4 | "ignorePaths": [ 5 | "**/coverage/**", 6 | "**/node_modules/**", 7 | "**/jsdoc/templates/*", 8 | "**/*.{json}", 9 | ".cspell.json" 10 | ], 11 | "dictionaries": [ 12 | "typescript", 13 | "softwareTerms", 14 | "node", 15 | "en_US", 16 | "npm", 17 | "misc", 18 | "filetypes" 19 | ], 20 | "ignoreRegExpList": [ 21 | "/`[^`]*`/" 22 | ], 23 | "words": [ 24 | "PostgreSQL", 25 | "Vitaly", 26 | "Tomilov", 27 | "savepoint", 28 | "savepoints", 29 | "spex", 30 | "qrec", 31 | "Millis" 32 | ], 33 | "overrides": [ 34 | { 35 | "filename": "**/*.{ts,js}", 36 | "ignoreRegExpList": [ 37 | "/@[a-z]+/", 38 | "/#(end)?region/" 39 | ], 40 | "includeRegExpList": [ 41 | "/\\/\\*[\\s\\S]*?\\*\\/|([^\\\\:]|^)\\/\\/.*$/", 42 | "/(\\/\\/[^\\n\\r]*[\\n\\r]+)/" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /.github/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### Release History 2 | 3 | * 9.0.0 Major update of just about everything. Lots of breaking changes. Released: July 27, 2019. 4 | * 8.4.0 Replacing `isFresh` with `useCount` everywhere. Released: April 18, 2018. 5 | * 8.3.0 Adding initialization option `schema` for dynamic schema change. Released: April 17, 2018. 6 | * 8.2.0 Adding [Conditional Tasks], plus query callbacks. Released: March 10, 2018. 7 | * 8.1.0 Adding new method [utils.taskArgs]. Released: Feb 26, 2018 8 | * 8.0.0 Major changes for task and transaction interfaces. Released: Feb 24, 2018 9 | * 7.5.2 Makes Symbolic CTF within [Custom Type Formatting] global. Released: Feb 12, 2018 10 | * 7.5.1 Added alias `:list` to [CSV Filter]. Released: Feb 12, 2018 11 | * 7.5.0 Extending [CSV Filter]. Released: Feb 12, 2018 12 | * 7.4.1 Added `inTransaction` flag into [TaskContext]. Released: Jan 26, 2018 13 | * 7.4.0 This project is now sponsored by [Lisk]. Released: Jan 20, 2018 14 | * 7.3.0 Updating [as.alias] method, adding method [ColumnSet.assignColumns]. Released: Nov 05, 2017. 15 | * 7.2.0 Renaming `_rawType` into `rawType` within [Custom Type Formatting]. Released: Oct 30, 2017. 16 | * 7.1.0 Adding support for symbols within [Custom Type Formatting]. Released: Oct 29, 2017. 17 | * 7.0.0 Adding methods [multi] and [multiResult] to support multi-query results. Released: Oct 08, 2017. 18 | * 6.10.0 Initial support for [Nested Named Parameters]. Released: Sept 27, 2017 19 | * 6.9.0 Upgrading to [spex] v2.0.0, with the updated protocol. Released: Sept 21, 2017 20 | * 6.8.0 Upgrading [QueryStream] support to use version 1.1.x or later. Released: Sept 20, 2017 21 | * 6.7.0 Upgrading driver [node-postgres] to version 7.x. Released: Sept 17, 2017 22 | * 6.5.0 [Custom Type Formatting] has been refactored (breaking change). Released: Aug 18, 2017. 23 | * 6.4.0 Adding methods `batch`, `page` and `sequence` to post-`connect` state. Released: Aug 15, 2017. 24 | * 6.3.0 Major re-work on handling connectivity issues. Released: July 01, 2017 25 | * 6.2.0 Extending Task/Transaction context with properties `duration`, `level` and `txLevel`. Released: June 28, 2017. 26 | * 6.1.0 Switching over to the latest 6.4.0 `node-postgres` driver and its new connection pool. Released: June 25, 2017. 27 | * 5.9.0 Added support SQL aliases, plus method `ColumnSet.assign`. Released: June 05, 2017. 28 | * 5.8.0 Added support for warnings to class [QueryFile]. Released: May 29, 2017. 29 | * 5.7.0 Major query formatting overhaul for passing in the calling context. Released: May 15, 2017 30 | * 5.6.0 Removed support for legacy Node.js versions, now requiring 4.x and higher. Released: Feb 25, 2017 31 | * 5.5.5 Extended type `ColumnSet` with new properties `names` and `variables`. Released: Jan 30, 2017 32 | * 5.5.0 Changed the diagnostics for invalid initialization + warnings. Released: Dec 09, 2016 33 | * 5.4.3 Major changes for supporting TypeScript 2.0 (no code changes). Released: Nov 20, 2016. 34 | * 5.4.1 Now forwarding `Date` formatting into the `node-postgres` driver. Released: Nov 20, 2016. 35 | * 5.4.0 Breaking changes: improvements in the [helpers] namespace for the event handlers. Released: Nov 20, 2016. 36 | * 5.2.0 Integrating type [QueryFile] directly into the query-formatting engine. Released: July 15, 2016. 37 | * 5.0.0 Change over to the new version of [spex] 1.0, with new rejection protocol. Released: June 26, 2016. 38 | 39 | [Nested Named Parameters]:https://github.com/vitaly-t/pg-promise#nested-named-parameters 40 | [QueryStream]:https://github.com/brianc/node-pg-query-stream 41 | [spex]:https://github.com/vitaly-t/spex 42 | [each]:http://vitaly-t.github.io/pg-promise/Database.html#each 43 | [map]:http://vitaly-t.github.io/pg-promise/Database.html#map 44 | [Connection Syntax]:https://github.com/vitaly-t/pg-promise/wiki/Connection-Syntax 45 | [helpers]:http://vitaly-t.github.io/pg-promise/helpers.html 46 | [QueryFile]:http://vitaly-t.github.io/pg-promise/QueryFile.html 47 | [QueryFileError]:http://vitaly-t.github.io/pg-promise/QueryFileError.html 48 | [PreparedStatement]:http://vitaly-t.github.io/pg-promise/PreparedStatement.html 49 | [ParameterizedQuery]:http://vitaly-t.github.io/pg-promise/ParameterizedQuery.html 50 | [Database]:http://vitaly-t.github.io/pg-promise/Database.html 51 | [QueryResultError]:http://vitaly-t.github.io/pg-promise/QueryResultError.html 52 | [Native Bindings]:https://node-postgres.com/features/native 53 | [Initialization Options]:#advanced 54 | [pgp.as]:http://vitaly-t.github.io/pg-promise/formatting.html 55 | [as.value]:http://vitaly-t.github.io/pg-promise/formatting.html#.value 56 | [as.format]:http://vitaly-t.github.io/pg-promise/formatting.html#.format 57 | [as.name]:http://vitaly-t.github.io/pg-promise/formatting.html#.name 58 | [as.alias]:http://vitaly-t.github.io/pg-promise/formatting.html#.alias 59 | [batch]:http://vitaly-t.github.io/pg-promise/Task.html#batch 60 | [sequence]:http://vitaly-t.github.io/pg-promise/Task.html#sequence 61 | [API]:http://vitaly-t.github.io/pg-promise/Database.html 62 | [API Documentation]:http://vitaly-t.github.io/pg-promise/Database.html 63 | [pg-minify]:https://github.com/vitaly-t/pg-minify 64 | [pg-monitor]:https://github.com/vitaly-t/pg-monitor 65 | [pg-promise]:https://github.com/vitaly-t/pg-promise 66 | [PG]:https://github.com/brianc/node-postgres 67 | [pg]:https://github.com/brianc/node-postgres 68 | [node-postgres]:https://github.com/brianc/node-postgres 69 | [Promise]:https://github.com/then/promise 70 | [Learn by Example]:https://github.com/vitaly-t/pg-promise/wiki/Learn-by-Example 71 | [Promise Adapter]:https://github.com/vitaly-t/pg-promise/wiki/Promise-Adapter 72 | [Result]:https://node-postgres.com/api/result 73 | [Custom Type Formatting]:https://github.com/vitaly-t/pg-promise#custom-type-formatting 74 | [multi]:http://vitaly-t.github.io/pg-promise/Database.html#multi 75 | [multiResult]:http://vitaly-t.github.io/pg-promise/Database.html#multiResult 76 | [ColumnSet.assignColumns]:http://vitaly-t.github.io/pg-promise/helpers.ColumnSet.html#assignColumns 77 | [Lisk]:https://lisk.io/ 78 | [TaskContext]:http://vitaly-t.github.io/pg-promise/global.html#TaskContext 79 | [CSV Filter]:https://github.com/vitaly-t/pg-promise#csv-filter 80 | [utils.taskArgs]:http://vitaly-t.github.io/pg-promise/utils.html#.taskArgs 81 | [Conditional Tasks]:https://github.com/vitaly-t/pg-promise#conditional-tasks 82 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at vitaly.tomilov@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to pg-promise 2 | -------------------------- 3 | 4 | Please take your time reading through this short document, if you want to get proper support for this library. 5 | 6 | --- 7 | 8 | At the time of writing this document, most of the new questions or issues appear to be repeated. 9 | 10 | Please search through the following before opening any new issue or asking a question: 11 | 12 | * [List of all existing issues](https://github.com/vitaly-t/pg-promise/issues?q=is%3Aissue+is%3Aclosed) 13 | * [Questions on StackOverflow](https://stackoverflow.com/questions/tagged/pg-promise) 14 | * [Project's WiKi Pages](https://github.com/vitaly-t/pg-promise/wiki) 15 | * [Official Documentation](http://vitaly-t.github.io/pg-promise/) 16 | 17 | If your question/issue is not covered by any of the resources above, you should post it on: 18 | 19 | * [StackOverflow](https://stackoverflow.com), if it is a question, so it can be easily found by others (label it with `pg-promise`); 20 | * [Project's Issues](https://github.com/vitaly-t/pg-promise/issues), if you think that you have found a bug. 21 | 22 | The author is no longer answering generic questions within [the chat](https://gitter.im/vitaly-t/pg-promise?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge), 23 | as those are neither saved no searchable. 24 | 25 | Please do not email me any questions related to the library usage, I do not answer those, as I do not do any support via email. 26 | 27 | Before creating a PR, see the [Testing](https://github.com/vitaly-t/pg-promise/wiki/Testing) page for instructions on how to test the library locally. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: New issues are for bug reports only. For everything else, use Discussions. 4 | title: '' 5 | labels: triage 6 | assignees: vitaly-t 7 | 8 | --- 9 | 10 | ### Expected behavior 11 | 12 | 13 | ### Actual behavior 14 | 15 | 16 | ### Steps to reproduce 17 | 18 | 19 | ### Environment 20 | 21 | * Version of pg-promise: 22 | * OS type (Linux/Windows/Mac): 23 | * Version of Node.js: 24 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - '**' 9 | jobs: 10 | check-pre-check: 11 | name: PreCheck 12 | timeout-minutes: 5 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Use Node.js 20 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: 20 20 | - name: Install Dependencies 21 | run: npm install 22 | - name: TypeScript Lint 23 | run: npm run tslint 24 | - name: ESLint 25 | run: npm run lint 26 | - name: Spelling Check 27 | run: npm run spelling 28 | check-postgres: 29 | strategy: 30 | matrix: 31 | include: 32 | # Note that we do keep Node v18 here, because v20 changes errors syntax for DB again, 33 | # which requires refactoring corresponding tests again, and for no good reason at all. 34 | - name: PostgreSQL 10, Node 18 35 | POSTGRES_IMAGE: postgres:10 36 | NODE_VERSION: 18 37 | - name: PostgreSQL 16, Node 18 38 | POSTGRES_IMAGE: postgres:16 39 | NODE_VERSION: 18 40 | fail-fast: false 41 | name: ${{ matrix.name }} 42 | timeout-minutes: 15 43 | runs-on: ubuntu-latest 44 | services: 45 | postgres: 46 | image: ${{ matrix.POSTGRES_IMAGE }} 47 | env: 48 | POSTGRES_HOST: postgres 49 | POSTGRES_DB: pg_promise_test 50 | POSTGRES_USER: postgres 51 | POSTGRES_PASSWORD: postgres 52 | ports: 53 | - 5432:5432 54 | options: >- 55 | --health-cmd pg_isready 56 | --health-interval 10s 57 | --health-timeout 5s 58 | --health-retries 5 59 | steps: 60 | - uses: actions/checkout@v3 61 | - name: Use Node.js ${{ matrix.NODE_VERSION }} 62 | uses: actions/setup-node@v3 63 | with: 64 | node-version: ${{ matrix.NODE_VERSION }} 65 | - name: Cache Node.js modules 66 | uses: actions/cache@v3 67 | with: 68 | path: ~/.npm 69 | key: ${{ runner.os }}-node-${{ matrix.NODE_VERSION }}-${{ hashFiles('**/package-lock.json') }} 70 | restore-keys: | 71 | ${{ runner.os }}-node-${{ matrix.NODE_VERSION }}- 72 | - name: Install Dependencies 73 | run: npm install 74 | - name: Initialize Database 75 | run: npm run test:init 76 | - name: Test and Coverage 77 | run: npm run coverage 78 | concurrency: 79 | group: ${{ github.workflow }}-${{ github.ref }} 80 | cancel-in-progress: true 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | coverage/ 4 | API/ 5 | typescript/*.js 6 | typescript/*.map 7 | test/typescript/*.js 8 | test/typescript/*.map 9 | typings/ 10 | generated.js 11 | package-lock.json 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2018 Vitaly Tomilov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | const js = require("@eslint/js"); 2 | const globals = require("globals"); 3 | 4 | module.exports = [ 5 | js.configs.recommended, 6 | { 7 | languageOptions: { 8 | globals: { 9 | ...globals.es6, 10 | ...globals.node, 11 | ...globals.jasmine, 12 | ...globals.BigInt, 13 | }, 14 | parserOptions: { 15 | ecmaFeatures: { globalReturn: true }, 16 | }, 17 | sourceType: "commonjs", 18 | ecmaVersion: 2022, 19 | }, 20 | rules: { 21 | "no-var": "error", 22 | "prefer-const": "error", 23 | "prefer-arrow-callback": "error", 24 | "no-else-return": "error", 25 | "no-multi-spaces": "error", 26 | "no-whitespace-before-property": "error", 27 | camelcase: "error", 28 | "new-cap": "error", 29 | "no-console": "error", 30 | "comma-dangle": "error", 31 | "no-shadow": "error", 32 | "object-shorthand": ["error", "properties"], 33 | indent: [ 34 | "error", 35 | 4, 36 | { 37 | SwitchCase: 1, 38 | }, 39 | ], 40 | quotes: ["error", "single"], 41 | semi: ["error", "always"], 42 | }, 43 | }, 44 | ]; 45 | -------------------------------------------------------------------------------- /examples/advanced.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | For an advanced pg-promise demo of building enterprise-level applications see: 4 | 5 | https://github.com/vitaly-t/pg-promise-demo 6 | 7 | */ 8 | -------------------------------------------------------------------------------- /examples/monitor.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | //////////////////////////////////////////////////// 4 | // This is a complete test application, which shows 5 | // how to use the following options: 6 | // 7 | // a) override the default promise library; 8 | // b) use pg-monitor to output all the query events; 9 | // c) change the default theme for pg-monitor; 10 | // d) add log handler to pg-monitor, to log events into a file or elsewhere. 11 | // 12 | // Packages used: pg-promise, pg-monitor, bluebird. 13 | //////////////////////////////////////////////////// 14 | 15 | const promise = require('bluebird'); // or any other Promise/A+ compatible library; 16 | 17 | const initOptions = { 18 | promiseLib: promise // overriding the default (ES6 Promise); 19 | }; 20 | 21 | const pgp = require('pg-promise')(initOptions); 22 | // See all options: http://vitaly-t.github.io/pg-promise/module-pg-promise.html 23 | 24 | const monitor = require('pg-monitor'); 25 | 26 | monitor.attach(initOptions); // attach to all query events; 27 | // See API: https://github.com/vitaly-t/pg-monitor#attachoptions-events-override 28 | 29 | monitor.setTheme('matrix'); // change the default theme; 30 | // Other themes: https://github.com/vitaly-t/pg-monitor/wiki/Color-Themes 31 | 32 | monitor.setLog((msg, info) => { 33 | // save the screen messages into your own log file; 34 | }); 35 | // See API: https://github.com/vitaly-t/pg-monitor#log 36 | 37 | // Database connection details; 38 | const cn = { 39 | host: 'localhost', // 'localhost' is the default; 40 | port: 5432, // 5432 is the default; 41 | database: 'myDatabase', 42 | user: 'myUser', 43 | password: 'myPassword', 44 | 45 | // to auto-exit on idle, without having to shut down the pool; 46 | // see https://github.com/vitaly-t/pg-promise#library-de-initialization 47 | allowExitOnIdle: true 48 | }; 49 | 50 | const db = pgp(cn); // database instance; 51 | 52 | db.any('select * from users where active = $1', [true]) 53 | .then(data => { 54 | console.log('DATA:', data); 55 | }) 56 | .catch(error => { 57 | console.log('ERROR:', error); 58 | }); 59 | -------------------------------------------------------------------------------- /examples/query.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | /////////////////////////////////////////////// 4 | // This is to show a complete test application; 5 | /////////////////////////////////////////////// 6 | 7 | const promise = require('bluebird'); // or any other Promise/A+ compatible library; 8 | 9 | const initOptions = { 10 | promiseLib: promise // overriding the default (ES6 Promise); 11 | }; 12 | 13 | const pgp = require('pg-promise')(initOptions); 14 | // See also: http://vitaly-t.github.io/pg-promise/module-pg-promise.html 15 | 16 | // Database connection details; 17 | const cn = { 18 | host: 'localhost', // 'localhost' is the default; 19 | port: 5432, // 5432 is the default; 20 | database: 'myDatabase', 21 | user: 'myUser', 22 | password: 'myPassword', 23 | 24 | // to auto-exit on idle, without having to shut down the pool; 25 | // see https://github.com/vitaly-t/pg-promise#library-de-initialization 26 | allowExitOnIdle: true 27 | }; 28 | // You can check for all default values in: 29 | // https://github.com/brianc/node-postgres/blob/master/packages/pg/lib/defaults.js 30 | 31 | const db = pgp(cn); // database instance; 32 | 33 | db.any('select * from users where active = $1', [true]) 34 | .then(data => { 35 | console.log('DATA:', data); // print data; 36 | }) 37 | .catch(error => { 38 | console.log('ERROR:', error); // print the error; 39 | }); 40 | -------------------------------------------------------------------------------- /examples/select-insert.md: -------------------------------------------------------------------------------- 1 | # SELECT ⇒ INSERT 2 | 3 | The following scenario is very common in adding new records: 4 | 5 | * try finding a specific record, and if found - return its id; 6 | * if no record found, add a new one and return the new id. 7 | 8 | ```sql 9 | -- a simple table example 10 | CREATE TABLE Users( 11 | id SERIAL PRIMARY KEY, 12 | name TEXT UNIQUE -- unique user name 13 | ); 14 | ``` 15 | 16 | Let's implement a function, according to that logic, to search by user's `name`, and return 17 | a new or existing `id` of the record. 18 | 19 | ```js 20 | function getInsertUserId(name) { 21 | return db.task('getInsertUserId', t => { 22 | return t.oneOrNone('SELECT id FROM Users WHERE name = $1', name, u => u && u.id) 23 | .then(userId => { 24 | return userId || t.one('INSERT INTO Users(name) VALUES($1) RETURNING id', name, u => u.id); 25 | }); 26 | }); 27 | } 28 | ``` 29 | 30 | Example of calling the function: 31 | 32 | ```js 33 | getInsertUserId('name') 34 | .then(userId => { 35 | // use the id; 36 | }) 37 | .catch(error => { 38 | // something went wrong; 39 | }); 40 | ``` 41 | 42 | The same function `getInsertUserId`, using ES7 `async` syntax: 43 | 44 | ```js 45 | async function getInsertUserId(name) { 46 | return db.task('getInsertUserId', async t => { 47 | const userId = await t.oneOrNone('SELECT id FROM Users WHERE name = $1', name, u => u && u.id); 48 | return userId || await t.one('INSERT INTO Users(name) VALUES($1) RETURNING id', name, u => u.id); 49 | }); 50 | } 51 | ``` 52 | 53 | ## Single-query alternative 54 | 55 | It is possible to wrap the whole operation into a single query, which would offer a better 56 | performance, executing always just one query, and more importantly - proper data integrity, 57 | by making sure that a parallel request would not try to execute a second `INSERT`. 58 | 59 | Implementing such a query however isn't trivial, and it can vary based on whether it is for 60 | PostgreSQL 9.5+ or an older version of the server. 61 | 62 | The following posts will help you get started writing your own single-query solution for this: 63 | 64 | * [Id from a conditional INSERT](http://stackoverflow.com/questions/36083669/get-id-from-a-conditional-insert) 65 | * [SELECT ⇒ INSERT in a function](http://stackoverflow.com/questions/15939902/is-select-or-insert-in-a-function-prone-to-race-conditions) 66 | -------------------------------------------------------------------------------- /examples/transaction.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | /////////////////////////////////////////////// 4 | // This is to show a complete test application; 5 | /////////////////////////////////////////////// 6 | 7 | const promise = require('bluebird'); // or any other Promise/A+ compatible library; 8 | 9 | const initOptions = { 10 | promiseLib: promise // overriding the default (ES6 Promise); 11 | }; 12 | 13 | const pgp = require('pg-promise')(initOptions); 14 | // See also: http://vitaly-t.github.io/pg-promise/module-pg-promise.html 15 | 16 | // Database connection details; 17 | const cn = { 18 | host: 'localhost', // 'localhost' is the default; 19 | port: 5432, // 5432 is the default; 20 | database: 'myDatabase', 21 | user: 'myUser', 22 | password: 'myPassword', 23 | 24 | // to auto-exit on idle, without having to shut down the pool; 25 | // see https://github.com/vitaly-t/pg-promise#library-de-initialization 26 | allowExitOnIdle: true 27 | }; 28 | // You can check for all default values in: 29 | // https://github.com/brianc/node-postgres/blob/master/packages/pg/lib/defaults.js 30 | 31 | const db = pgp(cn); // database instance; 32 | 33 | db.tx(async t => { 34 | const user = await t.one('INSERT INTO users(name) VALUES($1) RETURNING id', 'John'); 35 | const event = await t.one('INSERT INTO events(code) VALUES($1) RETURNING id', 123); 36 | return {user, event}; 37 | }) 38 | .then(({user, event}) => { 39 | // print new user id + new event id; 40 | console.log('DATA:', user.id, event.id); 41 | }) 42 | .catch(error => { 43 | console.log('ERROR:', error); // print the error; 44 | }); 45 | -------------------------------------------------------------------------------- /jsdoc/README.md: -------------------------------------------------------------------------------- 1 | ## Installing 2 | 3 | ``` 4 | $ npm install pg-promise 5 | ``` 6 | 7 | ## Initialization 8 | 9 | Loading and initializing the library with [Initialization Options]: 10 | 11 | ```js 12 | const initOptions = {/* initialization options */}; 13 | const pgp = require('pg-promise')(initOptions); 14 | ``` 15 | 16 | − or without [Initialization Options]: 17 | 18 | ```js 19 | const pgp = require('pg-promise')(); 20 | ``` 21 | 22 | ## Database 23 | 24 | Create your [Database] object from the connection as `pgp(connection, [dc])`: 25 | 26 | ```js 27 | const db = pgp(connection); 28 | ``` 29 | 30 | * The `connection` parameter is either a [Configuration Object] or a [Connection String]. 31 | * `dc` = Database Context - optional parameter (see [Database] constructor). 32 | 33 | Object `db` represents the [Database] protocol with lazy connection, i.e. only the actual query methods acquire 34 | and release the connection automatically. Therefore, you should create only one global/shared `db` object per connection details. 35 | 36 | It is best to initialize the library and create [Database] in its own module, see [Where should I initialize pg-promise]. 37 | 38 | ## Usage 39 | 40 | [Learn by Example] is the best quick-start tutorial. For everything else see the [WiKi] pages. 41 | 42 | ## References 43 | 44 | ### External Resources 45 | 46 | * [The library's Main Page](https://github.com/vitaly-t/pg-promise) 47 | * [TypeScript 4.x declarations](https://github.com/vitaly-t/pg-promise/tree/master/typescript) 48 | * [The library's WiKi Pages](https://github.com/vitaly-t/pg-promise/wiki) 49 | * [Popular questions on StackOverflow](https://stackoverflow.com/questions/tagged/pg-promise?sort=votes&pageSize=50) 50 | 51 | ### Main Types 52 | 53 | * [Database] - database-level methods and properties 54 | * [Task](http://vitaly-t.github.io/pg-promise/Task.html) - extends [Database] with task-level methods and properties 55 | 56 | [Database]:http://vitaly-t.github.io/pg-promise/Database.html 57 | 58 | ### Special Types 59 | 60 | * [QueryFile] - type for working with external SQL files 61 | * [PreparedStatement] - type for working with [Prepared Statements] 62 | * [ParameterizedQuery] - type for working with [Parameterized Queries] 63 | * [TransactionMode] - transaction configuration type 64 | * [PromiseAdapter] - adapter for using non-standard promise libraries 65 | 66 | [QueryFile]:http://vitaly-t.github.io/pg-promise/QueryFile.html 67 | [PreparedStatement]:http://vitaly-t.github.io/pg-promise/PreparedStatement.html 68 | [ParameterizedQuery]:http://vitaly-t.github.io/pg-promise/ParameterizedQuery.html 69 | [TransactionMode]:http://vitaly-t.github.io/pg-promise/txMode.TransactionMode.html 70 | [PromiseAdapter]:http://vitaly-t.github.io/pg-promise/PromiseAdapter.html 71 | [Prepared Statements]:https://github.com/vitaly-t/pg-promise/wiki/Learn-by-Example#prepared-statements 72 | [Parameterized Queries]:https://github.com/vitaly-t/pg-promise/wiki/Learn-by-Example#parameterized-queries 73 | 74 | ### Namespaces 75 | 76 | * [formatting] - the library's query formatting engine 77 | * [ctf] - ES6 symbols used by [Custom Type Formatting] 78 | * [helpers] - for automatic query generation 79 | * [utils] - simplifies the use of external SQL files 80 | * [errors] - custom error types supported by the library 81 | * [txMode] - types for configuring a transaction 82 | 83 | [formatting]:http://vitaly-t.github.io/pg-promise/formatting.html 84 | [ctf]:http://vitaly-t.github.io/pg-promise/formatting.ctf.html 85 | [helpers]:http://vitaly-t.github.io/pg-promise/helpers.html 86 | [utils]:http://vitaly-t.github.io/pg-promise/utils.html 87 | [errors]:http://vitaly-t.github.io/pg-promise/errors.html 88 | [txMode]:http://vitaly-t.github.io/pg-promise/txMode.html 89 | 90 | ### Events 91 | 92 | * [connect] - connecting to the database 93 | * [disconnect] - disconnecting from the database 94 | * [query] - executing a query 95 | * [task] - task start/end events 96 | * [transact] - transaction start/end events 97 | * [receive] - receiving data from a query 98 | * [error] - global error handler 99 | * [extend] - interface extension event 100 | 101 | [connect]:http://vitaly-t.github.io/pg-promise/global.html#event:connect 102 | [disconnect]:http://vitaly-t.github.io/pg-promise/global.html#event:disconnect 103 | [query]:http://vitaly-t.github.io/pg-promise/global.html#event:query 104 | [task]:http://vitaly-t.github.io/pg-promise/global.html#event:task 105 | [transact]:http://vitaly-t.github.io/pg-promise/global.html#event:transact 106 | [receive]:http://vitaly-t.github.io/pg-promise/global.html#event:receive 107 | [error]:http://vitaly-t.github.io/pg-promise/global.html#event:error 108 | [extend]:http://vitaly-t.github.io/pg-promise/global.html#event:extend 109 | 110 | [Configuration Object]:https://github.com/vitaly-t/pg-promise/wiki/Connection-Syntax#configuration-object 111 | [Connection String]:https://github.com/vitaly-t/pg-promise/wiki/Connection-Syntax#connection-string 112 | [Initialization Options]:http://vitaly-t.github.io/pg-promise/module-pg-promise.html 113 | [Where should I initialize pg-promise]:https://stackoverflow.com/questions/34382796/where-should-i-initialize-pg-promise 114 | [Learn by Example]:https://github.com/vitaly-t/pg-promise/wiki/Learn-by-Example 115 | [WiKi]:https://github.com/vitaly-t/pg-promise/wiki 116 | [Custom Type Formatting]:https://github.com/vitaly-t/pg-promise#custom-type-formatting 117 | -------------------------------------------------------------------------------- /jsdoc/fixLinks.js: -------------------------------------------------------------------------------- 1 | // Automatic links: 2 | const links = { 3 | 'Promises/A+': 'https://promisesaplus.com', 4 | 'spex': 'https://github.com/vitaly-t/spex', 5 | 'spex.batch': 'http://vitaly-t.github.io/spex/global.html#batch', 6 | 'spex.page': 'http://vitaly-t.github.io/spex/global.html#page', 7 | 'spex.sequence': 'http://vitaly-t.github.io/spex/global.html#sequence', 8 | 'Result': 'https://node-postgres.com/apis/result', 9 | 'pg-query-stream': 'https://github.com/brianc/node-postgres/tree/master/packages/pg-query-stream', 10 | 'QueryStream': 'https://github.com/brianc/node-postgres/blob/master/packages/pg-query-stream/src/index.ts', 11 | 'pg.Client': 'https://node-postgres.com/apis/client', 12 | 'BEGIN': 'http://www.postgresql.org/docs/9.6/static/sql-begin.html', 13 | 'Transaction Isolation': 'http://www.postgresql.org/docs/9.6/static/transaction-iso.html', 14 | 'pg-minify': 'https://github.com/vitaly-t/pg-minify', 15 | 'SQLParsingError': 'https://github.com/vitaly-t/pg-minify/blob/master/lib/error.js', 16 | 'PG': 'https://github.com/brianc/node-postgres', 17 | 'pg': 'https://github.com/brianc/node-postgres', 18 | 'pg-pool': 'https://github.com/brianc/node-postgres/tree/master/packages/pg-pool', 19 | 'Native Bindings': 'https://node-postgres.com/features/native', 20 | 'pg-native': 'https://github.com/brianc/node-postgres/tree/master/packages/pg-native', 21 | 'Named Parameters': 'https://github.com/vitaly-t/pg-promise#named-parameters', 22 | 'Index Variables': 'https://github.com/vitaly-t/pg-promise#index-variables', 23 | 'Nested Named Parameters': 'https://github.com/vitaly-t/pg-promise#nested-named-parameters', 24 | 'tags': 'https://github.com/vitaly-t/pg-promise/wiki/tags', 25 | 'Chaining Queries': 'https://github.com/vitaly-t/pg-promise/wiki/Chaining-Queries', 26 | 'Performance Boost': 'https://github.com/vitaly-t/pg-promise/wiki/Performance-Boost', 27 | 'Prepared Statement': 'http://www.postgresql.org/docs/9.6/static/sql-prepare.html', 28 | 'Prepared Statements': 'http://www.postgresql.org/docs/9.6/static/sql-prepare.html', 29 | 'Custom Type Formatting': 'https://github.com/vitaly-t/pg-promise#custom-type-formatting', 30 | 'Symbolic CTF': 'https://github.com/vitaly-t/pg-promise#symbolic-ctf', 31 | 'SQL Names': 'https://github.com/vitaly-t/pg-promise#sql-names', 32 | 'pg-promise-demo': 'https://github.com/vitaly-t/pg-promise-demo', 33 | 'Robust Listeners': 'https://github.com/vitaly-t/pg-promise/wiki/Robust-Listeners', 34 | 'Promise Adapter': 'https://github.com/vitaly-t/pg-promise/wiki/Promise-Adapter', 35 | 'Bluebird': 'https://github.com/petkaantonov/bluebird', 36 | 'Long Stack Traces': 'http://bluebirdjs.com/docs/api/promise.config.html', 37 | 'Symbol': 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol', 38 | 'Library de-initialization': 'https://github.com/vitaly-t/pg-promise#library-de-initialization', 39 | 'Nested Transactions': 'https://github.com/vitaly-t/pg-promise#nested-transactions', 40 | 'changing the database or the role': 'https://stackoverflow.com/questions/2875610/permanently-set-postgresql-schema-path', 41 | 'Promise.allSettled': 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled' 42 | }; 43 | 44 | function fixLinks(source) { 45 | return source.replace(/\$\[[a-z0-9\s/+-.]+\]/gi, name => { 46 | const sln = name.replace(/\$\[|\]/g, ''); // stripped link name; 47 | if (sln in links) { 48 | return '<a href="' + links[sln] + '">' + sln + '</a>'; 49 | } 50 | return name; 51 | }); 52 | } 53 | 54 | module.exports = {fixLinks}; 55 | -------------------------------------------------------------------------------- /jsdoc/jsdoc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | source: { 3 | include: [ 4 | 'lib' 5 | ] 6 | }, 7 | opts: { 8 | recurse: true, 9 | destination: './API' 10 | }, 11 | templates: { 12 | default: { 13 | layoutFile: './jsdoc/layout.html', 14 | staticFiles: { 15 | include: [ 16 | './jsdoc/style.css' 17 | ] 18 | } 19 | } 20 | }, 21 | plugins: [ 22 | 'plugins/markdown', 23 | './jsdoc/shortLinks' 24 | ] 25 | }; 26 | -------------------------------------------------------------------------------- /jsdoc/layout.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>pg-promise API</title> 6 | <script src="scripts/prettify/prettify.js"></script> 7 | <script src="scripts/prettify/lang-css.js"></script> 8 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> 9 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> 10 | <link type="text/css" rel="stylesheet" href="style.css"> 11 | </head> 12 | 13 | <body> 14 | 15 | <nav> 16 | <?js= this.nav ?> 17 | <div class="home-link"> 18 | <a href="https://github.com/vitaly-t/pg-promise"><< GitHub</a> 19 | </div> 20 | </nav> 21 | 22 | <div id="main"> 23 | <?js= content ?> 24 | </div> 25 | 26 | <br class="clear"> 27 | 28 | <footer> 29 | </footer> 30 | 31 | <script> prettyPrint(); </script> 32 | <script src="scripts/linenumber.js"></script> 33 | </body> 34 | </html> 35 | -------------------------------------------------------------------------------- /jsdoc/shortLinks.js: -------------------------------------------------------------------------------- 1 | const {fixLinks} = require('./fixLinks'); 2 | 3 | exports.handlers = { 4 | beforeParse(e) { 5 | e.source = fixLinks(e.source); 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /jsdoc/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | html { 6 | font-size: 16px; 7 | } 8 | 9 | html, body, section { 10 | background-color: #F0F0F0; 11 | } 12 | 13 | pre.prettyprint:not(pre.source) { 14 | width: auto; 15 | border: 1px solid lightgrey; 16 | padding: 10px; 17 | } 18 | 19 | p>code, .description code { 20 | background-color: lightgray; 21 | } 22 | 23 | h4.name { 24 | border-radius: 7px; 25 | padding: 4px 10px; 26 | background-color: rgb(16, 71, 70); 27 | color: rgb(136, 191, 190); 28 | font-size: 24px; 29 | } 30 | 31 | div#main { 32 | margin-left: 280px; 33 | float: none; 34 | } 35 | 36 | code { 37 | border-radius: 3px; 38 | padding: 2px 4px; 39 | } 40 | 41 | nav { 42 | background-color: #222; 43 | padding: 10px 20px 20px 20px; 44 | width: auto; 45 | top: 0; 46 | bottom: 0; 47 | position: fixed; 48 | font-size: 18px; 49 | margin-top: 0px; 50 | line-height: 24px; 51 | float: none; 52 | overflow-y: auto; 53 | } 54 | 55 | nav h2 a, 56 | nav h2 a:visited, 57 | nav div.home-link a, 58 | nav div.home-link a:visited { 59 | width: 100%; 60 | color: Gold; 61 | } 62 | 63 | nav ul li a, 64 | nav ul li a:visited { 65 | color: #EEE; 66 | font-size: 17px; 67 | } 68 | 69 | nav ul li a:hover { 70 | color: #CC0; 71 | } 72 | 73 | nav h3 { 74 | color: LightGreen; 75 | font-size: 18px; 76 | } 77 | 78 | nav h2 { 79 | font-size: 20px; 80 | } 81 | 82 | .nav li a { 83 | color: white !important; 84 | } 85 | 86 | .home-link { 87 | padding-top: 20px; 88 | } 89 | 90 | .home-link a { 91 | font-size: 16px; 92 | font-weight: bold; 93 | } 94 | 95 | table.params th:first-child, 96 | table.props th:first-child { 97 | border-top-left-radius: 5px; 98 | } 99 | 100 | table.params th:last-child, 101 | table.props th:last-child { 102 | border-top-right-radius: 5px; 103 | } 104 | 105 | table.params th, 106 | table.props th { 107 | border: none; 108 | border-right: 1px solid #F5F5F5; 109 | } 110 | 111 | table.params th, 112 | table.props th { 113 | font-family: 'Verdana'; 114 | font-weight: normal; 115 | } 116 | 117 | table.params th { 118 | background-color: SteelBlue; 119 | color: White; 120 | } 121 | 122 | table.props th { 123 | background-color: DarkCyan; 124 | color: White; 125 | } 126 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/fonts/OpenSans-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/jsdoc/templates/custom/static/fonts/OpenSans-Bold-webfont.eot -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/fonts/OpenSans-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/jsdoc/templates/custom/static/fonts/OpenSans-Bold-webfont.woff -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/fonts/OpenSans-BoldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/jsdoc/templates/custom/static/fonts/OpenSans-BoldItalic-webfont.eot -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/fonts/OpenSans-BoldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/jsdoc/templates/custom/static/fonts/OpenSans-BoldItalic-webfont.woff -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/fonts/OpenSans-Italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/jsdoc/templates/custom/static/fonts/OpenSans-Italic-webfont.eot -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/fonts/OpenSans-Italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/jsdoc/templates/custom/static/fonts/OpenSans-Italic-webfont.woff -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/fonts/OpenSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/jsdoc/templates/custom/static/fonts/OpenSans-Light-webfont.eot -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/fonts/OpenSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/jsdoc/templates/custom/static/fonts/OpenSans-Light-webfont.woff -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/fonts/OpenSans-LightItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/jsdoc/templates/custom/static/fonts/OpenSans-LightItalic-webfont.eot -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/fonts/OpenSans-LightItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/jsdoc/templates/custom/static/fonts/OpenSans-LightItalic-webfont.woff -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/fonts/OpenSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/jsdoc/templates/custom/static/fonts/OpenSans-Regular-webfont.eot -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/fonts/OpenSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/jsdoc/templates/custom/static/fonts/OpenSans-Regular-webfont.woff -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | (function() { 3 | var source = document.getElementsByClassName('prettyprint source linenums'); 4 | var i = 0; 5 | var lineNumber = 0; 6 | var lineId; 7 | var lines; 8 | var totalLines; 9 | var anchorHash; 10 | 11 | if (source && source[0]) { 12 | anchorHash = document.location.hash.substring(1); 13 | lines = source[0].getElementsByTagName('li'); 14 | totalLines = lines.length; 15 | 16 | for (; i < totalLines; i++) { 17 | lineNumber++; 18 | lineId = 'line' + lineNumber; 19 | lines[i].id = lineId; 20 | if (lineId === anchorHash) { 21 | lines[i].className += ' selected'; 22 | } 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: #006400; 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/static/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/augments.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var self = this; 4 | ?> 5 | 6 | <?js if (data.augments && data.augments.length) { ?> 7 | <ul><?js data.augments.forEach(function(a) { ?> 8 | <li><?js= self.linkto(a, a) ?></li> 9 | <?js }) ?></ul> 10 | <?js } ?> 11 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/container.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var self = this; 3 | var isGlobalPage; 4 | 5 | docs.forEach(function(doc, i) { 6 | ?> 7 | 8 | <?js 9 | // we only need to check this once 10 | if (typeof isGlobalPage === 'undefined') { 11 | isGlobalPage = (doc.kind === 'globalobj'); 12 | } 13 | ?> 14 | <?js if (doc.kind === 'mainpage' || (doc.kind === 'package')) { ?> 15 | <?js= self.partial('mainpage.tmpl', doc) ?> 16 | <?js } else if (doc.kind === 'source') { ?> 17 | <?js= self.partial('source.tmpl', doc) ?> 18 | <?js } else { ?> 19 | 20 | <section> 21 | 22 | <header> 23 | <?js if (!doc.longname || doc.kind !== 'module') { ?> 24 | <h2><?js if (doc.attribs) { ?><span class="attribs"><?js= doc.attribs ?></span><?js } 25 | if (doc.ancestors && doc.ancestors.length) { ?> 26 | <span class="ancestors"><?js= doc.ancestors.join('') ?></span><?js 27 | } 28 | ?><?js= doc.name ?><?js 29 | if (doc.variation) { ?> 30 | <sup class="variation"><?js= doc.variation ?></sup><?js } 31 | if (doc.signature && !doc.hideconstructor) { ?><?js= doc.signature ?><?js } ?></h2> 32 | <?js if (doc.classdesc) { ?> 33 | <div class="class-description"><?js= doc.classdesc ?></div> 34 | <?js } ?> 35 | <?js } else if (doc.kind === 'module' && doc.modules) { ?> 36 | <?js doc.modules.forEach(function(module) { ?> 37 | <?js if (module.classdesc) { ?> 38 | <div class="class-description"><?js= module.classdesc ?></div> 39 | <?js } ?> 40 | <?js }) ?> 41 | <?js } ?> 42 | </header> 43 | 44 | <article> 45 | <div class="container-overview"> 46 | <?js if (doc.kind === 'module' && doc.modules) { ?> 47 | <?js if (doc.description) { ?> 48 | <div class="description"><?js= doc.description ?></div> 49 | <?js } ?> 50 | 51 | <?js doc.modules.forEach(function(module) { ?> 52 | <?js= self.partial('method.tmpl', module) ?> 53 | <?js }) ?> 54 | <?js } else if (doc.kind === 'class' || (doc.kind === 'namespace' && doc.signature)) { ?> 55 | <?js= self.partial('method.tmpl', doc) ?> 56 | <?js } else { ?> 57 | <?js if (doc.description) { ?> 58 | <div class="description"><?js= doc.description ?></div> 59 | <?js } ?> 60 | 61 | <?js= self.partial('details.tmpl', doc) ?> 62 | 63 | <?js if (doc.examples && doc.examples.length) { ?> 64 | <h3>Example<?js= doc.examples.length > 1? 's':'' ?></h3> 65 | <?js= self.partial('examples.tmpl', doc.examples) ?> 66 | <?js } ?> 67 | <?js } ?> 68 | </div> 69 | 70 | <?js if (doc.augments && doc.augments.length) { ?> 71 | <h3 class="subsection-title">Extends</h3> 72 | 73 | <?js= self.partial('augments.tmpl', doc) ?> 74 | <?js } ?> 75 | 76 | <?js if (doc.requires && doc.requires.length) { ?> 77 | <h3 class="subsection-title">Requires</h3> 78 | 79 | <ul><?js doc.requires.forEach(function(r) { ?> 80 | <li><?js= self.linkto(r, r) ?></li> 81 | <?js }); ?></ul> 82 | <?js } ?> 83 | 84 | <?js 85 | var classes = self.find({kind: 'class', memberof: doc.longname}); 86 | if (!isGlobalPage && classes && classes.length) { 87 | ?> 88 | <h3 class="subsection-title">Classes</h3> 89 | 90 | <dl><?js classes.forEach(function(c) { ?> 91 | <dt><?js= self.linkto(c.longname, c.name) ?></dt> 92 | <dd><?js if (c.summary) { ?><?js= c.summary ?><?js } ?></dd> 93 | <?js }); ?></dl> 94 | <?js } ?> 95 | 96 | <?js 97 | var interfaces = self.find({kind: 'interface', memberof: doc.longname}); 98 | if (!isGlobalPage && interfaces && interfaces.length) { 99 | ?> 100 | <h3 class="subsection-title">Interfaces</h3> 101 | 102 | <dl><?js interfaces.forEach(function(i) { ?> 103 | <dt><?js= self.linkto(i.longname, i.name) ?></dt> 104 | <dd><?js if (i.summary) { ?><?js= i.summary ?><?js } ?></dd> 105 | <?js }); ?></dl> 106 | <?js } ?> 107 | 108 | <?js 109 | var mixins = self.find({kind: 'mixin', memberof: doc.longname}); 110 | if (!isGlobalPage && mixins && mixins.length) { 111 | ?> 112 | <h3 class="subsection-title">Mixins</h3> 113 | 114 | <dl><?js mixins.forEach(function(m) { ?> 115 | <dt><?js= self.linkto(m.longname, m.name) ?></dt> 116 | <dd><?js if (m.summary) { ?><?js= m.summary ?><?js } ?></dd> 117 | <?js }); ?></dl> 118 | <?js } ?> 119 | 120 | <?js 121 | var namespaces = self.find({kind: 'namespace', memberof: doc.longname}); 122 | if (!isGlobalPage && namespaces && namespaces.length) { 123 | ?> 124 | <h3 class="subsection-title">Namespaces</h3> 125 | 126 | <dl><?js namespaces.forEach(function(n) { ?> 127 | <dt><?js= self.linkto(n.longname, n.name) ?></dt> 128 | <dd><?js if (n.summary) { ?><?js= n.summary ?><?js } ?></dd> 129 | <?js }); ?></dl> 130 | <?js } ?> 131 | 132 | <?js 133 | var members = self.find({kind: 'member', memberof: isGlobalPage ? {isUndefined: true} : doc.longname}); 134 | 135 | // symbols that are assigned to module.exports are not globals, even though they're not a memberof anything 136 | if (isGlobalPage && members && members.length && members.forEach) { 137 | members = members.filter(function(m) { 138 | return m.longname && m.longname.indexOf('module:') !== 0; 139 | }); 140 | } 141 | if (members && members.length && members.forEach) { 142 | ?> 143 | <h3 class="subsection-title">Members</h3> 144 | 145 | <?js members.forEach(function(p) { ?> 146 | <?js= self.partial('members.tmpl', p) ?> 147 | <?js }); ?> 148 | <?js } ?> 149 | 150 | <?js 151 | var methods = self.find({kind: 'function', memberof: isGlobalPage ? {isUndefined: true} : doc.longname}); 152 | if (methods && methods.length && methods.forEach) { 153 | ?> 154 | <h3 class="subsection-title">Methods</h3> 155 | 156 | <?js methods.forEach(function(m) { ?> 157 | <?js= self.partial('method.tmpl', m) ?> 158 | <?js }); ?> 159 | <?js } ?> 160 | 161 | <?js 162 | var typedefs = self.find({kind: 'typedef', memberof: isGlobalPage ? {isUndefined: true} : doc.longname}); 163 | if (typedefs && typedefs.length && typedefs.forEach) { 164 | ?> 165 | <h3 class="subsection-title">Type Definitions</h3> 166 | 167 | <?js typedefs.forEach(function(e) { 168 | if (e.signature) { 169 | ?> 170 | <?js= self.partial('method.tmpl', e) ?> 171 | <?js 172 | } 173 | else { 174 | ?> 175 | <?js= self.partial('members.tmpl', e) ?> 176 | <?js 177 | } 178 | }); ?> 179 | <?js } ?> 180 | 181 | <?js 182 | var events = self.find({kind: 'event', memberof: isGlobalPage ? {isUndefined: true} : doc.longname}); 183 | if (events && events.length && events.forEach) { 184 | ?> 185 | <h3 class="subsection-title">Events</h3> 186 | 187 | <?js events.forEach(function(e) { ?> 188 | <?js= self.partial('method.tmpl', e) ?> 189 | <?js }); ?> 190 | <?js } ?> 191 | </article> 192 | 193 | </section> 194 | <?js } ?> 195 | 196 | <?js }); ?> 197 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/details.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var self = this; 4 | var defaultObjectClass = ''; 5 | 6 | // Check if the default value is an object or array; if so, apply code highlighting 7 | if (data.defaultvalue && (data.defaultvaluetype === 'object' || data.defaultvaluetype === 'array')) { 8 | data.defaultvalue = "<pre class=\"prettyprint\"><code>" + data.defaultvalue + "</code></pre>"; 9 | defaultObjectClass = ' class="object-value"'; 10 | } 11 | ?> 12 | <?js 13 | var properties = data.properties; 14 | if (properties && properties.length && properties.forEach && !data.hideconstructor) { 15 | ?> 16 | 17 | <h5 class="subsection-title">Properties:</h5> 18 | 19 | <?js= this.partial('properties.tmpl', data) ?> 20 | 21 | <?js } ?> 22 | 23 | <dl class="details"> 24 | 25 | <?js if (data.version) {?> 26 | <dt class="tag-version">Version:</dt> 27 | <dd class="tag-version"><ul class="dummy"><li><?js= version ?></li></ul></dd> 28 | <?js } ?> 29 | 30 | <?js if (data.since) {?> 31 | <dt class="tag-since">Since:</dt> 32 | <dd class="tag-since"><ul class="dummy"><li><?js= since ?></li></ul></dd> 33 | <?js } ?> 34 | 35 | <?js if (data.inherited && data.inherits && !data.overrides) { ?> 36 | <dt class="inherited-from">Inherited From:</dt> 37 | <dd class="inherited-from"><ul class="dummy"><li> 38 | <?js= this.linkto(data.inherits, this.htmlsafe(data.inherits)) ?> 39 | </li></ul></dd> 40 | <?js } ?> 41 | 42 | <?js if (data.overrides) { ?> 43 | <dt class="tag-overrides">Overrides:</dt> 44 | <dd class="tag-overrides"><ul class="dummy"><li> 45 | <?js= this.linkto(data.overrides, this.htmlsafe(data.overrides)) ?> 46 | </li></ul></dd> 47 | <?js } ?> 48 | 49 | <?js if (data.implementations && data.implementations.length) { ?> 50 | <dt class="implementations">Implementations:</dt> 51 | <dd class="implementations"><ul> 52 | <?js data.implementations.forEach(function(impl) { ?> 53 | <li><?js= self.linkto(impl, self.htmlsafe(impl)) ?></li> 54 | <?js }); ?> 55 | </ul></dd> 56 | <?js } ?> 57 | 58 | <?js if (data.implements && data.implements.length) { ?> 59 | <dt class="implements">Implements:</dt> 60 | <dd class="implements"><ul> 61 | <?js data.implements.forEach(function(impl) { ?> 62 | <li><?js= self.linkto(impl, self.htmlsafe(impl)) ?></li> 63 | <?js }); ?> 64 | </ul></dd> 65 | <?js } ?> 66 | 67 | <?js if (data.mixes && data.mixes.length) { ?> 68 | <dt class="mixes">Mixes In:</dt> 69 | 70 | <dd class="mixes"><ul> 71 | <?js data.mixes.forEach(function(a) { ?> 72 | <li><?js= self.linkto(a, a) ?></li> 73 | <?js }); ?> 74 | </ul></dd> 75 | <?js } ?> 76 | 77 | <?js if (data.deprecated) { ?> 78 | <dt class="important tag-deprecated">Deprecated:</dt><?js 79 | if (data.deprecated === true) { ?><dd class="yes-def tag-deprecated"><ul class="dummy"><li>Yes</li></ul></dd><?js } 80 | else { ?><dd><ul class="dummy"><li><?js= data.deprecated ?></li></ul></dd><?js } 81 | ?> 82 | <?js } ?> 83 | 84 | <?js if (data.author && author.length) {?> 85 | <dt class="tag-author">Author:</dt> 86 | <dd class="tag-author"> 87 | <ul><?js author.forEach(function(a) { ?> 88 | <li><?js= self.resolveAuthorLinks(a) ?></li> 89 | <?js }); ?></ul> 90 | </dd> 91 | <?js } ?> 92 | 93 | <?js if (data.copyright) {?> 94 | <dt class="tag-copyright">Copyright:</dt> 95 | <dd class="tag-copyright"><ul class="dummy"><li><?js= copyright ?></li></ul></dd> 96 | <?js } ?> 97 | 98 | <?js if (data.license) {?> 99 | <dt class="tag-license">License:</dt> 100 | <dd class="tag-license"><ul class="dummy"><li><?js= license ?></li></ul></dd> 101 | <?js } ?> 102 | 103 | <?js if (data.defaultvalue) {?> 104 | <dt class="tag-default">Default Value:</dt> 105 | <dd class="tag-default"><ul class="dummy"> 106 | <li<?js= defaultObjectClass ?>><?js= data.defaultvalue ?></li> 107 | </ul></dd> 108 | <?js } ?> 109 | 110 | <?js if (data.meta && self.outputSourceFiles) {?> 111 | <dt class="tag-source">Source:</dt> 112 | <dd class="tag-source"><ul class="dummy"><li> 113 | <?js= self.linkto(meta.shortpath) ?>, <?js= self.linkto(meta.shortpath, 'line ' + meta.lineno, null, 'line' + meta.lineno) ?> 114 | </li></ul></dd> 115 | <?js } ?> 116 | 117 | <?js if (data.tutorials && tutorials.length) {?> 118 | <dt class="tag-tutorial">Tutorials:</dt> 119 | <dd class="tag-tutorial"> 120 | <ul><?js tutorials.forEach(function(t) { ?> 121 | <li><?js= self.tutoriallink(t) ?></li> 122 | <?js }); ?></ul> 123 | </dd> 124 | <?js } ?> 125 | 126 | <?js if (data.see && see.length) {?> 127 | <dt class="tag-see">See:</dt> 128 | <dd class="tag-see"> 129 | <ul><?js see.forEach(function(s) { ?> 130 | <li><?js= self.linkto(s) ?></li> 131 | <?js }); ?></ul> 132 | </dd> 133 | <?js } ?> 134 | 135 | <?js if (data.todo && todo.length) {?> 136 | <dt class="tag-todo">To Do:</dt> 137 | <dd class="tag-todo"> 138 | <ul><?js todo.forEach(function(t) { ?> 139 | <li><?js= t ?></li> 140 | <?js }); ?></ul> 141 | </dd> 142 | <?js } ?> 143 | </dl> 144 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/example.tmpl: -------------------------------------------------------------------------------- 1 | <?js var data = obj; ?> 2 | <pre><code><?js= data ?></code></pre> 3 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/examples.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var self = this; 4 | 5 | data.forEach(function(example) { 6 | if (example.caption) { 7 | ?> 8 | <p class="code-caption"><?js= example.caption ?></p> 9 | <?js } ?> 10 | <pre class="prettyprint"><code><?js= self.htmlsafe(example.code) ?></code></pre> 11 | <?js 12 | }); 13 | ?> -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/exceptions.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | ?> 4 | <?js if (data.description && data.type && data.type.names) { ?> 5 | <dl> 6 | <dt> 7 | <div class="param-desc"> 8 | <?js= data.description ?> 9 | </div> 10 | </dt> 11 | <dd></dd> 12 | <dt> 13 | <dl> 14 | <dt> 15 | Type 16 | </dt> 17 | <dd> 18 | <?js= this.partial('type.tmpl', data.type.names) ?> 19 | </dd> 20 | </dl> 21 | </dt> 22 | <dd></dd> 23 | </dl> 24 | <?js } else { ?> 25 | <div class="param-desc"> 26 | <?js if (data.description) { ?> 27 | <?js= data.description ?> 28 | <?js } else if (data.type && data.type.names) { ?> 29 | <?js= this.partial('type.tmpl', data.type.names) ?> 30 | <?js } ?> 31 | </div> 32 | <?js } ?> 33 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/layout.tmpl: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>JSDoc: <?js= title ?></title> 6 | 7 | <script src="scripts/prettify/prettify.js"> </script> 8 | <script src="scripts/prettify/lang-css.js"> </script> 9 | <!--[if lt IE 9]> 10 | <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 11 | <![endif]--> 12 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> 13 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> 14 | </head> 15 | 16 | <body> 17 | 18 | <div id="main"> 19 | 20 | <h1 class="page-title"><?js= title ?></h1> 21 | 22 | <?js= content ?> 23 | </div> 24 | 25 | <nav> 26 | <?js= this.nav ?> 27 | </nav> 28 | 29 | <br class="clear"> 30 | 31 | <footer> 32 | Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc <?js= env.version.number ?></a><?js if(env.conf.templates && env.conf.templates.default && env.conf.templates.default.includeDate !== false) { ?> on <?js= (new Date()) ?><?js } ?> 33 | </footer> 34 | 35 | <script> prettyPrint(); </script> 36 | <script src="scripts/linenumber.js"> </script> 37 | </body> 38 | </html> 39 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/mainpage.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var self = this; 4 | ?> 5 | 6 | <?js if (data.kind === 'package') { ?> 7 | <h3><?js= data.name ?> <?js= data.version ?></h3> 8 | <?js } ?> 9 | 10 | <?js if (data.readme) { ?> 11 | <section> 12 | <article><?js= data.readme ?></article> 13 | </section> 14 | <?js } ?> 15 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/members.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var self = this; 4 | ?> 5 | <h4 class="name" id="<?js= id ?>"><?js= data.attribs + name + (data.signature ? data.signature : '') ?></h4> 6 | 7 | <?js if (data.summary) { ?> 8 | <p class="summary"><?js= summary ?></p> 9 | <?js } ?> 10 | 11 | <?js if (data.description) { ?> 12 | <div class="description"> 13 | <?js= data.description ?> 14 | </div> 15 | <?js } ?> 16 | 17 | <?js if (data.type && data.type.names) {?> 18 | <h5>Type:</h5> 19 | <ul> 20 | <li> 21 | <?js= self.partial('type.tmpl', data.type.names) ?> 22 | </li> 23 | </ul> 24 | <?js } ?> 25 | 26 | <?js= this.partial('details.tmpl', data) ?> 27 | 28 | <?js if (data.fires && fires.length) { ?> 29 | <h5>Fires:</h5> 30 | <ul><?js fires.forEach(function(f) { ?> 31 | <li><?js= self.linkto(f) ?></li> 32 | <?js }); ?></ul> 33 | <?js } ?> 34 | 35 | <?js if (data.examples && examples.length) { ?> 36 | <h5>Example<?js= examples.length > 1? 's':'' ?></h5> 37 | <?js= this.partial('examples.tmpl', examples) ?> 38 | <?js } ?> 39 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/method.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var self = this; 4 | ?> 5 | <?js if (data.kind !== 'module' && !data.hideconstructor) { ?> 6 | <?js if (data.kind === 'class' && data.classdesc) { ?> 7 | <h2>Constructor</h2> 8 | <?js } ?> 9 | 10 | <?js if (data.kind !== 'namespace') { ?> 11 | <h4 class="name" id="<?js= id ?>"><?js= data.attribs + (kind === 'class' ? 'new ' : '') + 12 | name + (data.signature || '') ?></h4> 13 | <?js } ?> 14 | 15 | <?js if (data.summary) { ?> 16 | <p class="summary"><?js= summary ?></p> 17 | <?js } ?> 18 | <?js } ?> 19 | 20 | <?js if (data.kind !== 'module' && data.description && !data.hideconstructor) { ?> 21 | <div class="description"> 22 | <?js= data.description ?> 23 | </div> 24 | <?js } ?> 25 | 26 | <?js if (data.augments && data.alias && data.alias.indexOf('module:') === 0) { ?> 27 | <h5>Extends:</h5> 28 | <?js= self.partial('augments.tmpl', data) ?> 29 | <?js } ?> 30 | 31 | <?js if (kind === 'event' && data.type && data.type.names) {?> 32 | <h5>Type:</h5> 33 | <ul> 34 | <li> 35 | <?js= self.partial('type.tmpl', data.type.names) ?> 36 | </li> 37 | </ul> 38 | <?js } ?> 39 | 40 | <?js if (data['this']) { ?> 41 | <h5>This:</h5> 42 | <ul><li><?js= this.linkto(data['this'], data['this']) ?></li></ul> 43 | <?js } ?> 44 | 45 | <?js if (data.params && params.length && !data.hideconstructor) { ?> 46 | <h5>Parameters:</h5> 47 | <?js= this.partial('params.tmpl', params) ?> 48 | <?js } ?> 49 | 50 | <?js= this.partial('details.tmpl', data) ?> 51 | 52 | <?js if (data.kind !== 'module' && data.requires && data.requires.length) { ?> 53 | <h5>Requires:</h5> 54 | <ul><?js data.requires.forEach(function(r) { ?> 55 | <li><?js= self.linkto(r) ?></li> 56 | <?js }); ?></ul> 57 | <?js } ?> 58 | 59 | <?js if (data.fires && fires.length) { ?> 60 | <h5>Fires:</h5> 61 | <ul><?js fires.forEach(function(f) { ?> 62 | <li><?js= self.linkto(f) ?></li> 63 | <?js }); ?></ul> 64 | <?js } ?> 65 | 66 | <?js if (data.listens && listens.length) { ?> 67 | <h5>Listens to Events:</h5> 68 | <ul><?js listens.forEach(function(f) { ?> 69 | <li><?js= self.linkto(f) ?></li> 70 | <?js }); ?></ul> 71 | <?js } ?> 72 | 73 | <?js if (data.listeners && listeners.length) { ?> 74 | <h5>Listeners of This Event:</h5> 75 | <ul><?js listeners.forEach(function(f) { ?> 76 | <li><?js= self.linkto(f) ?></li> 77 | <?js }); ?></ul> 78 | <?js } ?> 79 | 80 | <?js if (data.modifies && modifies.length) {?> 81 | <h5>Modifies:</h5> 82 | <?js if (modifies.length > 1) { ?><ul><?js 83 | modifies.forEach(function(m) { ?> 84 | <li><?js= self.partial('modifies.tmpl', m) ?></li> 85 | <?js }); 86 | ?></ul><?js } else { 87 | modifies.forEach(function(m) { ?> 88 | <?js= self.partial('modifies.tmpl', m) ?> 89 | <?js }); 90 | } } ?> 91 | 92 | <?js if (data.exceptions && exceptions.length) { ?> 93 | <h5>Throws:</h5> 94 | <?js if (exceptions.length > 1) { ?><ul><?js 95 | exceptions.forEach(function(r) { ?> 96 | <li><?js= self.partial('exceptions.tmpl', r) ?></li> 97 | <?js }); 98 | ?></ul><?js } else { 99 | exceptions.forEach(function(r) { ?> 100 | <?js= self.partial('exceptions.tmpl', r) ?> 101 | <?js }); 102 | } } ?> 103 | 104 | <?js if (data.returns && returns.length) { ?> 105 | <h5>Returns:</h5> 106 | <?js if (returns.length > 1) { ?><ul><?js 107 | returns.forEach(function(r) { ?> 108 | <li><?js= self.partial('returns.tmpl', r) ?></li> 109 | <?js }); 110 | ?></ul><?js } else { 111 | returns.forEach(function(r) { ?> 112 | <?js= self.partial('returns.tmpl', r) ?> 113 | <?js }); 114 | } } ?> 115 | 116 | <?js if (data.yields && yields.length) { ?> 117 | <h5>Yields:</h5> 118 | <?js if (yields.length > 1) { ?><ul><?js 119 | yields.forEach(function(r) { ?> 120 | <li><?js= self.partial('returns.tmpl', r) ?></li> 121 | <?js }); 122 | ?></ul><?js } else { 123 | yields.forEach(function(r) { ?> 124 | <?js= self.partial('returns.tmpl', r) ?> 125 | <?js }); 126 | } } ?> 127 | 128 | <?js if (data.examples && examples.length) { ?> 129 | <h5>Example<?js= examples.length > 1? 's':'' ?></h5> 130 | <?js= this.partial('examples.tmpl', examples) ?> 131 | <?js } ?> 132 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/modifies.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj || {}; 3 | ?> 4 | 5 | <?js if (data.type && data.type.names) {?> 6 | <dl> 7 | <dt> 8 | Type 9 | </dt> 10 | <dd> 11 | <?js= this.partial('type.tmpl', data.type.names) ?> 12 | </dd> 13 | </dl> 14 | <?js } ?> 15 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/params.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var params = obj; 3 | 4 | /* sort subparams under their parent params (like opts.classname) */ 5 | var parentParam = null; 6 | params.forEach(function(param, i) { 7 | var paramRegExp; 8 | 9 | if (!param) { 10 | return; 11 | } 12 | 13 | if (parentParam && parentParam.name && param.name) { 14 | try { 15 | paramRegExp = new RegExp('^(?:' + parentParam.name + '(?:\\[\\])*)\\.(.+)#39;); 16 | } 17 | catch (e) { 18 | // there's probably a typo in the JSDoc comment that resulted in a weird 19 | // parameter name 20 | return; 21 | } 22 | 23 | if ( paramRegExp.test(param.name) ) { 24 | param.name = RegExp.$1; 25 | parentParam.subparams = parentParam.subparams || []; 26 | parentParam.subparams.push(param); 27 | params[i] = null; 28 | } 29 | else { 30 | parentParam = param; 31 | } 32 | } 33 | else { 34 | parentParam = param; 35 | } 36 | }); 37 | 38 | /* determine if we need extra columns, "attributes" and "default" */ 39 | params.hasAttributes = false; 40 | params.hasDefault = false; 41 | params.hasName = false; 42 | 43 | params.forEach(function(param) { 44 | if (!param) { return; } 45 | 46 | if (param.optional || param.nullable || param.variable) { 47 | params.hasAttributes = true; 48 | } 49 | 50 | if (param.name) { 51 | params.hasName = true; 52 | } 53 | 54 | if (typeof param.defaultvalue !== 'undefined') { 55 | params.hasDefault = true; 56 | } 57 | }); 58 | ?> 59 | 60 | <table class="params"> 61 | <thead> 62 | <tr> 63 | <?js if (params.hasName) {?> 64 | <th>Name</th> 65 | <?js } ?> 66 | 67 | <th>Type</th> 68 | 69 | <?js if (params.hasAttributes) {?> 70 | <th>Attributes</th> 71 | <?js } ?> 72 | 73 | <?js if (params.hasDefault) {?> 74 | <th>Default</th> 75 | <?js } ?> 76 | 77 | <th class="last">Description</th> 78 | </tr> 79 | </thead> 80 | 81 | <tbody> 82 | <?js 83 | var self = this; 84 | params.forEach(function(param) { 85 | if (!param) { return; } 86 | ?> 87 | 88 | <tr> 89 | <?js if (params.hasName) {?> 90 | <td class="name"><code><?js= param.name ?></code></td> 91 | <?js } ?> 92 | 93 | <td class="type"> 94 | <?js if (param.type && param.type.names) {?> 95 | <?js= self.partial('type.tmpl', param.type.names) ?> 96 | <?js } ?> 97 | </td> 98 | 99 | <?js if (params.hasAttributes) {?> 100 | <td class="attributes"> 101 | <?js if (param.optional) { ?> 102 | <optional><br> 103 | <?js } ?> 104 | 105 | <?js if (param.nullable) { ?> 106 | <nullable><br> 107 | <?js } ?> 108 | 109 | <?js if (param.variable) { ?> 110 | <repeatable><br> 111 | <?js } ?> 112 | </td> 113 | <?js } ?> 114 | 115 | <?js if (params.hasDefault) {?> 116 | <td class="default"> 117 | <?js if (typeof param.defaultvalue !== 'undefined') { ?> 118 | <?js= self.htmlsafe(param.defaultvalue) ?> 119 | <?js } ?> 120 | </td> 121 | <?js } ?> 122 | 123 | <td class="description last"><?js= param.description ?><?js if (param.subparams) { ?> 124 | <h6>Properties</h6> 125 | <?js= self.partial('params.tmpl', param.subparams) ?> 126 | <?js } ?></td> 127 | </tr> 128 | 129 | <?js }); ?> 130 | </tbody> 131 | </table> 132 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/properties.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var props = data.subprops || data.properties; 4 | 5 | /* sort subprops under their parent props (like opts.classname) */ 6 | var parentProp = null; 7 | props.forEach(function(prop, i) { 8 | if (!prop) { return; } 9 | if ( parentProp && prop.name && prop.name.indexOf(parentProp.name + '.') === 0 ) { 10 | prop.name = prop.name.substring(parentProp.name.length + 1); 11 | parentProp.subprops = parentProp.subprops || []; 12 | parentProp.subprops.push(prop); 13 | props[i] = null; 14 | } 15 | else { 16 | parentProp = prop; 17 | } 18 | }); 19 | 20 | /* determine if we need extra columns, "attributes" and "default" */ 21 | props.hasAttributes = false; 22 | props.hasDefault = false; 23 | props.hasName = false; 24 | 25 | props.forEach(function(prop) { 26 | if (!prop) { return; } 27 | 28 | if (prop.optional || prop.nullable) { 29 | props.hasAttributes = true; 30 | } 31 | 32 | if (prop.name) { 33 | props.hasName = true; 34 | } 35 | 36 | if (typeof prop.defaultvalue !== 'undefined' && !data.isEnum) { 37 | props.hasDefault = true; 38 | } 39 | }); 40 | ?> 41 | 42 | <table class="props"> 43 | <thead> 44 | <tr> 45 | <?js if (props.hasName) {?> 46 | <th>Name</th> 47 | <?js } ?> 48 | 49 | <th>Type</th> 50 | 51 | <?js if (props.hasAttributes) {?> 52 | <th>Attributes</th> 53 | <?js } ?> 54 | 55 | <?js if (props.hasDefault) {?> 56 | <th>Default</th> 57 | <?js } ?> 58 | 59 | <th class="last">Description</th> 60 | </tr> 61 | </thead> 62 | 63 | <tbody> 64 | <?js 65 | var self = this; 66 | props.forEach(function(prop) { 67 | if (!prop) { return; } 68 | ?> 69 | 70 | <tr> 71 | <?js if (props.hasName) {?> 72 | <td class="name"><code><?js= prop.name ?></code></td> 73 | <?js } ?> 74 | 75 | <td class="type"> 76 | <?js if (prop.type && prop.type.names) {?> 77 | <?js= self.partial('type.tmpl', prop.type.names) ?> 78 | <?js } ?> 79 | </td> 80 | 81 | <?js if (props.hasAttributes) {?> 82 | <td class="attributes"> 83 | <?js if (prop.optional) { ?> 84 | <optional><br> 85 | <?js } ?> 86 | 87 | <?js if (prop.nullable) { ?> 88 | <nullable><br> 89 | <?js } ?> 90 | </td> 91 | <?js } ?> 92 | 93 | <?js if (props.hasDefault) {?> 94 | <td class="default"> 95 | <?js if (typeof prop.defaultvalue !== 'undefined') { ?> 96 | <?js= self.htmlsafe(prop.defaultvalue) ?> 97 | <?js } ?> 98 | </td> 99 | <?js } ?> 100 | 101 | <td class="description last"><?js= prop.description ?><?js if (prop.subprops) { ?> 102 | <h6>Properties</h6><?js= self.partial('properties.tmpl', prop) ?> 103 | <?js } ?></td> 104 | </tr> 105 | 106 | <?js }); ?> 107 | </tbody> 108 | </table> 109 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/returns.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj || {}; 3 | if (data.description) { 4 | ?> 5 | <div class="param-desc"> 6 | <?js= description ?> 7 | </div> 8 | <?js } ?> 9 | 10 | <?js if (data.type && data.type.names) {?> 11 | <dl> 12 | <dt> 13 | Type 14 | </dt> 15 | <dd> 16 | <?js= this.partial('type.tmpl', data.type.names) ?> 17 | </dd> 18 | </dl> 19 | <?js } ?> -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/source.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | ?> 4 | <section> 5 | <article> 6 | <pre class="prettyprint source linenums"><code><?js= data.code ?></code></pre> 7 | </article> 8 | </section> -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/tutorial.tmpl: -------------------------------------------------------------------------------- 1 | <section> 2 | 3 | <header> 4 | <?js if (children.length > 0) { ?> 5 | <ul><?js 6 | var self = this; 7 | children.forEach(function(t) { ?> 8 | <li><?js= self.tutoriallink(t.name) ?></li> 9 | <?js }); ?></ul> 10 | <?js } ?> 11 | 12 | <h2><?js= header ?></h2> 13 | </header> 14 | 15 | <article> 16 | <?js= content ?> 17 | </article> 18 | 19 | </section> 20 | -------------------------------------------------------------------------------- /jsdoc/templates/custom/tmpl/type.tmpl: -------------------------------------------------------------------------------- 1 | <?js 2 | var data = obj; 3 | var self = this; 4 | data.forEach(function(name, i) { ?> 5 | <span class="param-type"><?js= self.linkto(name, self.htmlsafe(name)) ?></span> 6 | <?js if (i < data.length-1) { ?>|<?js } ?> 7 | <?js }); ?> -------------------------------------------------------------------------------- /lib/assert.js: -------------------------------------------------------------------------------- 1 | const {assertOptions} = require('assert-options'); 2 | 3 | // this to allow override options-related errors globally (for pg-promise) 4 | global.pgPromiseAssert = assertOptions; 5 | 6 | module.exports = { 7 | assert() { 8 | return global.pgPromiseAssert.apply(null, [...arguments]); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /lib/connect.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {Events} = require('./events'); 11 | const {ColorConsole} = require('./utils/color'); 12 | 13 | const npm = { 14 | utils: require('./utils'), 15 | text: require('./text'), 16 | formatting: require('./formatting') 17 | }; 18 | 19 | function poolConnect(ctx, db, config) { 20 | return config.promise((resolve, reject) => { 21 | const p = db.$pool; 22 | if (p.ending) { 23 | db.$destroy(); 24 | const err = new Error(npm.text.poolDestroyed); 25 | Events.error(ctx.options, err, { 26 | dc: ctx.dc 27 | }); 28 | reject(err); 29 | return; 30 | } 31 | p.connect((err, client) => { 32 | if (err) { 33 | Events.error(ctx.options, err, { 34 | cn: npm.utils.getSafeConnection(ctx.cn), 35 | dc: ctx.dc 36 | }); 37 | reject(err); 38 | } else { 39 | if ('$useCount' in client) { 40 | // Make sure useCount drops to 1, if it ever reaches maximum integer number; 41 | // We do not drop it to zero, to avoid rerun of initialization queries that 42 | // usually check for useCount === 0; 43 | // istanbul ignore if 44 | if (client.$useCount >= Number.MAX_SAFE_INTEGER) { 45 | client.$useCount = 1; // resetting; cannot auto-test this 46 | } else { 47 | client.$useCount = ++client.$useCount; 48 | } 49 | } else { 50 | Object.defineProperty(client, '$useCount', { 51 | value: 0, 52 | configurable: false, 53 | enumerable: false, 54 | writable: true 55 | }); 56 | setSchema(client, ctx); 57 | } 58 | setCtx(client, ctx); 59 | const end = lockClientEnd(client); 60 | client.on('error', onError); 61 | resolve({ 62 | client, 63 | useCount: client.$useCount, 64 | release(kill) { 65 | client.end = end; 66 | client.release(kill || client.$connectionError); 67 | Events.disconnect(ctx, client); 68 | client.removeListener('error', onError); 69 | } 70 | }); 71 | Events.connect(ctx, client, client.$useCount); 72 | } 73 | }); 74 | }); 75 | } 76 | 77 | function directConnect(ctx, config) { 78 | return config.promise((resolve, reject) => { 79 | const client = new config.pgp.pg.Client(ctx.cn); 80 | client.connect(err => { 81 | if (err) { 82 | Events.error(ctx.options, err, { 83 | cn: npm.utils.getSafeConnection(ctx.cn), 84 | dc: ctx.dc 85 | }); 86 | reject(err); 87 | } else { 88 | setSchema(client, ctx); 89 | setCtx(client, ctx); 90 | const end = lockClientEnd(client); 91 | client.on('error', onError); 92 | resolve({ 93 | client, 94 | useCount: 0, 95 | release() { 96 | client.end = end; 97 | const p = config.promise((res, rej) => client.end().then(res).catch(rej)); 98 | Events.disconnect(ctx, client); 99 | client.removeListener('error', onError); 100 | return p; 101 | } 102 | }); 103 | Events.connect(ctx, client, 0); 104 | } 105 | }); 106 | }); 107 | } 108 | 109 | // this event only happens when the connection is lost physically, 110 | // which cannot be tested automatically; removing from coverage: 111 | // istanbul ignore next 112 | function onError(err) { 113 | const ctx = this.$ctx; 114 | const cn = npm.utils.getSafeConnection(ctx.cn); 115 | Events.error(ctx.options, err, {cn, dc: ctx.dc}); 116 | if (ctx.cnOptions && typeof ctx.cnOptions.onLost === 'function' && !ctx.notified) { 117 | try { 118 | ctx.cnOptions.onLost.call(this, err, { 119 | cn, 120 | dc: ctx.dc, 121 | start: ctx.start, 122 | client: this 123 | }); 124 | } catch (e) { 125 | ColorConsole.error(e && e.stack || e); 126 | } 127 | ctx.notified = true; 128 | } 129 | } 130 | 131 | function lockClientEnd(client) { 132 | const end = client.end; 133 | client.end = doNotCall => { 134 | // This call can happen only in the following two cases: 135 | // 1. the client made the call directly, against the library's documentation (invalid code) 136 | // 2. connection with the server broke, and the pool is terminating all clients forcefully. 137 | ColorConsole.error(`${npm.text.clientEnd}\n${npm.utils.getLocalStack(1, 3)}\n`); 138 | if (!doNotCall) { 139 | end.call(client); 140 | } 141 | }; 142 | return end; 143 | } 144 | 145 | function setCtx(client, ctx) { 146 | Object.defineProperty(client, '$ctx', { 147 | value: ctx, 148 | writable: true 149 | }); 150 | } 151 | 152 | function setSchema(client, ctx) { 153 | let s = ctx.options.schema; 154 | if (!s) { 155 | return; 156 | } 157 | if (typeof s === 'function') { 158 | s = s.call(ctx.dc, ctx.dc); 159 | } 160 | if (Array.isArray(s)) { 161 | s = s.filter(a => a && typeof a === 'string'); 162 | } 163 | if (typeof s === 'string' || (Array.isArray(s) && s.length)) { 164 | client.query(npm.formatting.as.format('SET search_path TO $1:name', [s]), err => { 165 | // istanbul ignore if; 166 | if (err) { 167 | // This is unlikely to ever happen, unless the connection is created faulty, 168 | // and fails on the very first query, which is impossible to test automatically. 169 | throw err; 170 | } 171 | }); 172 | } 173 | } 174 | 175 | module.exports = config => ({ 176 | pool: (ctx, db) => poolConnect(ctx, db, config), 177 | direct: ctx => directConnect(ctx, config) 178 | }); 179 | -------------------------------------------------------------------------------- /lib/context.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | /** 11 | * @class ConnectionContext 12 | * @private 13 | * @summary Internal connection context. 14 | * 15 | * @param {object} cc 16 | * Connection Context. 17 | * 18 | * @param {object} cc.cn 19 | * Connection details 20 | * 21 | * @param {*} cc.dc 22 | * Database Context 23 | * 24 | * @param {object} cc.options 25 | * Library's Initialization Options 26 | * 27 | * @param {object} cc.db 28 | * Database Session we're attached to, if any. 29 | * 30 | * @param {number} cc.level 31 | * Task Level 32 | * 33 | * @param {number} cc.txLevel 34 | * Transaction Level 35 | * 36 | * @param {object} cc.parentCtx 37 | * Connection Context of the parent operation, if any. 38 | * 39 | */ 40 | class ConnectionContext { 41 | 42 | constructor(cc) { 43 | this.cn = cc.cn; // connection details; 44 | this.dc = cc.dc; // database context; 45 | this.options = cc.options; // library options; 46 | this.db = cc.db; // database session; 47 | this.level = cc.level; // task level; 48 | this.txLevel = cc.txLevel; // transaction level; 49 | this.parentCtx = null; // parent context 50 | this.taskCtx = null; // task context 51 | this.start = null; // Date/Time when connected 52 | this.txCount = 0; 53 | } 54 | 55 | connect(db) { 56 | this.db = db; 57 | this.start = new Date(); 58 | } 59 | 60 | disconnect(kill) { 61 | if (this.db) { 62 | const p = this.db.release(kill); 63 | this.db = null; 64 | return p; 65 | } 66 | } 67 | 68 | clone() { 69 | const obj = new ConnectionContext(this); 70 | obj.parent = this; 71 | obj.parentCtx = this.taskCtx; 72 | return obj; 73 | } 74 | 75 | get nextTxCount() { 76 | let txCurrent = this, txTop = this; 77 | while (txCurrent.parent) { 78 | txCurrent = txCurrent.parent; 79 | if (txCurrent.taskCtx && txCurrent.taskCtx.isTX) { 80 | txTop = txCurrent; 81 | } 82 | } 83 | return txTop.txCount++; 84 | } 85 | } 86 | 87 | /** 88 | * Connection Context 89 | * @module context 90 | * @author Vitaly Tomilov 91 | * @private 92 | */ 93 | module.exports = {ConnectionContext}; 94 | -------------------------------------------------------------------------------- /lib/database-pool.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {ColorConsole} = require('./utils/color'); 11 | 12 | const npm = { 13 | utils: require('./utils') 14 | }; 15 | 16 | /** 17 | * @class DatabasePool 18 | * @static 19 | * @private 20 | */ 21 | class DatabasePool { 22 | 23 | /** 24 | * Global instance of the database pool repository. 25 | * 26 | * @returns {{dbMap: {}, dbs: Array}} 27 | */ 28 | static get instance() { 29 | const s = Symbol.for('pgPromiseDatabasePool'); 30 | let scope = global[s]; 31 | if (!scope) { 32 | scope = { 33 | dbMap: {}, // map of used database context keys (connection + dc) 34 | dbs: [] // all database objects 35 | }; 36 | global[s] = scope; 37 | } 38 | return scope; 39 | } 40 | 41 | /** 42 | * @method DatabasePool.register 43 | * @static 44 | * @description 45 | * - Registers each database object, to make sure no duplicates connections are used, 46 | * and if they are, produce a warning; 47 | * - Registers each Pool object, to be able to release them all when requested. 48 | * 49 | * @param {Database} db - The new Database object being registered. 50 | */ 51 | static register(db) { 52 | const cnKey = DatabasePool.createContextKey(db); 53 | npm.utils.addReadProp(db, '$cnKey', cnKey, true); 54 | const {dbMap, dbs} = DatabasePool.instance; 55 | if (cnKey in dbMap) { 56 | dbMap[cnKey]++; 57 | /* istanbul ignore if */ 58 | if (!db.$config.options.noWarnings) { 59 | ColorConsole.warn(`WARNING: Creating a duplicate database object for the same connection.\n${npm.utils.getLocalStack(4, 3)}\n`); 60 | } 61 | } else { 62 | dbMap[cnKey] = 1; 63 | } 64 | dbs.push(db); 65 | } 66 | 67 | /** 68 | * @method DatabasePool.unregister 69 | * @static 70 | * @param db 71 | */ 72 | static unregister(db) { 73 | const cnKey = db.$cnKey; 74 | const {dbMap} = DatabasePool.instance; 75 | if (!--dbMap[cnKey]) { 76 | delete dbMap[cnKey]; 77 | } 78 | } 79 | 80 | /** 81 | * @method DatabasePool.shutDown 82 | * @static 83 | */ 84 | static shutDown() { 85 | const {instance} = DatabasePool; 86 | instance.dbs.forEach(db => { 87 | db.$destroy(); 88 | }); 89 | instance.dbs.length = 0; 90 | instance.dbMap = {}; 91 | } 92 | 93 | /** 94 | * @method DatabasePool.createContextKey 95 | * @static 96 | * @description 97 | * For connections that are objects it reorders the keys alphabetically, 98 | * and then serializes the result into a JSON string. 99 | * 100 | * @param {Database} db - Database instance. 101 | */ 102 | static createContextKey(db) { 103 | let cn = db.$cn; 104 | if (typeof cn === 'object') { 105 | const obj = {}, keys = Object.keys(cn).sort(); 106 | keys.forEach(name => { 107 | obj[name] = cn[name]; 108 | }); 109 | cn = obj; 110 | } 111 | return npm.utils.toJson(npm.utils.getSafeConnection(cn)) + npm.utils.toJson(db.$dc); 112 | } 113 | } 114 | 115 | module.exports = {DatabasePool}; 116 | -------------------------------------------------------------------------------- /lib/errors/README.md: -------------------------------------------------------------------------------- 1 | ### `errors` namespace 2 | 3 | This folder contains everything that's available via the [errors] namespace, before and after initialization: 4 | 5 | ```js 6 | const pgpLib = require('pg-promise'); 7 | const pgp = pgpLib(/*initialization options*/); 8 | 9 | pgpLib.errors; // `errors` namespace 10 | pgp.errors; // `errors` namespace 11 | ``` 12 | 13 | [errors]:http://vitaly-t.github.io/pg-promise/errors.html 14 | -------------------------------------------------------------------------------- /lib/errors/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {QueryResultError, queryResultErrorCode} = require('./query-result-error'); 11 | const {PreparedStatementError} = require('./prepared-statement-error'); 12 | const {ParameterizedQueryError} = require('./parameterized-query-error'); 13 | const {QueryFileError} = require('./query-file-error'); 14 | 15 | /** 16 | * @namespace errors 17 | * @description 18 | * Error types namespace, available as `pgp.errors`, before and after initializing the library. 19 | * 20 | * @property {function} PreparedStatementError 21 | * {@link errors.PreparedStatementError PreparedStatementError} class constructor. 22 | * 23 | * Represents all errors that can be reported by class {@link PreparedStatement}. 24 | * 25 | * @property {function} ParameterizedQueryError 26 | * {@link errors.ParameterizedQueryError ParameterizedQueryError} class constructor. 27 | * 28 | * Represents all errors that can be reported by class {@link ParameterizedQuery}. 29 | * 30 | * @property {function} QueryFileError 31 | * {@link errors.QueryFileError QueryFileError} class constructor. 32 | * 33 | * Represents all errors that can be reported by class {@link QueryFile}. 34 | * 35 | * @property {function} QueryResultError 36 | * {@link errors.QueryResultError QueryResultError} class constructor. 37 | * 38 | * Represents all result-specific errors from query methods. 39 | * 40 | * @property {errors.queryResultErrorCode} queryResultErrorCode 41 | * Error codes `enum` used by class {@link errors.QueryResultError QueryResultError}. 42 | * 43 | */ 44 | 45 | module.exports = { 46 | QueryResultError, 47 | queryResultErrorCode, 48 | PreparedStatementError, 49 | ParameterizedQueryError, 50 | QueryFileError 51 | }; 52 | -------------------------------------------------------------------------------- /lib/errors/parameterized-query-error.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {QueryFileError} = require('./query-file-error'); 11 | 12 | const npm = { 13 | os: require('os'), 14 | utils: require('../utils') 15 | }; 16 | 17 | /** 18 | * @class errors.ParameterizedQueryError 19 | * @augments external:Error 20 | * @description 21 | * {@link errors.ParameterizedQueryError ParameterizedQueryError} class, available from the {@link errors} namespace. 22 | * 23 | * This type represents all errors that can be reported by class {@link ParameterizedQuery}, whether it is used 24 | * explicitly or implicitly (via a simple `{text, values}` object). 25 | * 26 | * @property {string} name 27 | * Standard {@link external:Error Error} property - error type name = `ParameterizedQueryError`. 28 | * 29 | * @property {string} message 30 | * Standard {@link external:Error Error} property - the error message. 31 | * 32 | * @property {string} stack 33 | * Standard {@link external:Error Error} property - the stack trace. 34 | * 35 | * @property {errors.QueryFileError} error 36 | * Internal {@link errors.QueryFileError} object. 37 | * 38 | * It is set only when the source {@link ParameterizedQuery} used a {@link QueryFile} which threw the error. 39 | * 40 | * @property {object} result 41 | * Resulting Parameterized Query object. 42 | * 43 | * @see ParameterizedQuery 44 | */ 45 | class ParameterizedQueryError extends Error { 46 | constructor(error, pq) { 47 | const isQueryFileError = error instanceof QueryFileError; 48 | const message = isQueryFileError ? 'Failed to initialize \'text\' from a QueryFile.' : error; 49 | super(message); 50 | this.name = this.constructor.name; 51 | if (isQueryFileError) { 52 | this.error = error; 53 | } 54 | this.result = pq; 55 | Error.captureStackTrace(this, this.constructor); 56 | } 57 | } 58 | 59 | /** 60 | * @method errors.ParameterizedQueryError#toString 61 | * @description 62 | * Creates a well-formatted multi-line string that represents the error. 63 | * 64 | * It is called automatically when writing the object into the console. 65 | * 66 | * @param {number} [level=0] 67 | * Nested output level, to provide visual offset. 68 | * 69 | * @returns {string} 70 | */ 71 | ParameterizedQueryError.prototype.toString = function (level) { 72 | level = level > 0 ? parseInt(level) : 0; 73 | const gap0 = npm.utils.messageGap(level), 74 | gap1 = npm.utils.messageGap(level + 1), 75 | gap2 = npm.utils.messageGap(level + 2), 76 | lines = [ 77 | 'ParameterizedQueryError {', 78 | gap1 + 'message: "' + this.message + '"', 79 | gap1 + 'result: {', 80 | gap2 + 'text: ' + npm.utils.toJson(this.result.text), 81 | gap2 + 'values: ' + npm.utils.toJson(this.result.values), 82 | gap1 + '}' 83 | ]; 84 | if (this.error) { 85 | lines.push(gap1 + 'error: ' + this.error.toString(level + 1)); 86 | } 87 | lines.push(gap0 + '}'); 88 | return lines.join(npm.os.EOL); 89 | }; 90 | 91 | npm.utils.addInspection(ParameterizedQueryError, function () { 92 | return this.toString(); 93 | }); 94 | 95 | module.exports = {ParameterizedQueryError}; 96 | -------------------------------------------------------------------------------- /lib/errors/prepared-statement-error.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {QueryFileError} = require('./query-file-error'); 11 | 12 | const npm = { 13 | os: require('os'), 14 | utils: require('../utils') 15 | }; 16 | 17 | /** 18 | * @class errors.PreparedStatementError 19 | * @augments external:Error 20 | * @description 21 | * {@link errors.PreparedStatementError PreparedStatementError} class, available from the {@link errors} namespace. 22 | * 23 | * This type represents all errors that can be reported by class {@link PreparedStatement}, whether it is used 24 | * explicitly or implicitly (via a simple `{name, text, values}` object). 25 | * 26 | * @property {string} name 27 | * Standard {@link external:Error Error} property - error type name = `PreparedStatementError`. 28 | * 29 | * @property {string} message 30 | * Standard {@link external:Error Error} property - the error message. 31 | * 32 | * @property {string} stack 33 | * Standard {@link external:Error Error} property - the stack trace. 34 | * 35 | * @property {errors.QueryFileError} error 36 | * Internal {@link errors.QueryFileError} object. 37 | * 38 | * It is set only when the source {@link PreparedStatement} used a {@link QueryFile} which threw the error. 39 | * 40 | * @property {object} result 41 | * Resulting Prepared Statement object. 42 | * 43 | * @see PreparedStatement 44 | */ 45 | class PreparedStatementError extends Error { 46 | constructor(error, ps) { 47 | const isQueryFileError = error instanceof QueryFileError; 48 | const message = isQueryFileError ? 'Failed to initialize \'text\' from a QueryFile.' : error; 49 | super(message); 50 | this.name = this.constructor.name; 51 | if (isQueryFileError) { 52 | this.error = error; 53 | } 54 | this.result = ps; 55 | Error.captureStackTrace(this, this.constructor); 56 | } 57 | } 58 | 59 | /** 60 | * @method errors.PreparedStatementError#toString 61 | * @description 62 | * Creates a well-formatted multi-line string that represents the error. 63 | * 64 | * It is called automatically when writing the object into the console. 65 | * 66 | * @param {number} [level=0] 67 | * Nested output level, to provide visual offset. 68 | * 69 | * @returns {string} 70 | */ 71 | PreparedStatementError.prototype.toString = function (level) { 72 | level = level > 0 ? parseInt(level) : 0; 73 | const gap0 = npm.utils.messageGap(level), 74 | gap1 = npm.utils.messageGap(level + 1), 75 | gap2 = npm.utils.messageGap(level + 2), 76 | lines = [ 77 | 'PreparedStatementError {', 78 | gap1 + 'message: "' + this.message + '"', 79 | gap1 + 'result: {', 80 | gap2 + 'name: ' + npm.utils.toJson(this.result.name), 81 | gap2 + 'text: ' + npm.utils.toJson(this.result.text), 82 | gap2 + 'values: ' + npm.utils.toJson(this.result.values), 83 | gap1 + '}' 84 | ]; 85 | if (this.error) { 86 | lines.push(gap1 + 'error: ' + this.error.toString(level + 1)); 87 | } 88 | lines.push(gap0 + '}'); 89 | return lines.join(npm.os.EOL); 90 | }; 91 | 92 | npm.utils.addInspection(PreparedStatementError, function () { 93 | return this.toString(); 94 | }); 95 | 96 | module.exports = {PreparedStatementError}; 97 | -------------------------------------------------------------------------------- /lib/errors/query-file-error.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const npm = { 11 | os: require('os'), 12 | utils: require('../utils'), 13 | minify: require('pg-minify') 14 | }; 15 | 16 | /** 17 | * @class errors.QueryFileError 18 | * @augments external:Error 19 | * @description 20 | * {@link errors.QueryFileError QueryFileError} class, available from the {@link errors} namespace. 21 | * 22 | * This type represents all errors related to {@link QueryFile}. 23 | * 24 | * @property {string} name 25 | * Standard {@link external:Error Error} property - error type name = `QueryFileError`. 26 | * 27 | * @property {string} message 28 | * Standard {@link external:Error Error} property - the error message. 29 | * 30 | * @property {string} stack 31 | * Standard {@link external:Error Error} property - the stack trace. 32 | * 33 | * @property {string} file 34 | * File path/name that was passed into the {@link QueryFile} constructor. 35 | * 36 | * @property {object} options 37 | * Set of options that was used by the {@link QueryFile} object. 38 | * 39 | * @property {SQLParsingError} error 40 | * Internal $[SQLParsingError] object. 41 | * 42 | * It is set only when the error was thrown by $[pg-minify] while parsing the SQL file. 43 | * 44 | * @see QueryFile 45 | * 46 | */ 47 | class QueryFileError extends Error { 48 | constructor(error, qf) { 49 | const isSqlError = error instanceof npm.minify.SQLParsingError; 50 | const message = isSqlError ? 'Failed to parse the SQL.' : error.message; 51 | super(message); 52 | this.name = this.constructor.name; 53 | if (isSqlError) { 54 | this.error = error; 55 | } 56 | this.file = qf.file; 57 | this.options = qf.options; 58 | Error.captureStackTrace(this, this.constructor); 59 | } 60 | } 61 | 62 | /** 63 | * @method errors.QueryFileError#toString 64 | * @description 65 | * Creates a well-formatted multi-line string that represents the error. 66 | * 67 | * It is called automatically when writing the object into the console. 68 | * 69 | * @param {number} [level=0] 70 | * Nested output level, to provide visual offset. 71 | * 72 | * @returns {string} 73 | */ 74 | QueryFileError.prototype.toString = function (level) { 75 | level = level > 0 ? parseInt(level) : 0; 76 | const gap0 = npm.utils.messageGap(level), 77 | gap1 = npm.utils.messageGap(level + 1), 78 | lines = [ 79 | 'QueryFileError {', 80 | gap1 + 'message: "' + this.message + '"', 81 | gap1 + 'options: ' + npm.utils.toJson(this.options), 82 | gap1 + 'file: "' + this.file + '"' 83 | ]; 84 | if (this.error) { 85 | lines.push(gap1 + 'error: ' + this.error.toString(level + 1)); 86 | } 87 | lines.push(gap0 + '}'); 88 | return lines.join(npm.os.EOL); 89 | }; 90 | 91 | npm.utils.addInspection(QueryFileError, function () { 92 | return this.toString(); 93 | }); 94 | 95 | module.exports = {QueryFileError}; 96 | -------------------------------------------------------------------------------- /lib/errors/query-result-error.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const npm = { 11 | os: require('os'), 12 | utils: require('../utils'), 13 | text: require('../text') 14 | }; 15 | 16 | /** 17 | * @enum {number} 18 | * @alias errors.queryResultErrorCode 19 | * @readonly 20 | * @description 21 | * `queryResultErrorCode` enumerator, available from the {@link errors} namespace. 22 | * 23 | * Represents an integer code for each type of error supported by type {@link errors.QueryResultError}. 24 | * 25 | * @see {@link errors.QueryResultError} 26 | */ 27 | const queryResultErrorCode = { 28 | /** No data returned from the query. */ 29 | noData: 0, 30 | 31 | /** No return data was expected. */ 32 | notEmpty: 1, 33 | 34 | /** Multiple rows were not expected. */ 35 | multiple: 2 36 | }; 37 | 38 | const errorMessages = [ 39 | {name: 'noData', message: npm.text.noData}, 40 | {name: 'notEmpty', message: npm.text.notEmpty}, 41 | {name: 'multiple', message: npm.text.multiple} 42 | ]; 43 | 44 | /** 45 | * @class errors.QueryResultError 46 | * @augments external:Error 47 | * @description 48 | * 49 | * This error is specified as the rejection reason for all result-specific methods when the result doesn't match 50 | * the expectation, i.e. when a query result doesn't match its Query Result Mask - the value of {@link queryResult}. 51 | * 52 | * The error applies to the result from the following methods: {@link Database#none none}, 53 | * {@link Database#one one}, {@link Database#oneOrNone oneOrNone} and {@link Database#many many}. 54 | * 55 | * Supported errors: 56 | * 57 | * - `No return data was expected.`, method {@link Database#none none} 58 | * - `No data returned from the query.`, methods {@link Database#one one} and {@link Database#many many} 59 | * - `Multiple rows were not expected.`, methods {@link Database#one one} and {@link Database#oneOrNone oneOrNone} 60 | * 61 | * Like any other error, this one is notified with through the global event {@link event:error error}. 62 | * 63 | * The type is available from the {@link errors} namespace. 64 | * 65 | * @property {string} name 66 | * Standard {@link external:Error Error} property - error type name = `QueryResultError`. 67 | * 68 | * @property {string} message 69 | * Standard {@link external:Error Error} property - the error message. 70 | * 71 | * @property {string} stack 72 | * Standard {@link external:Error Error} property - the stack trace. 73 | * 74 | * @property {object} result 75 | * The original $[Result] object that was received. 76 | * 77 | * @property {number} received 78 | * Total number of rows received. It is simply the value of `result.rows.length`. 79 | * 80 | * @property {number} code 81 | * Error code - {@link errors.queryResultErrorCode queryResultErrorCode} value. 82 | * 83 | * @property {string} query 84 | * Query that was executed. 85 | * 86 | * Normally, it is the query already formatted with values, if there were any. 87 | * But if you are using initialization option `pgFormatting`, then the query string is before formatting. 88 | * 89 | * @property {*} values 90 | * Values passed in as query parameters. Available only when initialization option `pgFormatting` is used. 91 | * Otherwise, the values are within the pre-formatted `query` string. 92 | * 93 | * @example 94 | * 95 | * const QueryResultError = pgp.errors.QueryResultError; 96 | * const qrec = pgp.errors.queryResultErrorCode; 97 | * 98 | * const initOptions = { 99 | * 100 | * // pg-promise initialization options... 101 | * 102 | * error(err, e) { 103 | * if (err instanceof QueryResultError) { 104 | * // A query returned unexpected number of records, and thus rejected; 105 | * 106 | * // we can check the error code, if we want specifics: 107 | * if(err.code === qrec.noData) { 108 | * // expected some data, but received none; 109 | * } 110 | * 111 | * // If you write QueryResultError into the console, 112 | * // you will get a nicely formatted output. 113 | * 114 | * console.log(err); 115 | * 116 | * // See also: err, e.query, e.params, etc. 117 | * } 118 | * } 119 | * }; 120 | * 121 | * @see 122 | * {@link queryResult}, {@link Database#none none}, {@link Database#one one}, 123 | * {@link Database#oneOrNone oneOrNone}, {@link Database#many many} 124 | * 125 | */ 126 | class QueryResultError extends Error { 127 | constructor(code, result, query, values) { 128 | const message = errorMessages[code].message; 129 | super(message); 130 | this.name = this.constructor.name; 131 | this.code = code; 132 | this.result = result; 133 | this.query = query; 134 | this.values = values; 135 | this.received = result.rows.length; 136 | Error.captureStackTrace(this, this.constructor); 137 | } 138 | } 139 | 140 | /** 141 | * @method errors.QueryResultError#toString 142 | * @description 143 | * Creates a well-formatted multi-line string that represents the error. 144 | * 145 | * It is called automatically when writing the object into the console. 146 | * 147 | * @param {number} [level=0] 148 | * Nested output level, to provide visual offset. 149 | * 150 | * @returns {string} 151 | */ 152 | QueryResultError.prototype.toString = function (level) { 153 | level = level > 0 ? parseInt(level) : 0; 154 | const gap0 = npm.utils.messageGap(level), 155 | gap1 = npm.utils.messageGap(level + 1), 156 | lines = [ 157 | 'QueryResultError {', 158 | gap1 + 'code: queryResultErrorCode.' + errorMessages[this.code].name, 159 | gap1 + 'message: "' + this.message + '"', 160 | gap1 + 'received: ' + this.received, 161 | gap1 + 'query: ' + (typeof this.query === 'string' ? '"' + this.query + '"' : npm.utils.toJson(this.query)) 162 | ]; 163 | if (this.values !== undefined) { 164 | lines.push(gap1 + 'values: ' + npm.utils.toJson(this.values)); 165 | } 166 | lines.push(gap0 + '}'); 167 | return lines.join(npm.os.EOL); 168 | }; 169 | 170 | npm.utils.addInspection(QueryResultError, function () { 171 | return this.toString(); 172 | }); 173 | 174 | module.exports = { 175 | QueryResultError, 176 | queryResultErrorCode 177 | }; 178 | -------------------------------------------------------------------------------- /lib/helpers/README.md: -------------------------------------------------------------------------------- 1 | ### `helpers` namespace 2 | 3 | This folder contains everything that's available via the [helpers] namespace, after initializing the library: 4 | 5 | ```js 6 | const pgp = require('pg-promise')(/*initialization options*/); 7 | const helpers = pgp.helpers; // `helpers` namespace 8 | ``` 9 | 10 | [helpers]:http://vitaly-t.github.io/pg-promise/helpers.html 11 | -------------------------------------------------------------------------------- /lib/helpers/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {Column} = require('./column'); 11 | const {ColumnSet} = require('./column-set'); 12 | const {TableName, _TN} = require('./table-name'); 13 | const method = require('./methods'); 14 | 15 | /** 16 | * @namespace helpers 17 | * @description 18 | * Namespace for query-formatting generators, available as {@link module:pg-promise~helpers pgp.helpers}, after initializing the library. 19 | * 20 | * It unifies the approach to generating multi-row `INSERT` / `UPDATE` queries with the single-row ones. 21 | * 22 | * See also: $[Performance Boost]. 23 | * 24 | * @property {function} TableName 25 | * {@link helpers.TableName TableName} class constructor. 26 | * 27 | * @property {function} _TN 28 | * {@link helpers._TN _TN} Table-Name conversion function. 29 | * 30 | * @property {function} ColumnSet 31 | * {@link helpers.ColumnSet ColumnSet} class constructor. 32 | * 33 | * @property {function} Column 34 | * {@link helpers.Column Column} class constructor. 35 | * 36 | * @property {function} insert 37 | * {@link helpers.insert insert} static method. 38 | * 39 | * @property {function} update 40 | * {@link helpers.update update} static method. 41 | * 42 | * @property {function} values 43 | * {@link helpers.values values} static method. 44 | * 45 | * @property {function} sets 46 | * {@link helpers.sets sets} static method. 47 | * 48 | * @property {function} concat 49 | * {@link helpers.concat concat} static method. 50 | */ 51 | module.exports = config => { 52 | const capSQL = () => config.options && config.options.capSQL; 53 | const res = { 54 | insert(data, columns, table) { 55 | return method.insert(data, columns, table, capSQL()); 56 | }, 57 | update(data, columns, table, options) { 58 | return method.update(data, columns, table, options, capSQL()); 59 | }, 60 | concat(queries) { 61 | return method.concat(queries, capSQL()); 62 | }, 63 | values(data, columns) { 64 | return method.values(data, columns, capSQL()); 65 | }, 66 | sets(data, columns) { 67 | return method.sets(data, columns, capSQL()); 68 | }, 69 | TableName, 70 | _TN, 71 | ColumnSet, 72 | Column 73 | }; 74 | return res; 75 | }; 76 | -------------------------------------------------------------------------------- /lib/helpers/methods/concat.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {QueryFile} = require('../../query-file'); 11 | 12 | const npm = { 13 | formatting: require('../../formatting') 14 | }; 15 | 16 | /** 17 | * @method helpers.concat 18 | * @description 19 | * Formats and concatenates multiple queries into a single query string. 20 | * 21 | * Before joining the queries, the method does the following: 22 | * - Formats each query, if `values` are provided; 23 | * - Removes all leading and trailing spaces, tabs and semi-colons; 24 | * - Automatically skips all empty queries. 25 | * 26 | * @param {array<string|helpers.QueryFormat|QueryFile>} queries 27 | * Array of mixed-type elements: 28 | * - a simple query string, to be used as is 29 | * - a {@link helpers.QueryFormat QueryFormat}-like object = `{query, [values], [options]}` 30 | * - a {@link QueryFile} object 31 | * 32 | * @returns {string} 33 | * Concatenated string with all queries. 34 | * 35 | * @example 36 | * 37 | * const pgp = require('pg-promise')(); 38 | * 39 | * const qf1 = new pgp.QueryFile('./query1.sql', {minify: true}); 40 | * const qf2 = new pgp.QueryFile('./query2.sql', {minify: true}); 41 | * 42 | * const query = pgp.helpers.concat([ 43 | * {query: 'INSERT INTO Users(name, age) VALUES($1, $2)', values: ['John', 23]}, // QueryFormat-like object 44 | * {query: qf1, values: [1, 'Name']}, // QueryFile with formatting parameters 45 | * 'SELECT count(*) FROM Users', // a simple-string query, 46 | * qf2 // direct QueryFile object 47 | * ]); 48 | * 49 | * // query = concatenated string with all the queries 50 | */ 51 | function concat(queries, capSQL) { 52 | if (!Array.isArray(queries)) { 53 | throw new TypeError('Parameter \'queries\' must be an array.'); 54 | } 55 | const fmOptions = {capSQL}; 56 | const all = queries.map((q, index) => { 57 | if (typeof q === 'string') { 58 | // a simple query string without parameters: 59 | return clean(q); 60 | } 61 | if (q && typeof q === 'object') { 62 | if (q instanceof QueryFile) { 63 | // QueryFile object: 64 | return clean(q[npm.formatting.as.ctf.toPostgres]()); 65 | } 66 | if ('query' in q) { 67 | // object {query, values, options}: 68 | let opt = q.options && typeof q.options === 'object' ? q.options : {}; 69 | opt = opt.capSQL === undefined ? Object.assign(opt, fmOptions) : opt; 70 | return clean(npm.formatting.as.format(q.query, q.values, opt)); 71 | } 72 | } 73 | throw new Error(`Invalid query element at index ${index}.`); 74 | }); 75 | 76 | return all.filter(q => q).join(';'); 77 | } 78 | 79 | function clean(q) { 80 | // removes from the query all leading and trailing symbols ' ', '\t' and ';' 81 | return q.replace(/^[\s;]*|[\s;]*$/g, ''); 82 | } 83 | 84 | module.exports = {concat}; 85 | 86 | /** 87 | * @typedef helpers.QueryFormat 88 | * @description 89 | * A simple structure of parameters to be passed into method {@link formatting.format as.format} exactly as they are, 90 | * used by {@link helpers.concat}. 91 | * 92 | * @property {string|value|object} query 93 | * A query string or a value/object that implements $[Custom Type Formatting], to be formatted according to `values`. 94 | * 95 | * @property {array|object|value} [values] 96 | * Query-formatting values. 97 | * 98 | * @property {object} [options] 99 | * Query-formatting options, as supported by method {@link formatting.format as.format}. 100 | * 101 | * @see 102 | * {@link formatting.format as.format} 103 | */ 104 | -------------------------------------------------------------------------------- /lib/helpers/methods/index.js: -------------------------------------------------------------------------------- 1 | const {concat} = require('./concat'); 2 | const {insert} = require('./insert'); 3 | const {update} = require('./update'); 4 | const {values} = require('./values'); 5 | const {sets} = require('./sets'); 6 | 7 | module.exports = { 8 | concat, 9 | insert, 10 | update, 11 | values, 12 | sets 13 | }; 14 | -------------------------------------------------------------------------------- /lib/helpers/methods/insert.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {TableName} = require('../table-name'); 11 | const {ColumnSet} = require('../column-set'); 12 | 13 | const npm = { 14 | formatting: require('../../formatting'), 15 | utils: require('../../utils') 16 | }; 17 | 18 | /** 19 | * @method helpers.insert 20 | * @description 21 | * Generates an `INSERT` query for either one object or an array of objects. 22 | * 23 | * @param {object|object[]} data 24 | * An insert object with properties for insert values, or an array of such objects. 25 | * 26 | * When `data` is not a non-null object and not an array, it will throw {@link external:TypeError TypeError} = `Invalid parameter 'data' specified.` 27 | * 28 | * When `data` is an empty array, it will throw {@link external:TypeError TypeError} = `Cannot generate an INSERT from an empty array.` 29 | * 30 | * When `data` is an array that contains a non-object value, the method will throw {@link external:Error Error} = 31 | * `Invalid insert object at index N.` 32 | * 33 | * @param {array|helpers.Column|helpers.ColumnSet} [columns] 34 | * Set of columns to be inserted. 35 | * 36 | * It is optional when `data` is a single object, and required when `data` is an array of objects. If not specified for an array 37 | * of objects, the method will throw {@link external:TypeError TypeError} = `Parameter 'columns' is required when inserting multiple records.` 38 | * 39 | * When `columns` is not a {@link helpers.ColumnSet ColumnSet} object, a temporary {@link helpers.ColumnSet ColumnSet} 40 | * is created - from the value of `columns` (if it was specified), or from the value of `data` (if it is not an array). 41 | * 42 | * When the final {@link helpers.ColumnSet ColumnSet} is empty (no columns in it), the method will throw 43 | * {@link external:Error Error} = `Cannot generate an INSERT without any columns.` 44 | * 45 | * @param {helpers.TableName|Table|string} [table] 46 | * Destination table. 47 | * 48 | * It is normally a required parameter. But when `columns` is passed in as a {@link helpers.ColumnSet ColumnSet} object 49 | * with `table` set in it, that will be used when this parameter isn't specified. When neither is available, the method 50 | * will throw {@link external:Error Error} = `Table name is unknown.` 51 | * 52 | * @returns {string} 53 | * An `INSERT` query string. 54 | * 55 | * @see 56 | * {@link helpers.Column Column}, 57 | * {@link helpers.ColumnSet ColumnSet}, 58 | * {@link helpers.TableName TableName} 59 | * 60 | * @example 61 | * 62 | * const pgp = require('pg-promise')({ 63 | * capSQL: true // if you want all generated SQL capitalized 64 | * }); 65 | * const {insert} = pgp.helpers; 66 | * 67 | * const dataSingle = {val: 123, msg: 'hello'}; 68 | * const dataMulti = [{val: 123, msg: 'hello'}, {val: 456, msg: 'world!'}]; 69 | * 70 | * // Column details can be taken from the data object: 71 | * 72 | * insert(dataSingle, null, 'my-table'); 73 | * //=> INSERT INTO "my-table"("val","msg") VALUES(123,'hello') 74 | * 75 | * @example 76 | * 77 | * // Column details are required for a multi-row `INSERT`: 78 | * const {insert} = pgp.helpers; 79 | * 80 | * insert(dataMulti, ['val', 'msg'], 'my-table'); 81 | * //=> INSERT INTO "my-table"("val","msg") VALUES(123,'hello'),(456,'world!') 82 | * 83 | * @example 84 | * 85 | * // Column details from a reusable ColumnSet (recommended for performance): 86 | * const {ColumnSet, insert} = pgp.helpers; 87 | * 88 | * const cs = new ColumnSet(['val', 'msg'], {table: 'my-table'}); 89 | * 90 | * insert(dataMulti, cs); 91 | * //=> INSERT INTO "my-table"("val","msg") VALUES(123,'hello'),(456,'world!') 92 | * 93 | */ 94 | function insert(data, columns, table, capSQL) { 95 | 96 | if (!data || typeof data !== 'object') { 97 | throw new TypeError('Invalid parameter \'data\' specified.'); 98 | } 99 | 100 | const isArray = Array.isArray(data); 101 | 102 | if (isArray && !data.length) { 103 | throw new TypeError('Cannot generate an INSERT from an empty array.'); 104 | } 105 | 106 | if (columns instanceof ColumnSet) { 107 | if (npm.utils.isNull(table)) { 108 | table = columns.table; 109 | } 110 | } else { 111 | if (isArray && npm.utils.isNull(columns)) { 112 | throw new TypeError('Parameter \'columns\' is required when inserting multiple records.'); 113 | } 114 | columns = new ColumnSet(columns || data); 115 | } 116 | 117 | if (!columns.columns.length) { 118 | throw new Error('Cannot generate an INSERT without any columns.'); 119 | } 120 | 121 | if (!table) { 122 | throw new Error('Table name is unknown.'); 123 | } 124 | 125 | if (!(table instanceof TableName)) { 126 | table = new TableName(table); 127 | } 128 | 129 | let query = capSQL ? sql.capCase : sql.lowCase; 130 | const fmOptions = {capSQL}; 131 | 132 | const format = npm.formatting.as.format; 133 | query = format(query, [table.name, columns.names], fmOptions); 134 | 135 | if (isArray) { 136 | return query + data.map((d, index) => { 137 | if (!d || typeof d !== 'object') { 138 | throw new Error(`Invalid insert object at index ${index}.`); 139 | } 140 | return '(' + format(columns.variables, columns.prepare(d), fmOptions) + ')'; 141 | }).join(); 142 | } 143 | return query + '(' + format(columns.variables, columns.prepare(data), fmOptions) + ')'; 144 | } 145 | 146 | const sql = { 147 | lowCase: 'insert into $1^($2^) values', 148 | capCase: 'INSERT INTO $1^($2^) VALUES' 149 | }; 150 | 151 | module.exports = {insert}; 152 | -------------------------------------------------------------------------------- /lib/helpers/methods/sets.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {ColumnSet} = require('../column-set'); 11 | 12 | const npm = { 13 | format: require('../../formatting').as.format, 14 | utils: require('../../utils') 15 | }; 16 | 17 | /** 18 | * @method helpers.sets 19 | * @description 20 | * Generates a string of comma-separated value-set statements from a single object: `col1=val1, col2=val2, ...`, 21 | * to be used as part of a query. 22 | * 23 | * Since it is to be used as part of `UPDATE` queries, {@link helpers.Column Column} properties `cnd` and `skip` apply. 24 | * 25 | * @param {object} data 26 | * A simple, non-null and non-array source object. 27 | * 28 | * If it is anything else, the method will throw {@link external:TypeError TypeError} = `Invalid parameter 'data' specified.` 29 | * 30 | * @param {array|helpers.Column|helpers.ColumnSet} [columns] 31 | * Columns for which to set values. 32 | * 33 | * When not specified, properties of the `data` object are used. 34 | * 35 | * When no effective columns are found, an empty string is returned. 36 | * 37 | * @returns {string} 38 | * - comma-separated value-set statements for the `data` object 39 | * - an empty string, if no effective columns found 40 | * 41 | * @see 42 | * {@link helpers.Column Column}, 43 | * {@link helpers.ColumnSet ColumnSet} 44 | * 45 | * @example 46 | * 47 | * const pgp = require('pg-promise')(); 48 | * 49 | * const data = {id: 1, val: 123, msg: 'hello'}; 50 | * 51 | * // Properties can be pulled automatically from the object: 52 | * 53 | * pgp.helpers.sets(data); 54 | * //=> "id"=1,"val"=123,"msg"='hello' 55 | * 56 | * @example 57 | * 58 | * // Column details from a reusable ColumnSet (recommended for performance); 59 | * // NOTE: Conditional columns (start with '?') are skipped: 60 | * const {ColumnSet, sets} = pgp.helpers; 61 | * 62 | * const cs = new ColumnSet(['?id','val', 'msg']); 63 | * 64 | * sets(data, cs); 65 | * //=> "val"=123,"msg"='hello' 66 | * 67 | */ 68 | function sets(data, columns, capSQL) { 69 | 70 | if (!data || typeof data !== 'object' || Array.isArray(data)) { 71 | throw new TypeError('Invalid parameter \'data\' specified.'); 72 | } 73 | 74 | if (!(columns instanceof ColumnSet)) { 75 | columns = new ColumnSet(columns || data); 76 | } 77 | 78 | return npm.format(columns.assign({source: data}), columns.prepare(data), {capSQL}); 79 | } 80 | 81 | module.exports = {sets}; 82 | -------------------------------------------------------------------------------- /lib/helpers/methods/values.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {ColumnSet} = require('../column-set'); 11 | 12 | const npm = { 13 | formatting: require('../../formatting'), 14 | utils: require('../../utils') 15 | }; 16 | 17 | /** 18 | * @method helpers.values 19 | * @description 20 | * Generates a string of comma-separated value groups from either one object or an array of objects, 21 | * to be used as part of a query: 22 | * 23 | * - from a single object: `(val_1, val_2, ...)` 24 | * - from an array of objects: `(val_11, val_12, ...), (val_21, val_22, ...)` 25 | * 26 | * @param {object|object[]} data 27 | * A source object with properties as values, or an array of such objects. 28 | * 29 | * If it is anything else, the method will throw {@link external:TypeError TypeError} = `Invalid parameter 'data' specified.` 30 | * 31 | * When `data` is an array that contains a non-object value, the method will throw {@link external:Error Error} = 32 | * `Invalid object at index N.` 33 | * 34 | * When `data` is an empty array, an empty string is returned. 35 | * 36 | * @param {array|helpers.Column|helpers.ColumnSet} [columns] 37 | * Columns for which to return values. 38 | * 39 | * It is optional when `data` is a single object, and required when `data` is an array of objects. If not specified for an array 40 | * of objects, the method will throw {@link external:TypeError TypeError} = `Parameter 'columns' is required when generating multi-row values.` 41 | * 42 | * When the final {@link helpers.ColumnSet ColumnSet} is empty (no columns in it), the method will throw 43 | * {@link external:Error Error} = `Cannot generate values without any columns.` 44 | * 45 | * @returns {string} 46 | * - comma-separated value groups, according to `data` 47 | * - an empty string, if `data` is an empty array 48 | * 49 | * @see 50 | * {@link helpers.Column Column}, 51 | * {@link helpers.ColumnSet ColumnSet} 52 | * 53 | * @example 54 | * 55 | * const pgp = require('pg-promise')(); 56 | * 57 | * const dataSingle = {val: 123, msg: 'hello'}; 58 | * const dataMulti = [{val: 123, msg: 'hello'}, {val: 456, msg: 'world!'}]; 59 | * 60 | * // Properties can be pulled automatically from a single object: 61 | * 62 | * pgp.helpers.values(dataSingle); 63 | * //=> (123,'hello') 64 | * 65 | * @example 66 | * 67 | * // Column details are required when using an array of objects: 68 | * 69 | * pgp.helpers.values(dataMulti, ['val', 'msg']); 70 | * //=> (123,'hello'),(456,'world!') 71 | * 72 | * @example 73 | * 74 | * // Column details from a reusable ColumnSet (recommended for performance): 75 | * const {ColumnSet, values} = pgp.helpers; 76 | * 77 | * const cs = new ColumnSet(['val', 'msg']); 78 | * 79 | * values(dataMulti, cs); 80 | * //=> (123,'hello'),(456,'world!') 81 | * 82 | */ 83 | function values(data, columns, capSQL) { 84 | 85 | if (!data || typeof data !== 'object') { 86 | throw new TypeError('Invalid parameter \'data\' specified.'); 87 | } 88 | 89 | const isArray = Array.isArray(data); 90 | 91 | if (!(columns instanceof ColumnSet)) { 92 | if (isArray && npm.utils.isNull(columns)) { 93 | throw new TypeError('Parameter \'columns\' is required when generating multi-row values.'); 94 | } 95 | columns = new ColumnSet(columns || data); 96 | } 97 | 98 | if (!columns.columns.length) { 99 | throw new Error('Cannot generate values without any columns.'); 100 | } 101 | 102 | const format = npm.formatting.as.format, 103 | fmOptions = {capSQL}; 104 | 105 | if (isArray) { 106 | return data.map((d, index) => { 107 | if (!d || typeof d !== 'object') { 108 | throw new Error(`Invalid object at index ${index}.`); 109 | } 110 | return '(' + format(columns.variables, columns.prepare(d), fmOptions) + ')'; 111 | }).join(); 112 | } 113 | return '(' + format(columns.variables, columns.prepare(data), fmOptions) + ')'; 114 | } 115 | 116 | module.exports = {values}; 117 | -------------------------------------------------------------------------------- /lib/helpers/table-name.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {assert} = require('../assert'); 11 | 12 | const npm = { 13 | utils: require('../utils'), 14 | format: require('../formatting').as // formatting namespace 15 | }; 16 | 17 | /** 18 | * @class helpers.TableName 19 | * @description 20 | * Represents a full table name that can be injected into queries directly. 21 | * 22 | * This is a read-only type that can be used wherever parameter `table` is supported. 23 | * 24 | * It supports $[Custom Type Formatting], which means you can use the type directly as a formatting 25 | * parameter, without specifying any escaping. 26 | * 27 | * Filter `:alias` is an alternative approach to splitting an SQL name into multiple ones. 28 | * 29 | * @param {string|Table} table 30 | * Table name details, depending on the type: 31 | * 32 | * - table name, if `table` is a string 33 | * - {@link Table} object 34 | * 35 | * @property {string} name 36 | * Formatted/escaped full table name, combining `schema` + `table`. 37 | * 38 | * @property {string} table 39 | * Table name. 40 | * 41 | * @property {string} schema 42 | * Database schema name. 43 | * 44 | * It is `undefined` when no valid schema was specified. 45 | * 46 | * @returns {helpers.TableName} 47 | * 48 | * @see 49 | * {@link helpers._TN _TN}, 50 | * {@link helpers.TableName#toPostgres toPostgres} 51 | * 52 | * @example 53 | * 54 | * const table = new pgp.helpers.TableName({table: 'my-table', schema: 'my-schema'}); 55 | * console.log(table); 56 | * //=> "my-schema"."my-table" 57 | * 58 | * // Formatting the type directly: 59 | * pgp.as.format('SELECT * FROM $1', table); 60 | * //=> SELECT * FROM "my-schema"."my-table" 61 | * 62 | */ 63 | class TableName { 64 | 65 | constructor(table) { 66 | if (typeof table === 'string') { 67 | this.table = table; 68 | } else { 69 | const config = assert(table, ['table', 'schema']); 70 | this.table = config.table; 71 | if (npm.utils.isText(config.schema)) { 72 | this.schema = config.schema; 73 | } 74 | } 75 | if (!npm.utils.isText(this.table)) { 76 | throw new TypeError('Table name must be a non-empty text string.'); 77 | } 78 | this.name = npm.format.name(this.table); 79 | if (this.schema) { 80 | this.name = npm.format.name(this.schema) + '.' + this.name; 81 | } 82 | Object.freeze(this); 83 | } 84 | } 85 | 86 | /** 87 | * @method helpers.TableName#toPostgres 88 | * @description 89 | * $[Custom Type Formatting], based on $[Symbolic CTF], i.e. the actual method is available only via {@link external:Symbol Symbol}: 90 | * 91 | * ```js 92 | * const {toPostgres} = pgp.as.ctf; // Custom Type Formatting symbols namespace 93 | * const fullName = tn[toPostgres]; // tn = an object of type TableName 94 | * ``` 95 | * 96 | * This is a raw formatting type (`rawType = true`), i.e. when used as a query-formatting parameter, type `TableName` 97 | * injects full table name as raw text. 98 | * 99 | * @param {helpers.TableName} [self] 100 | * Optional self-reference, for ES6 arrow functions. 101 | * 102 | * @returns {string} 103 | * Escaped full table name that includes optional schema name, if specified. 104 | */ 105 | TableName.prototype[npm.format.ctf.toPostgres] = function (self) { 106 | self = this instanceof TableName && this || self; 107 | return self.name; 108 | }; 109 | 110 | TableName.prototype[npm.format.ctf.rawType] = true; // use as pre-formatted 111 | 112 | /** 113 | * @method helpers.TableName#toString 114 | * @description 115 | * Creates a well-formatted string that represents the object. 116 | * 117 | * It is called automatically when writing the object into the console. 118 | * 119 | * @returns {string} 120 | */ 121 | TableName.prototype.toString = function () { 122 | return this.name; 123 | }; 124 | 125 | npm.utils.addInspection(TableName, function () { 126 | return this.toString(); 127 | }); 128 | 129 | /** 130 | * @interface Table 131 | * @description 132 | * Structure for any table name/path. 133 | * 134 | * Function {@link helpers._TN _TN} can help you construct it from a string. 135 | * 136 | * @property {string} [schema] - schema name, if specified 137 | * @property {string} table - table name 138 | * 139 | * @see {@link helpers.TableName TableName}, {@link helpers._TN _TN} 140 | */ 141 | 142 | /** 143 | * @external TemplateStringsArray 144 | * @see https://microsoft.github.io/PowerBI-JavaScript/interfaces/_node_modules_typedoc_node_modules_typescript_lib_lib_es5_d_.templatestringsarray.html 145 | */ 146 | 147 | /** 148 | * @function helpers._TN 149 | * @description 150 | * Table-Name helper function, to convert any `"schema.table"` string 151 | * into `{schema, table}` object. It also works as a template-tag function. 152 | * 153 | * @param {string|TemplateStringsArray} path 154 | * Table-name path, as a simple string or a template string (with parameters). 155 | * 156 | * @example 157 | * const {ColumnSet, _TN} = pgp.helpers; 158 | * 159 | * // Using as a regular function: 160 | * const cs1 = new ColumnSet(['id', 'name'], {table: _TN('schema.table')}); 161 | * 162 | * // Using as a template-tag function: 163 | * const schema = 'schema'; 164 | * const cs2 = new ColumnSet(['id', 'name'], {table: _TN`${schema}.table`}); 165 | * 166 | * @returns {Table} 167 | * 168 | * @see {@link helpers.TableName TableName}, {@link external:TemplateStringsArray TemplateStringsArray} 169 | */ 170 | function _TN(path, ...args) { 171 | if (Array.isArray(path)) { 172 | path = path.reduce((a, c, i) => a + c + (args[i] ?? ''), ''); 173 | } // else 'path' is a string 174 | const [schema, table] = path.split('.'); 175 | if (table === undefined) { 176 | return {table: schema}; 177 | } 178 | return schema ? {schema, table} : {table}; 179 | } 180 | 181 | module.exports = {TableName, _TN}; 182 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | /* eslint no-var: off */ 11 | var v = process.versions.node.split('.'), 12 | highVer = +v[0]; 13 | 14 | // istanbul ignore next 15 | if (highVer < 16) { 16 | 17 | // From pg-promise v11.15.0, the oldest supported Node.js is v16.0.0 18 | 19 | // Node.js v14.x was supported up to pg-promise v11.14.0 20 | // Node.js v12.x was supported up to pg-promise v10.15.4 21 | // Node.js v8.x was supported up to pg-promise v10.14.2 22 | // Node.js v7.6.0 was supported up to pg-promise v10.3.5 23 | // Node.js v4.5.0 was supported up to pg-promise v8.7.5 24 | // Node.js v0.10 was supported up to pg-promise v5.5.8 25 | 26 | throw new Error('Minimum Node.js version supported by pg-promise is 16.0.0'); 27 | } 28 | 29 | module.exports = require('./main'); 30 | -------------------------------------------------------------------------------- /lib/inner-state.js: -------------------------------------------------------------------------------- 1 | const {addReadProp} = require('./utils'); 2 | 3 | /** 4 | * @private 5 | * @class InnerState 6 | * @description 7 | * Implements support for private/inner state object inside the class, 8 | * which can be accessed by a derived class via hidden read-only property _inner. 9 | */ 10 | class InnerState { 11 | 12 | constructor(initialState) { 13 | addReadProp(this, '_inner', {}, true); 14 | if (initialState && typeof initialState === 'object') { 15 | this.extendState(initialState); 16 | } 17 | } 18 | 19 | /** 20 | * Extends or overrides inner state with the specified properties. 21 | * 22 | * Only own properties are used, i.e. inherited ones are skipped. 23 | */ 24 | extendState(state) { 25 | for (const a in state) { 26 | // istanbul ignore else 27 | if (Object.prototype.hasOwnProperty.call(state, a)) { 28 | this._inner[a] = state[a]; 29 | } 30 | } 31 | } 32 | } 33 | 34 | /** 35 | * @member InnerState#_inner 36 | * Private/Inner object state. 37 | */ 38 | 39 | module.exports = {InnerState}; 40 | -------------------------------------------------------------------------------- /lib/patterns.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | /* 11 | The most important regular expressions and data as used by the library, 12 | isolated here to help with possible edge cases during integration. 13 | */ 14 | 15 | module.exports = { 16 | // Searches for all Named Parameters, supporting any of the following syntax: 17 | // ${propName}, $(propName), $[propName], $/propName/, lt;propName> 18 | // Nested property names are also supported: ${propName.abc} 19 | namedParameters: /\$(?:({)|(\()|(<)|(\[)|(\/))\s*[a-zA-Z0-9$_.]+(\^|~|#|:raw|:alias|:name|:json|:csv|:list|:value)?\s*(?:(?=\2)(?=\3)(?=\4)(?=\5)}|(?=\1)(?=\3)(?=\4)(?=\5)\)|(?=\1)(?=\2)(?=\4)(?=\5)>|(?=\1)(?=\2)(?=\3)(?=\5)]|(?=\1)(?=\2)(?=\3)(?=\4)\/)/g, 20 | 21 | // Searches for all variables $1, $2, ...$100000, and while it will find greater than $100000 22 | // variables, the formatting engine is expected to throw an error for those. 23 | multipleValues: /\$([1-9][0-9]{0,16}(?![0-9])(\^|~|#|:raw|:alias|:name|:json|:csv|:list|:value)?)/g, 24 | 25 | // Searches for all occurrences of variable $1 26 | singleValue: /\$1(?![0-9])(\^|~|#|:raw|:alias|:name|:json|:csv|:list|:value)?/g, 27 | 28 | // Matches a valid column name for the Column type parser, according to the following rules: 29 | // - can contain: any combination of a-z, A-Z, 0-9, $ or _ 30 | // - can contain ? at the start 31 | // - can contain one of the supported filters/modifiers 32 | validColumn: /\??[a-zA-Z0-9$_]+(\^|~|#|:raw|:alias|:name|:json|:csv|:list|:value)?/, 33 | 34 | // Matches a valid open-name JavaScript variable, according to the following rules: 35 | // - can contain: any combination of a-z, A-Z, 0-9, $ or _ 36 | validVariable: /[a-zA-Z0-9$_]+/, 37 | 38 | // Matches a valid modifier in a column/property: 39 | hasValidModifier: /\^|~|#|:raw|:alias|:name|:json|:csv|:list|:value/, 40 | 41 | // List of all supported formatting modifiers: 42 | validModifiers: ['^', '~', '#', ':raw', ':alias', ':name', ':json', ':csv', ':list', ':value'] 43 | }; 44 | -------------------------------------------------------------------------------- /lib/promise-adapter.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {assert} = require('./assert'); 11 | 12 | /** 13 | * @class PromiseAdapter 14 | * @summary Adapter for the primary promise operations. 15 | * @description 16 | * Provides compatibility with promise libraries that cannot be recognized automatically, 17 | * via functions that implement the primary operations with promises: 18 | * 19 | * - construct a new promise with a callback function 20 | * - resolve a promise with some result data 21 | * - reject a promise with a reason 22 | * - resolve an array of promises 23 | * 24 | * The type is available from the library's root: `pgp.PromiseAdapter`. 25 | * 26 | * @param {object} api 27 | * Promise API configuration object. 28 | * 29 | * Passing in anything other than an object will throw {@link external:TypeError TypeError} = `Adapter requires an api configuration object.` 30 | * 31 | * @param {function} api.create 32 | * A function that takes a callback parameter and returns a new promise object. 33 | * The callback parameter is expected to be `function(resolve, reject)`. 34 | * 35 | * Passing in anything other than a function will throw {@link external:TypeError TypeError} = `Function 'create' must be specified.` 36 | * 37 | * @param {function} api.resolve 38 | * A function that takes an optional data parameter and resolves a promise with it. 39 | * 40 | * Passing in anything other than a function will throw {@link external:TypeError TypeError} = `Function 'resolve' must be specified.` 41 | * 42 | * @param {function} api.reject 43 | * A function that takes an optional error parameter and rejects a promise with it. 44 | * 45 | * Passing in anything other than a function will throw {@link external:TypeError TypeError} = `Function 'reject' must be specified.` 46 | * 47 | * @param {function} api.all 48 | * A function that resolves an array of promises. 49 | * 50 | * Passing in anything other than a function will throw {@link external:TypeError TypeError} = `Function 'all' must be specified.` 51 | * 52 | * @returns {PromiseAdapter} 53 | */ 54 | class PromiseAdapter { 55 | constructor(api) { 56 | 57 | if (!api || typeof api !== 'object') { 58 | throw new TypeError('Adapter requires an api configuration object.'); 59 | } 60 | 61 | api = assert(api, ['create', 'resolve', 'reject', 'all']); 62 | 63 | this.create = api.create; 64 | this.resolve = api.resolve; 65 | this.reject = api.reject; 66 | this.all = api.all; 67 | 68 | if (typeof this.create !== 'function') { 69 | throw new TypeError('Function \'create\' must be specified.'); 70 | } 71 | 72 | if (typeof this.resolve !== 'function') { 73 | throw new TypeError('Function \'resolve\' must be specified.'); 74 | } 75 | 76 | if (typeof this.reject !== 'function') { 77 | throw new TypeError('Function \'reject\' must be specified.'); 78 | } 79 | 80 | if (typeof this.all !== 'function') { 81 | throw new TypeError('Function \'all\' must be specified.'); 82 | } 83 | } 84 | } 85 | 86 | module.exports = {PromiseAdapter}; 87 | -------------------------------------------------------------------------------- /lib/promise-parser.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {PromiseAdapter} = require('./promise-adapter'); 11 | 12 | ////////////////////////////////////////// 13 | // Parses and validates a promise library; 14 | function parse(pl) { 15 | 16 | let promise; 17 | if (pl instanceof PromiseAdapter) { 18 | promise = function (func) { 19 | return pl.create(func); 20 | }; 21 | promise.resolve = pl.resolve; 22 | promise.reject = pl.reject; 23 | promise.all = pl.all; 24 | return promise; 25 | } 26 | const t = typeof pl; 27 | if (t === 'function' || t === 'object') { 28 | const Root = typeof pl.Promise === 'function' ? pl.Promise : pl; 29 | promise = function (func) { 30 | return new Root(func); 31 | }; 32 | promise.resolve = Root.resolve; 33 | promise.reject = Root.reject; 34 | promise.all = Root.all; 35 | if (typeof promise.resolve === 'function' && 36 | typeof promise.reject === 'function' && 37 | typeof promise.all === 'function') { 38 | return promise; 39 | } 40 | } 41 | 42 | throw new TypeError('Invalid promise library specified.'); 43 | } 44 | 45 | function parsePromise(promiseLib) { 46 | const result = {promiseLib}; 47 | if (promiseLib) { 48 | result.promise = parse(promiseLib); 49 | } else { 50 | result.promise = parse(Promise); 51 | result.promiseLib = Promise; 52 | } 53 | return result; 54 | } 55 | 56 | module.exports = {parsePromise}; 57 | -------------------------------------------------------------------------------- /lib/query-result.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | /** 11 | * @enum {number} 12 | * @alias queryResult 13 | * @readonly 14 | * @description 15 | * **Query Result Mask** 16 | * 17 | * Binary mask that represents the number of rows expected from a query method, 18 | * used by generic {@link Database#query query} method, plus {@link Database#func func}. 19 | * 20 | * The mask is always the last optional parameter, which defaults to `queryResult.any`. 21 | * 22 | * Any combination of flags is supported, except for `one + many`. 23 | * 24 | * The type is available from the library's root: `pgp.queryResult`. 25 | * 26 | * @see {@link Database#query Database.query}, {@link Database#func Database.func} 27 | */ 28 | const queryResult = { 29 | /** Single row is expected, to be resolved as a single row-object. */ 30 | one: 1, 31 | /** One or more rows expected, to be resolved as an array, with at least 1 row-object. */ 32 | many: 2, 33 | /** Expecting no rows, to be resolved with `null`. */ 34 | none: 4, 35 | /** `many|none` - any result is expected, to be resolved with an array of rows-objects. */ 36 | any: 6 37 | }; 38 | 39 | module.exports = {queryResult}; 40 | -------------------------------------------------------------------------------- /lib/special-query.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const specialQueryType = { 11 | result: 0, 12 | multiResult: 1, 13 | stream: 2 14 | }; 15 | 16 | class SpecialQuery { 17 | constructor(type) { 18 | this.isResult = type === specialQueryType.result; // type used implicitly 19 | this.isStream = type === specialQueryType.stream; 20 | this.isMultiResult = type === specialQueryType.multiResult; 21 | } 22 | } 23 | 24 | const cache = { 25 | resultQuery: new SpecialQuery(specialQueryType.result), 26 | multiResultQuery: new SpecialQuery(specialQueryType.multiResult), 27 | streamQuery: new SpecialQuery(specialQueryType.stream) 28 | }; 29 | 30 | module.exports = Object.assign({SpecialQuery}, cache); 31 | -------------------------------------------------------------------------------- /lib/stream.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {Events} = require('./events'); 11 | 12 | const npm = { 13 | utils: require('./utils'), 14 | text: require('./text') 15 | }; 16 | 17 | //////////////////////////////////////////// 18 | // Streams query data into any destination, 19 | // with the help of pg-query-stream library. 20 | function $stream(ctx, qs, initCB, config) { 21 | 22 | const $p = config.promise; 23 | 24 | // istanbul ignore next: 25 | // we do not provide code coverage for the Native Bindings specifics 26 | if (ctx.options.pgNative) { 27 | return $p.reject(new Error(npm.text.nativeStreaming)); 28 | } 29 | // Stream class was renamed again, see the following issue: 30 | // https://github.com/brianc/node-postgres/issues/2412 31 | if (!qs || !qs.constructor || qs.constructor.name !== 'QueryStream') { 32 | // invalid or missing stream object; 33 | return $p.reject(new TypeError(npm.text.invalidStream)); 34 | } 35 | if (qs._reading || qs._closed) { 36 | // stream object is in the wrong state; 37 | return $p.reject(new Error(npm.text.invalidStreamState)); 38 | } 39 | if (typeof initCB !== 'function') { 40 | // parameter `initCB` must be passed as the initialization callback; 41 | return $p.reject(new TypeError(npm.text.invalidStreamCB)); 42 | } 43 | 44 | let error = Events.query(ctx.options, getContext()); 45 | 46 | if (error) { 47 | error = getError(error); 48 | Events.error(ctx.options, error, getContext()); 49 | return $p.reject(error); 50 | } 51 | 52 | const stream = ctx.db.client.query(qs); 53 | 54 | stream.on('data', onData); 55 | stream.on('error', onError); 56 | stream.on('end', onEnd); 57 | 58 | try { 59 | initCB.call(this, stream); // the stream must be initialized during the call; 60 | } catch (e) { 61 | release(); 62 | error = getError(e); 63 | Events.error(ctx.options, error, getContext()); 64 | return $p.reject(error); 65 | } 66 | 67 | const start = Date.now(); 68 | let resolve, reject, nRows = 0; 69 | 70 | function onData(data) { 71 | nRows++; 72 | error = Events.receive(ctx.options, [data], undefined, getContext()); 73 | if (error) { 74 | onError(error); 75 | } 76 | } 77 | 78 | function onError(e) { 79 | release(); 80 | stream.destroy(); 81 | e = getError(e); 82 | Events.error(ctx.options, e, getContext()); 83 | reject(e); 84 | } 85 | 86 | function onEnd() { 87 | release(); 88 | resolve({ 89 | processed: nRows, // total number of rows processed; 90 | duration: Date.now() - start // duration, in milliseconds; 91 | }); 92 | } 93 | 94 | function release() { 95 | stream.removeListener('data', onData); 96 | stream.removeListener('error', onError); 97 | stream.removeListener('end', onEnd); 98 | } 99 | 100 | function getError(e) { 101 | return e instanceof npm.utils.InternalError ? e.error : e; 102 | } 103 | 104 | function getContext() { 105 | let client; 106 | if (ctx.db) { 107 | client = ctx.db.client; 108 | } else { 109 | error = new Error(npm.text.looseQuery); 110 | } 111 | return { 112 | client, 113 | dc: ctx.dc, 114 | query: qs.cursor.text, 115 | params: qs.cursor.values, 116 | ctx: ctx.ctx 117 | }; 118 | } 119 | 120 | return $p((res, rej) => { 121 | resolve = res; 122 | reject = rej; 123 | }); 124 | 125 | } 126 | 127 | module.exports = $stream; 128 | -------------------------------------------------------------------------------- /lib/text.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | /* All error messages used in the module */ 11 | 12 | const streamVersion = require('../package.json') 13 | .devDependencies['pg-query-stream']; 14 | 15 | module.exports = { 16 | nativeError: 'Failed to initialize Native Bindings.', 17 | 18 | /* Database errors */ 19 | queryDisconnected: 'Cannot execute a query on a disconnected client.', 20 | invalidQuery: 'Invalid query format.', 21 | invalidFunction: 'Invalid function name.', 22 | invalidProc: 'Invalid procedure name.', 23 | invalidMask: 'Invalid Query Result Mask specified.', 24 | looseQuery: 'Querying against a released or lost connection.', 25 | 26 | /* result errors */ 27 | notEmpty: 'No return data was expected.', 28 | noData: 'No data returned from the query.', 29 | multiple: 'Multiple rows were not expected.', 30 | 31 | /* streaming support */ 32 | nativeStreaming: 'Streaming doesn\'t work with Native Bindings.', 33 | invalidStream: `Invalid or missing stream object: pg-query-stream >= v${streamVersion} was expected`, 34 | invalidStreamState: 'Invalid stream state.', 35 | invalidStreamCB: 'Invalid or missing stream initialization callback.', 36 | 37 | /* connection errors */ 38 | poolDestroyed: 'Connection pool of the database object has been destroyed.', 39 | clientEnd: 'Abnormal client.end() call, due to invalid code or failed server connection.' 40 | }; 41 | -------------------------------------------------------------------------------- /lib/tx-mode.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {InnerState} = require('./inner-state'); 11 | const {addInspection} = require('./utils'); 12 | const {assert} = require('./assert'); 13 | 14 | /** 15 | * @enum {number} 16 | * @alias txMode.isolationLevel 17 | * @readonly 18 | * @summary Transaction Isolation Level. 19 | * @description 20 | * The type is available from the {@link txMode} namespace. 21 | * 22 | * @see $[Transaction Isolation] 23 | */ 24 | const isolationLevel = { 25 | /** Isolation level not specified. */ 26 | none: 0, 27 | 28 | /** ISOLATION LEVEL SERIALIZABLE */ 29 | serializable: 1, 30 | 31 | /** ISOLATION LEVEL REPEATABLE READ */ 32 | repeatableRead: 2, 33 | 34 | /** ISOLATION LEVEL READ COMMITTED */ 35 | readCommitted: 3 36 | 37 | // From the official documentation: http://www.postgresql.org/docs/9.5/static/sql-set-transaction.html 38 | // The SQL standard defines one additional level, READ UNCOMMITTED. In PostgreSQL READ UNCOMMITTED is treated as READ COMMITTED. 39 | // => skipping `READ UNCOMMITTED`. 40 | }; 41 | 42 | /** 43 | * @class txMode.TransactionMode 44 | * @description 45 | * Constructs a complete transaction-opening `BEGIN` command, from these options: 46 | * - isolation level 47 | * - access mode 48 | * - deferrable mode 49 | * 50 | * The type is available from the {@link txMode} namespace. 51 | * 52 | * @param {} [options] 53 | * Transaction Mode options. 54 | * 55 | * @param {txMode.isolationLevel} [options.tiLevel] 56 | * Transaction Isolation Level. 57 | * 58 | * @param {boolean} [options.readOnly] 59 | * Sets transaction access mode based on the read-only flag: 60 | * - `undefined` - access mode not specified (default) 61 | * - `true` - access mode is set to `READ ONLY` 62 | * - `false` - access mode is set to `READ WRITE` 63 | * 64 | * @param {boolean} [options.deferrable] 65 | * Sets transaction deferrable mode based on the boolean value: 66 | * - `undefined` - deferrable mode not specified (default) 67 | * - `true` - mode is set to `DEFERRABLE` 68 | * - `false` - mode is set to `NOT DEFERRABLE` 69 | * 70 | * It is used only when `tiLevel`=`isolationLevel.serializable` 71 | * and `readOnly`=`true`, or else it is ignored. 72 | * 73 | * @returns {txMode.TransactionMode} 74 | * 75 | * @see $[BEGIN], {@link txMode.isolationLevel} 76 | * 77 | * @example 78 | * 79 | * const {TransactionMode, isolationLevel} = pgp.txMode; 80 | * 81 | * // Create a reusable transaction mode (serializable + read-only + deferrable): 82 | * const mode = new TransactionMode({ 83 | * tiLevel: isolationLevel.serializable, 84 | * readOnly: true, 85 | * deferrable: true 86 | * }); 87 | * 88 | * db.tx({mode}, t => { 89 | * return t.any('SELECT * FROM table'); 90 | * }) 91 | * .then(data => { 92 | * // success; 93 | * }) 94 | * .catch(error => { 95 | * // error 96 | * }); 97 | * 98 | * // Instead of the default BEGIN, such transaction will start with: 99 | * 100 | * // BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE 101 | * 102 | */ 103 | class TransactionMode extends InnerState { 104 | 105 | constructor(options) { 106 | options = assert(options, ['tiLevel', 'deferrable', 'readOnly']); 107 | const {readOnly, deferrable} = options; 108 | let {tiLevel} = options; 109 | let level, accessMode, deferrableMode, begin = 'begin'; 110 | tiLevel = (tiLevel > 0) ? parseInt(tiLevel) : 0; 111 | 112 | if (tiLevel > 0 && tiLevel < 4) { 113 | const values = ['serializable', 'repeatable read', 'read committed']; 114 | level = 'isolation level ' + values[tiLevel - 1]; 115 | } 116 | if (readOnly) { 117 | accessMode = 'read only'; 118 | } else { 119 | if (readOnly !== undefined) { 120 | accessMode = 'read write'; 121 | } 122 | } 123 | // From the official documentation: http://www.postgresql.org/docs/9.5/static/sql-set-transaction.html 124 | // The DEFERRABLE transaction property has no effect unless the transaction is also SERIALIZABLE and READ ONLY 125 | if (tiLevel === isolationLevel.serializable && readOnly) { 126 | if (deferrable) { 127 | deferrableMode = 'deferrable'; 128 | } else { 129 | if (deferrable !== undefined) { 130 | deferrableMode = 'not deferrable'; 131 | } 132 | } 133 | } 134 | if (level) { 135 | begin += ' ' + level; 136 | } 137 | if (accessMode) { 138 | begin += ' ' + accessMode; 139 | } 140 | if (deferrableMode) { 141 | begin += ' ' + deferrableMode; 142 | } 143 | 144 | super({begin, capBegin: begin.toUpperCase()}); 145 | } 146 | 147 | /** 148 | * @method txMode.TransactionMode#begin 149 | * @description 150 | * Returns a complete BEGIN statement, according to all the parameters passed into the class. 151 | * 152 | * This method is primarily for internal use by the library. 153 | * 154 | * @param {boolean} [cap=false] 155 | * Indicates whether the returned SQL must be capitalized. 156 | * 157 | * @returns {string} 158 | */ 159 | begin(cap) { 160 | return cap ? this._inner.capBegin : this._inner.begin; 161 | } 162 | } 163 | 164 | addInspection(TransactionMode, function () { 165 | return this.begin(true); 166 | }); 167 | 168 | /** 169 | * @namespace txMode 170 | * @description 171 | * Transaction Mode namespace, available as `pgp.txMode`, before and after initializing the library. 172 | * 173 | * Extends the default `BEGIN` with Transaction Mode parameters: 174 | * - isolation level 175 | * - access mode 176 | * - deferrable mode 177 | * 178 | * @property {function} TransactionMode 179 | * {@link txMode.TransactionMode TransactionMode} class constructor. 180 | * 181 | * @property {txMode.isolationLevel} isolationLevel 182 | * Transaction Isolation Level enumerator 183 | * 184 | * @see $[BEGIN] 185 | */ 186 | module.exports = { 187 | isolationLevel, 188 | TransactionMode 189 | }; 190 | 191 | -------------------------------------------------------------------------------- /lib/types/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-present, Vitaly Tomilov 3 | * 4 | * See the LICENSE file at the top-level directory of this distribution 5 | * for licensing information. 6 | * 7 | * Removal or modification of this copyright notice is prohibited. 8 | */ 9 | 10 | const {ServerFormatting} = require('./server-formatting'); 11 | const {PreparedStatement} = require('./prepared-statement'); 12 | const {ParameterizedQuery} = require('./parameterized-query'); 13 | 14 | module.exports = { 15 | ServerFormatting, 16 | PreparedStatement, 17 | ParameterizedQuery 18 | }; 19 | -------------------------------------------------------------------------------- /lib/types/server-formatting.js: -------------------------------------------------------------------------------- 1 | const {InnerState} = require('../inner-state'); 2 | const {addInspection} = require('../utils'); 3 | const utils = require('../utils'); 4 | 5 | /** 6 | * @private 7 | * @class ServerFormatting 8 | */ 9 | class ServerFormatting extends InnerState { 10 | 11 | constructor(options) { 12 | const _inner = { 13 | options, 14 | changed: true, 15 | currentError: undefined, 16 | target: {} 17 | }; 18 | super(_inner); 19 | setValues.call(this, options.values); 20 | } 21 | 22 | get error() { 23 | return this._inner.currentError; 24 | } 25 | 26 | get text() { 27 | return this._inner.options.text; 28 | } 29 | 30 | set text(value) { 31 | const _i = this._inner; 32 | if (value !== _i.options.text) { 33 | _i.options.text = value; 34 | _i.changed = true; 35 | } 36 | } 37 | 38 | get binary() { 39 | return this._inner.options.binary; 40 | } 41 | 42 | set binary(value) { 43 | const _i = this._inner; 44 | if (value !== _i.options.binary) { 45 | _i.options.binary = value; 46 | _i.changed = true; 47 | } 48 | } 49 | 50 | get rowMode() { 51 | return this._inner.options.rowMode; 52 | } 53 | 54 | set rowMode(value) { 55 | const _i = this._inner; 56 | if (value !== _i.options.rowMode) { 57 | _i.options.rowMode = value; 58 | _i.changed = true; 59 | } 60 | } 61 | 62 | get values() { 63 | return this._inner.target.values; 64 | } 65 | 66 | set values(values) { 67 | setValues.call(this, values); 68 | } 69 | 70 | } 71 | 72 | /** 73 | * @member ServerFormatting#parse 74 | */ 75 | 76 | function setValues(v) { 77 | const target = this._inner.target; 78 | if (Array.isArray(v)) { 79 | if (v.length) { 80 | target.values = v; 81 | } else { 82 | delete target.values; 83 | } 84 | } else { 85 | if (utils.isNull(v)) { 86 | delete target.values; 87 | } else { 88 | target.values = [v]; 89 | } 90 | } 91 | } 92 | 93 | addInspection(ServerFormatting, function () { 94 | return this.toString(); 95 | }); 96 | 97 | module.exports = {ServerFormatting}; 98 | -------------------------------------------------------------------------------- /lib/utils/README.md: -------------------------------------------------------------------------------- 1 | ### `utils` namespace 2 | 3 | This folder contains everything that's available via the [utils] namespace, before and after initialization: 4 | 5 | ```js 6 | const pgpLib = require('pg-promise'); 7 | const pgp = pgpLib(/*initialization options*/); 8 | 9 | pgpLib.utils; // `utils` namespace 10 | pgp.utils; // `utils` namespace 11 | ``` 12 | 13 | [utils]:http://vitaly-t.github.io/pg-promise/utils.html 14 | -------------------------------------------------------------------------------- /lib/utils/color.js: -------------------------------------------------------------------------------- 1 | const util = require('util'); 2 | 3 | class ColorConsole { 4 | 5 | static log() { 6 | ColorConsole.writeNormal([...arguments], 39); // white 7 | } 8 | 9 | static info() { 10 | ColorConsole.writeNormal([...arguments], 36); // cyan 11 | } 12 | 13 | static success() { 14 | ColorConsole.writeNormal([...arguments], 32); // green 15 | } 16 | 17 | static warn() { 18 | ColorConsole.writeNormal([...arguments], 33); // yellow 19 | } 20 | 21 | static error() { 22 | ColorConsole.writeError([...arguments], 31); // red 23 | } 24 | 25 | static writeNormal(params, color) { 26 | // istanbul ignore else 27 | if (process.stdout.isTTY) { 28 | console.log.apply(null, ColorConsole.formatColor(params, color)); // eslint-disable-line no-console 29 | } else { 30 | console.log.apply(null, params); // eslint-disable-line no-console 31 | } 32 | } 33 | 34 | static writeError(params, color) { 35 | // istanbul ignore else 36 | if (process.stderr.isTTY) { 37 | console.error.apply(null, ColorConsole.formatColor(params, color)); // eslint-disable-line no-console 38 | } else { 39 | console.error.apply(null, params); // eslint-disable-line no-console 40 | } 41 | } 42 | 43 | static formatColor(args, color) { 44 | return args.map(a => `\x1b[${color}m${util.format(a)}\x1b[0m`); 45 | } 46 | } 47 | 48 | ColorConsole.log.bright = function () { 49 | ColorConsole.writeNormal([...arguments], 97); // light white 50 | }; 51 | 52 | ColorConsole.info.bright = function () { 53 | ColorConsole.writeNormal([...arguments], 93); // light cyan 54 | }; 55 | 56 | ColorConsole.success.bright = function () { 57 | ColorConsole.writeNormal([...arguments], 92); // light green 58 | }; 59 | 60 | ColorConsole.warn.bright = function () { 61 | ColorConsole.writeNormal([...arguments], 93); // light yellow 62 | }; 63 | 64 | ColorConsole.error.bright = function () { 65 | ColorConsole.writeError([...arguments], 91); // light red 66 | }; 67 | 68 | module.exports = {ColorConsole}; 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pg-promise", 3 | "version": "11.15.0", 4 | "description": "PostgreSQL interface for Node.js", 5 | "main": "lib/index.js", 6 | "typings": "typescript/pg-promise.d.ts", 7 | "scripts": { 8 | "spelling": "cspell --config=.cspell.json \"**/*.{md,ts,js}\" --no-progress", 9 | "coverage": "istanbul cover ./node_modules/jasmine-node/bin/jasmine-node --captureExceptions test", 10 | "doc": "jsdoc -c ./jsdoc/jsdoc.js ./jsdoc/README.md -t ./jsdoc/templates/custom", 11 | "lint": "eslint ./lib ./test/*.js ./test/db --fix", 12 | "test": "jasmine-node --captureExceptions test", 13 | "test:init": "node test/db/init.js", 14 | "test:native": "jasmine-node test --config PG_NATIVE true", 15 | "tslint": "tslint ./typescript/*.ts" 16 | }, 17 | "files": [ 18 | "lib", 19 | "typescript" 20 | ], 21 | "homepage": "https://github.com/vitaly-t/pg-promise", 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/vitaly-t/pg-promise.git" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/vitaly-t/pg-promise/issues", 28 | "email": "vitaly.tomilov@gmail.com" 29 | }, 30 | "keywords": [ 31 | "pg", 32 | "promise", 33 | "postgres" 34 | ], 35 | "author": { 36 | "name": "Vitaly Tomilov", 37 | "email": "vitaly.tomilov@gmail.com" 38 | }, 39 | "license": "MIT", 40 | "engines": { 41 | "node": ">=16.0" 42 | }, 43 | "dependencies": { 44 | "assert-options": "0.8.3", 45 | "pg": "8.16.3", 46 | "pg-minify": "1.8.0", 47 | "spex": "3.4.1" 48 | }, 49 | "peerDependencies": { 50 | "pg-query-stream": "4.10.3" 51 | }, 52 | "devDependencies": { 53 | "@eslint/js": "9.30.1", 54 | "@types/node": "24.0.10", 55 | "bluebird": "3.7.2", 56 | "coveralls": "3.1.1", 57 | "cspell": "9.1.3", 58 | "eslint": "9.30.1", 59 | "globals": "16.3.0", 60 | "istanbul": "0.4.5", 61 | "jasmine-node": "3.0.0", 62 | "jsdoc": "4.0.4", 63 | "JSONStream": "1.3.5", 64 | "tslint": "6.1.3", 65 | "typescript": "5.8.3" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/adapter.spec.js: -------------------------------------------------------------------------------- 1 | const pgp = require('../lib/index'); 2 | const {PromiseAdapter} = pgp; 3 | 4 | const dummy = () => { 5 | }; 6 | 7 | describe('Adapter', () => { 8 | 9 | describe('with invalid parameters', () => { 10 | const error = 'Adapter requires an api configuration object.'; 11 | it('must throw an error', () => { 12 | expect(() => { 13 | new PromiseAdapter(); 14 | }).toThrow(error); 15 | expect(() => { 16 | new PromiseAdapter(null); 17 | }).toThrow(error); 18 | expect(() => { 19 | new PromiseAdapter(123); 20 | }).toThrow(error); 21 | expect(() => { 22 | new PromiseAdapter('test'); 23 | }).toThrow(error); 24 | }); 25 | }); 26 | 27 | describe('with invalid \'create\'', () => { 28 | it('must throw an error', () => { 29 | expect(() => { 30 | new PromiseAdapter({}); 31 | }).toThrow('Function \'create\' must be specified.'); 32 | }); 33 | }); 34 | 35 | describe('with invalid \'resolve\'', () => { 36 | it('must throw an error', () => { 37 | expect(() => { 38 | new PromiseAdapter({create: dummy}); 39 | }).toThrow('Function \'resolve\' must be specified.'); 40 | }); 41 | }); 42 | 43 | describe('with invalid \'reject\'', () => { 44 | it('must throw an error', () => { 45 | expect(() => { 46 | new PromiseAdapter({create: dummy, resolve: dummy}); 47 | }).toThrow('Function \'reject\' must be specified.'); 48 | }); 49 | }); 50 | 51 | describe('with invalid \'all\'', () => { 52 | it('must throw an error', () => { 53 | expect(() => { 54 | new PromiseAdapter({create: dummy, resolve: dummy, reject: dummy}); 55 | }).toThrow('Function \'all\' must be specified.'); 56 | }); 57 | }); 58 | 59 | describe('with all valid parameters', () => { 60 | it('must be successful', () => { 61 | const adapter = new PromiseAdapter({create: dummy, resolve: dummy, reject: dummy, all: dummy}); 62 | expect(adapter instanceof PromiseAdapter).toBe(true); 63 | }); 64 | }); 65 | 66 | }); 67 | -------------------------------------------------------------------------------- /test/color.spec.js: -------------------------------------------------------------------------------- 1 | const {ColorConsole} = require('../lib/utils/color'); 2 | 3 | describe('protocol', () => { 4 | it('must have the right signature', () => { 5 | expect(typeof ColorConsole).toBe('function'); 6 | expect(typeof ColorConsole.log).toBe('function'); 7 | expect(typeof ColorConsole.log.bright).toBe('function'); 8 | expect(typeof ColorConsole.info).toBe('function'); 9 | expect(typeof ColorConsole.info.bright).toBe('function'); 10 | expect(typeof ColorConsole.warn).toBe('function'); 11 | expect(typeof ColorConsole.warn.bright).toBe('function'); 12 | expect(typeof ColorConsole.error).toBe('function'); 13 | expect(typeof ColorConsole.error.bright).toBe('function'); 14 | expect(typeof ColorConsole.success).toBe('function'); 15 | expect(typeof ColorConsole.success.bright).toBe('function'); 16 | }); 17 | }); 18 | 19 | describe('text', () => { 20 | it('must match', () => { 21 | expect(invoke(ColorConsole.log, 'log')).toEqual(['log']); 22 | expect(invoke(ColorConsole.log.bright, ['one', 'two'])).toEqual(['one', 'two']); 23 | expect(invoke(ColorConsole.info, 'info')).toEqual(['info']); 24 | expect(invoke(ColorConsole.info.bright, ['one', 'two'])).toEqual(['one', 'two']); 25 | expect(invoke(ColorConsole.warn, 'warning')).toEqual(['warning']); 26 | expect(invoke(ColorConsole.warn.bright, ['one', 'two'])).toEqual(['one', 'two']); 27 | expect(invoke(ColorConsole.success, 'success')).toEqual(['success']); 28 | expect(invoke(ColorConsole.success.bright, ['one', 'two'])).toEqual(['one', 'two']); 29 | expect(invoke(ColorConsole.error, 'error', true)).toEqual(['error']); 30 | expect(invoke(ColorConsole.error.bright, ['one', 'two'], true)).toEqual(['one', 'two']); 31 | }); 32 | 33 | function invoke(method, text, isErr) { 34 | text = Array.isArray(text) ? text : [text]; 35 | const name = isErr ? 'error' : 'log'; 36 | const old = console[name]; // eslint-disable-line no-console 37 | let res; 38 | console[name] = function () { // eslint-disable-line no-console 39 | res = [...arguments].map(removeColors); 40 | }; 41 | method.apply(null, text); 42 | console[name] = old; // eslint-disable-line no-console 43 | return res; 44 | } 45 | }); 46 | 47 | function removeColors(text) { 48 | /*eslint no-control-regex: 0*/ 49 | return text.replace(/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]/g, ''); 50 | } 51 | -------------------------------------------------------------------------------- /test/db/capture.js: -------------------------------------------------------------------------------- 1 | function hookConsole(callback) { 2 | 3 | const stdout = process.stdout, 4 | stderr = process.stderr, 5 | oldOutWrite = stdout.write, 6 | oldErrWrite = stderr.write; 7 | 8 | stdout.write = (() => { 9 | return string => { 10 | callback(string); 11 | }; 12 | })(stdout.write); 13 | 14 | stderr.write = (() => { 15 | return string => { 16 | callback(string); 17 | }; 18 | })(stderr.write); 19 | 20 | return () => { 21 | stdout.write = oldOutWrite; 22 | stderr.write = oldErrWrite; 23 | }; 24 | } 25 | 26 | // removes color elements from text; 27 | function removeColors(text) { 28 | return text.replace(/\\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]/g, ''); 29 | } 30 | 31 | function capture() { 32 | let text = ''; 33 | const hook = hookConsole(s => { 34 | if (!text) { 35 | text = s; 36 | } 37 | }); 38 | return () => { 39 | hook(); 40 | return removeColors(text); 41 | }; 42 | } 43 | 44 | module.exports = capture; 45 | -------------------------------------------------------------------------------- /test/db/header.js: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////// 2 | // Database connection header used in every test; 3 | ////////////////////////////////////////////////// 4 | 5 | const pgpLib = require('../../lib/index'); 6 | const defPromise = require('bluebird'); // default promise library; 7 | 8 | defPromise.config({ 9 | warnings: false 10 | }); 11 | 12 | // Either match your local database configuration according to the details below, 13 | // or the other way round - change the details to match your local configuration. 14 | const cn = { 15 | allowExitOnIdle: true, 16 | host: process.env.POSTGRES_HOST || 'localhost', // server name or IP address; 17 | port: 5432, // default port; 18 | database: process.env.POSTGRES_DB || 'pg_promise_test', // local database name for testing; 19 | user: process.env.POSTGRES_USER || 'postgres', // username; 20 | password: process.env.POSTGRES_PASSWORD || 'postgres' //- add password, if needed; 21 | }; 22 | pgpLib.suppressErrors = true; // suppress console output for error messages; 23 | 24 | function main(options, dc) { 25 | const pgNative = eval(process.env.PG_NATIVE); 26 | if (pgNative) { 27 | if (options && typeof options === 'object') { 28 | if (!('pgNative' in options)) { 29 | options.pgNative = true; 30 | } 31 | } else { 32 | if (!options) { 33 | options = { 34 | pgNative: true 35 | }; 36 | } 37 | } 38 | } 39 | const result = { 40 | pgpLib, cn, 41 | pgp: pgpLib(options) 42 | }; 43 | 44 | result.db = result.pgp(cn, dc); 45 | return result; 46 | } 47 | 48 | main.defPromise = defPromise; 49 | 50 | module.exports = main; 51 | -------------------------------------------------------------------------------- /test/db/init.js: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////// 2 | // Initialization scripts for the test database; 3 | //////////////////////////////////////////////// 4 | 5 | const dbHeader = require('./header'); 6 | const promise = dbHeader.defPromise; 7 | const {ColorConsole} = require('../../lib/utils/color'); 8 | 9 | const header = dbHeader({ 10 | query(e) { 11 | ColorConsole.info(e.query); // print all executed queries; 12 | }, 13 | promiseLib: promise, 14 | capSQL: true 15 | }); 16 | 17 | const {db} = header; 18 | 19 | async function getPgVersion() { 20 | const c = await db.connect(); 21 | c.done(); // success, release connection 22 | return c.client.serverVersion; 23 | } 24 | 25 | (async function () { 26 | 27 | const pgHighVersion = parseInt((await getPgVersion()).split('.')[0]); 28 | 29 | try { 30 | await db.tx(`Init PG v${pgHighVersion}`, async t => { 31 | 32 | // drop all functions; 33 | await t.none('DROP FUNCTION IF EXISTS "findUser"(int)'); 34 | await t.none('DROP FUNCTION IF EXISTS get_users()'); 35 | 36 | // drop all tables; 37 | await t.none('DROP TABLE IF EXISTS audit'); 38 | await t.none('DROP TABLE IF EXISTS person'); 39 | await t.none('DROP TABLE IF EXISTS users'); 40 | await t.none('DROP TABLE IF EXISTS images'); 41 | 42 | // create all tables; 43 | await t.none('CREATE TABLE audit(id serial, event text, created timestamptz, ref int)'); 44 | await t.none('CREATE TABLE person(id serial, name text, dob date)'); 45 | await t.none('CREATE TABLE users(id serial, login text, active boolean)'); 46 | await t.none('CREATE TABLE images(id serial, name text, data bytea)'); 47 | 48 | // insert records into 'users'; 49 | await t.none('INSERT INTO users(login, active) VALUES($1, $2)', ['user-1', true]); 50 | await t.none('INSERT INTO users(login, active) VALUES($1, $2)', ['user-2', true]); 51 | await t.none('INSERT INTO users(login, active) VALUES($1, $2)', ['user-3', false]); 52 | await t.none('INSERT INTO users(login, active) VALUES($1, $2)', ['user-4', false]); 53 | 54 | // insert records into 'person'; 55 | await t.none('INSERT INTO person(name, dob) VALUES($1, $2)', ['David', new Date(1995, 8, 7)]); 56 | await t.none('INSERT INTO person(name, dob) VALUES($1, $2)', ['John', new Date(1980, 3, 20)]); 57 | await t.none('INSERT INTO person(name, dob) VALUES($1, $2)', ['Mark', new Date(1973, 5, 12)]); 58 | await t.none('INSERT INTO person(name, dob) VALUES($1, $2)', ['Peter', new Date(1992, 11, 3)]); 59 | 60 | // adding functions: 61 | await t.none('CREATE OR REPLACE FUNCTION "findUser"(userId int) RETURNS SETOF users AS $ SELECT * FROM users WHERE id = userId $ language \'sql\''); 62 | await t.none('CREATE OR REPLACE FUNCTION get_users() RETURNS SETOF users AS $ SELECT * FROM users $ language \'sql\''); 63 | 64 | // adding procedures: 65 | if (pgHighVersion >= 11) { 66 | // Postgresql v10 and earlier do not support procedures 67 | await t.none('CREATE OR REPLACE PROCEDURE empty_proc() LANGUAGE SQL AS $ $;'); 68 | await t.none(`CREATE OR REPLACE PROCEDURE output_proc(INOUT output1 boolean, INOUT output2 text) 69 | LANGUAGE plpgsql AS $ 70 | BEGIN 71 | output1 := true; 72 | output2 := concat(output2, '-hello!'); 73 | END;$;`); 74 | } 75 | }); 76 | ColorConsole.success.bright('*** SUCCESS ***'); 77 | } catch (err) { 78 | ColorConsole.error.bright('FAILED:', err); 79 | } 80 | }()); 81 | -------------------------------------------------------------------------------- /test/db/tools.js: -------------------------------------------------------------------------------- 1 | const util = require('util'); 2 | 3 | function inspect(obj) { 4 | return obj[util.inspect.custom](); 5 | } 6 | 7 | module.exports = { 8 | inspect 9 | }; 10 | -------------------------------------------------------------------------------- /test/entry.spec.js: -------------------------------------------------------------------------------- 1 | const {PromiseAdapter} = require('../lib/index'); 2 | const supportsPromise = typeof Promise !== 'undefined'; 3 | 4 | const header = require('./db/header'); 5 | const promise = header.defPromise; 6 | const options = { 7 | promiseLib: promise, 8 | noWarnings: true 9 | }; 10 | const dbHeader = header(options); 11 | const pgp = dbHeader.pgp; 12 | const db = dbHeader.db; 13 | 14 | const BatchError = pgp.spex.errors.BatchError; 15 | 16 | function dummy() { 17 | } 18 | 19 | describe('Library entry function', () => { 20 | 21 | describe('without any promise override', () => { 22 | it('must return a valid library object', () => { 23 | if (supportsPromise) { 24 | const lib = header({noWarnings: true}); 25 | expect(typeof lib.pgp).toBe('function'); 26 | } else { 27 | expect(() => { 28 | header(); 29 | }).toThrow('Promise library must be specified.'); 30 | } 31 | }); 32 | }); 33 | 34 | describe('with PromiseAdapter override', () => { 35 | const P = header.defPromise; 36 | 37 | function create(func) { 38 | return new P(func); 39 | } 40 | 41 | function resolve(data) { 42 | return P.resolve(data); 43 | } 44 | 45 | function reject(reason) { 46 | return P.reject(reason); 47 | } 48 | 49 | function all(data) { 50 | return P.all(data); 51 | } 52 | 53 | const adapter = new PromiseAdapter({create, resolve, reject, all}); 54 | it('must accept custom promise', () => { 55 | const lib = header({ 56 | promiseLib: adapter, 57 | noWarnings: true 58 | }); 59 | expect(lib.pgp instanceof Function).toBe(true); 60 | }); 61 | 62 | describe('using PromiseAdapter', () => { 63 | let result; 64 | beforeEach(done => { 65 | const lib = header({ 66 | promiseLib: adapter, 67 | noWarnings: true 68 | }); 69 | lib.db.one('select 1 as value') 70 | .then(data => { 71 | result = data; 72 | }) 73 | .finally(done); 74 | }); 75 | it('must return successfully', () => { 76 | expect(result.value).toBe(1); 77 | }); 78 | }); 79 | }); 80 | 81 | if (supportsPromise) { 82 | describe('without any options', () => { 83 | let result; 84 | beforeEach(done => { 85 | const db1 = header({noWarnings: true, promiseLib: promise}).db; 86 | db1.query('select * from users') 87 | .then(data => { 88 | result = data; 89 | }) 90 | .finally(done); 91 | }); 92 | it('must be able to execute queries', () => { 93 | expect(Array.isArray(result)).toBe(true); 94 | }); 95 | }); 96 | } 97 | 98 | describe('with a valid promise library-object override', () => { 99 | it('must return a valid library object', () => { 100 | const lib = header( 101 | { 102 | promiseLib: { 103 | resolve: dummy, 104 | reject: dummy, 105 | all: dummy 106 | }, 107 | noWarnings: true 108 | }); 109 | expect(typeof lib.pgp).toBe('function'); 110 | }); 111 | }); 112 | 113 | describe('with a valid promise library-function override', () => { 114 | it('must return a valid library object', () => { 115 | function fakePromiseLib() { 116 | } 117 | 118 | fakePromiseLib.resolve = dummy; 119 | fakePromiseLib.reject = dummy; 120 | fakePromiseLib.all = dummy; 121 | const lib = header({ 122 | promiseLib: fakePromiseLib, 123 | noWarnings: true 124 | }); 125 | expect(typeof lib.pgp).toBe('function'); 126 | }); 127 | }); 128 | 129 | describe('with invalid promise override', () => { 130 | const error = 'Invalid promise library specified.'; 131 | it('must throw the correct error', () => { 132 | expect(() => { 133 | header({ 134 | promiseLib: 'test' 135 | }); 136 | }) 137 | .toThrow(error); 138 | expect(() => { 139 | header({ 140 | promiseLib: dummy 141 | }); 142 | }) 143 | .toThrow(error); 144 | }); 145 | }); 146 | 147 | describe('with invalid options parameter', () => { 148 | const errBody = 'Invalid "options" parameter: '; 149 | it('must throw an error', () => { 150 | expect(() => { 151 | header(123); 152 | }).toThrow(errBody + '123'); 153 | expect(() => { 154 | header('hello'); 155 | }).toThrow(errBody + '"hello"'); 156 | }); 157 | }); 158 | 159 | describe('multi-init', () => { 160 | 161 | const PromiseOne = { 162 | create: cb => new promise.Promise(cb), 163 | resolve: data => promise.resolve(data), 164 | reject: () => promise.reject('reject-one'), 165 | all: data => promise.all(data) 166 | }; 167 | 168 | const PromiseTwo = { 169 | create: cb => new promise.Promise(cb), 170 | resolve: data => promise.resolve(data), 171 | reject: () => promise.reject('reject-two'), 172 | all: data => promise.all(data) 173 | }; 174 | 175 | const one = new PromiseAdapter(PromiseOne); 176 | const two = new PromiseAdapter(PromiseTwo); 177 | let result; 178 | 179 | beforeEach(done => { 180 | const pg1 = header({promiseLib: one, noWarnings: true}), db1 = pg1.db; 181 | const pg2 = header({promiseLib: two, noWarnings: true}), db2 = pg2.db; 182 | db.task(t => { 183 | return t.batch([ 184 | db1.query('select $1', []), db2.query('select $1', []) 185 | ]); 186 | }) 187 | .catch(error => { 188 | result = error; 189 | }) 190 | .finally(done); 191 | }); 192 | 193 | it('must be supported', () => { 194 | expect(result instanceof BatchError).toBe(true); 195 | expect(result.data).toEqual([ 196 | { 197 | success: false, 198 | result: 'reject-one' 199 | }, 200 | { 201 | success: false, 202 | result: 'reject-two' 203 | } 204 | ]); 205 | }); 206 | }); 207 | 208 | describe('Taking no initialization options', () => { 209 | it('must be supported', () => { 210 | expect(typeof dbHeader.pgpLib()).toBe('function'); 211 | }); 212 | }); 213 | }); 214 | -------------------------------------------------------------------------------- /test/generator.spec.js: -------------------------------------------------------------------------------- 1 | const header = require('./db/header'); 2 | const promise = header.defPromise; 3 | 4 | const options = { 5 | noWarnings: true, 6 | promiseLib: promise 7 | }; 8 | 9 | const dbHeader = header(options); 10 | const db = dbHeader.db; 11 | 12 | describe('ES6 Generators', () => { 13 | 14 | // v9.0 dropped all generators support, and now it throws an error, 15 | // if someone still tries to use them. 16 | 17 | const errMsg = 'ES6 generator functions are no longer supported!'; 18 | 19 | describe('for tasks', () => { 20 | let error; 21 | beforeEach(done => { 22 | db.task(function* () { 23 | }) 24 | .catch(e => { 25 | error = e; 26 | }) 27 | .finally(done); 28 | }); 29 | it('must reject', () => { 30 | expect(error instanceof TypeError).toBe(true); 31 | expect(error.message).toBe(errMsg); 32 | }); 33 | }); 34 | 35 | describe('for transactions', () => { 36 | let error; 37 | beforeEach(done => { 38 | db.tx(function* () { 39 | }) 40 | .catch(e => { 41 | error = e; 42 | }) 43 | .finally(done); 44 | }); 45 | it('must reject', () => { 46 | expect(error instanceof TypeError).toBe(true); 47 | expect(error.message).toBe(errMsg); 48 | }); 49 | }); 50 | 51 | }); 52 | -------------------------------------------------------------------------------- /test/sql-special/dup-files/1-one.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/test/sql-special/dup-files/1-one.sql -------------------------------------------------------------------------------- /test/sql-special/dup-files/one.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/test/sql-special/dup-files/one.sql -------------------------------------------------------------------------------- /test/sql-special/dup-folders/1-sub/first.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/test/sql-special/dup-folders/1-sub/first.sql -------------------------------------------------------------------------------- /test/sql-special/dup-folders/sub/first.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/test/sql-special/dup-folders/sub/first.sql -------------------------------------------------------------------------------- /test/sql-special/folders-only/one/first.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/test/sql-special/folders-only/one/first.sql -------------------------------------------------------------------------------- /test/sql-special/folders-only/two/first.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/test/sql-special/folders-only/two/first.sql -------------------------------------------------------------------------------- /test/sql-special/one.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/test/sql-special/one.sql -------------------------------------------------------------------------------- /test/sql-special/sql-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "dir": "./test/sql", 3 | "output": "generated.js", 4 | "module": { 5 | "name": "load", 6 | "path": "./loadSql" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/sql-special/sql-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /test/sql-special/sql-simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "dir": "./test/sql", 3 | "output": "generated.js", 4 | "module": { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/sql-special/two.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/test/sql-special/two.sql -------------------------------------------------------------------------------- /test/sql/allUsers.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Multi-line comments 3 | */ 4 | select * from users -- single-line comment 5 | -------------------------------------------------------------------------------- /test/sql/invalid.sql: -------------------------------------------------------------------------------- 1 | select '1; -------------------------------------------------------------------------------- /test/sql/params.sql: -------------------------------------------------------------------------------- 1 | /* 2 | This is to test static formatting parameters, 3 | such as schema name and table name. 4 | */ 5 | 6 | -- We schema/table we should use the SQL Name syntax (with tilde): 7 | SELECT ${column~} FROM ${schema~}.${table~} 8 | -------------------------------------------------------------------------------- /test/sql/simple.sql: -------------------------------------------------------------------------------- 1 | select 1; --comment -------------------------------------------------------------------------------- /test/sql/sub/one.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/test/sql/sub/one.sql -------------------------------------------------------------------------------- /test/sql/sub/one.two.three.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/test/sql/sub/one.two.three.sql -------------------------------------------------------------------------------- /test/sql/sub/third/notSql.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/test/sql/sub/third/notSql.txt -------------------------------------------------------------------------------- /test/sql/sub/two.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vitaly-t/pg-promise/563b7c2162ba3ce3d46f7f85ee86758af18691f2/test/sql/sub/two.sql -------------------------------------------------------------------------------- /test/typescript.spec.js: -------------------------------------------------------------------------------- 1 | const exec = require('child_process').exec; 2 | const path = require('path'); 3 | const TIMEOUT = 20000; // 20 SECONDS 4 | 5 | describe('Typescript', () => { 6 | describe('build', () => { 7 | it('must build without error', done => { 8 | exec('tsc', {cwd: path.join('test', 'typescript')}, error => { 9 | expect(error).toBe(null); 10 | done(); 11 | }); 12 | }, TIMEOUT); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/typescript/adapter.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | 3 | const adapter = new pgPromise.PromiseAdapter({ 4 | create(cb) { 5 | return new Promise(cb); 6 | }, 7 | resolve() { 8 | }, 9 | reject() { 10 | }, 11 | all() { 12 | return Promise.resolve(); 13 | } 14 | }); 15 | 16 | const pgp = pgPromise({ 17 | promiseLib: adapter 18 | }); 19 | -------------------------------------------------------------------------------- /test/typescript/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call tsc 3 | -------------------------------------------------------------------------------- /test/typescript/clean.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | call del *.js *.map /Q 4 | -------------------------------------------------------------------------------- /test/typescript/config.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | import {IGenericPromise} from "../../typescript/pg-promise"; 3 | 4 | const pgp: pgPromise.IMain = pgPromise(); 5 | const db = pgp('connection'); 6 | 7 | const cfg = db.$config; 8 | const p: IGenericPromise = cfg.promise; 9 | 10 | p((resolve, reject) => { 11 | resolve(123); 12 | reject(new Error('ops!')); 13 | }) 14 | .then(data => { 15 | 16 | }); 17 | 18 | cfg.options.capSQL = true; 19 | 20 | cfg.pgp.as.format(''); 21 | 22 | const version: string = cfg.version; 23 | -------------------------------------------------------------------------------- /test/typescript/connection.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | import {IConnectionParameters} from '../../typescript/pg-subset'; 3 | import {ILostContext} from "../../typescript/pg-promise"; 4 | 5 | const pgp: pgPromise.IMain = pgPromise({ 6 | schema: ['public', 'mine'], 7 | capSQL: true, 8 | pgNative: true, 9 | pgFormatting: true, 10 | noWarnings: true 11 | }); 12 | 13 | const pgp2: pgPromise.IMain = pgPromise({ 14 | schema() { 15 | } 16 | }); 17 | 18 | const pgp3: pgPromise.IMain = pgPromise({ 19 | schema(dc: any) { 20 | return ['one', 'two']; 21 | } 22 | }); 23 | 24 | const db = pgp('connection'); 25 | 26 | type t = IConnectionParameters; 27 | let r: t = {}; 28 | r.application_name = 'hello'; 29 | r.ssl = { 30 | ca: '' 31 | }; 32 | 33 | r.password = () => 'pass123'; 34 | 35 | r.keepAlive = true; 36 | 37 | const db2 = pgp({ 38 | binary: true 39 | }); 40 | 41 | db.connect() 42 | .then(ctx => { 43 | ctx.batch([1, 2, 3]); 44 | ctx.sequence(index => { 45 | }); 46 | ctx.page(a => { 47 | }); 48 | const cn = ctx.client.connectionParameters; 49 | ctx.done(); 50 | }); 51 | 52 | db.connect({}); 53 | 54 | db.connect({ 55 | direct: true, 56 | onLost(err: any, e: ILostContext) { 57 | e.client.removeListener('notification', () => { 58 | }) 59 | } 60 | }); 61 | -------------------------------------------------------------------------------- /test/typescript/errors.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | 3 | const err1 = <pgPromise.errors.QueryResultError><unknown>null; 4 | const query = err1.query; 5 | 6 | const err2 = <pgPromise.errors.QueryFileError><unknown>null; 7 | const line = err2.error.position.line; 8 | 9 | const err3 = <pgPromise.errors.PreparedStatementError><unknown>null; 10 | const file = err3.error.file; 11 | 12 | const qrec = pgPromise.errors.queryResultErrorCode; 13 | const t = qrec.multiple; 14 | -------------------------------------------------------------------------------- /test/typescript/events.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | 3 | const pgp: pgPromise.IMain<{}, MyClient> = pgPromise({ 4 | connect(e) { 5 | const v = e.client.version; 6 | }, 7 | receive(e: any) { 8 | const dc = e.ctx.dc; 9 | const d = e.data[0].prop; 10 | const r = e.result.fields[0].name; 11 | const query = e.query; 12 | }, 13 | query(e: any) { 14 | const dc = e.dc; 15 | const query = e.query; 16 | }, 17 | error(err: any, e: any) { 18 | const dc = e.dc; 19 | const query = e.query; 20 | }, 21 | extend(obj: any, dc: any) { 22 | obj['method'] = (val: any) => { 23 | return obj.one(null, val); 24 | } 25 | } 26 | }); 27 | 28 | class MyClient extends pgp.pg.Client { 29 | version?: string; 30 | } 31 | 32 | const db = pgp({ 33 | Client: MyClient 34 | }); 35 | 36 | db.task(t => { 37 | const dc = t.ctx.dc; 38 | const useCount = t.ctx.useCount; 39 | return t.batch([ 40 | t.one('query'), 41 | t.none('query') 42 | ]); 43 | }) 44 | .then(data => { 45 | }) 46 | .catch(error => { 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /test/typescript/extensions.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | 3 | interface IExtensions { 4 | findUser(userId: number): Promise<any>; 5 | } 6 | 7 | const pgp: pgPromise.IMain = pgPromise({ 8 | extend(obj: any, dc: any) { 9 | obj['findUser'] = (userId: number) => { 10 | return obj.one('', userId); 11 | } 12 | } 13 | }); 14 | 15 | const db = pgp('connection'); 16 | 17 | const pgpExt = pgPromise({ 18 | extend(obj: pgPromise.IDatabase<IExtensions> & IExtensions) { 19 | obj.findUser = (userId: number) => { 20 | return obj.one('', userId); 21 | } 22 | } 23 | }); 24 | 25 | const dbExt1 = pgp<IExtensions>('connection'); 26 | const dbExt2 = pgpExt<IExtensions>('connection'); 27 | const dbExt3 = pgpExt<IExtensions>('connection'); 28 | 29 | dbExt1.findUser(123).then(); 30 | dbExt2.findUser(123).then(); 31 | dbExt3.findUser(123).then(); 32 | 33 | dbExt1.task(function (t) { 34 | return t.findUser(123); 35 | }); 36 | 37 | dbExt2.task(function (t) { 38 | return t.findUser(123); 39 | }); 40 | 41 | dbExt3.task(function (t) { 42 | return t.findUser(123); 43 | }); 44 | 45 | dbExt3.task(t => { 46 | return [1, 2, 3]; 47 | }) 48 | .then(data => { 49 | const a: number = data[0]; 50 | }); 51 | 52 | dbExt3.tx<number>(t => { 53 | return Promise.resolve(123); 54 | }) 55 | .then(data => { 56 | const a: number = data; 57 | }); 58 | 59 | dbExt1.tx(t => { 60 | return t.findUser(123); 61 | }); 62 | 63 | dbExt2.tx(t => { 64 | return t.findUser(123); 65 | }); 66 | 67 | dbExt3.tx(t => { 68 | return t.findUser(123); 69 | }); 70 | -------------------------------------------------------------------------------- /test/typescript/formatting.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | 3 | const value1 = pgPromise.as.array([]); 4 | const value2 = pgPromise.as.array(() => []); 5 | 6 | const pgp: pgPromise.IMain = pgPromise(); 7 | 8 | const value3 = pgp.as.array([], {capSQL: true}); 9 | const value4 = pgp.as.array(() => []); 10 | const value5 = pgp.as.format('hello', []); 11 | const value6 = pgp.as.format(new pgPromise.QueryFile('')); 12 | 13 | let alias = pgp.as.alias('a'); 14 | alias = pgp.as.alias(() => 'a'); 15 | 16 | const num1 = pgp.as.number(123); 17 | const num2 = pgp.as.number(123n); 18 | const num3 = pgp.as.number(() => 123); 19 | const num4 = pgp.as.number(() => 123n); 20 | 21 | class CTF { 22 | 23 | constructor() { 24 | this.rawType = true; 25 | } 26 | 27 | toPostgres(a: any) { 28 | 29 | } 30 | 31 | rawType: boolean; 32 | 33 | // CTF symbols support: 34 | 35 | [pgp.as.ctf.toPostgres](): any { 36 | return 123; 37 | }; 38 | 39 | // Ops! TypeScript doesn't support it yet! 40 | // See these issues: 41 | // - https://github.com/Microsoft/TypeScript/issues/16432 42 | // - https://github.com/Microsoft/TypeScript/pull/15473 43 | // 44 | // [pgp.as.ctf.rawType]: boolean; 45 | 46 | } 47 | 48 | const ctf = new CTF(); 49 | 50 | const testCTF = pgp.as.format(ctf, null, {def: 1, partial: true, capSQL: true}); 51 | 52 | const testFunc1 = pgp.as.func(() => { 53 | }); 54 | 55 | const testFunc2 = pgp.as.func(a => { 56 | return 123; 57 | }); 58 | -------------------------------------------------------------------------------- /test/typescript/help.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | import {Column, ColumnSet, TableName, IMain} from '../../typescript/pg-promise'; 3 | 4 | const pgp: IMain = pgPromise({ 5 | capSQL: true 6 | }); 7 | 8 | const col1: Column = new pgp.helpers.Column({ 9 | name: 'third-col', 10 | prop: 'third', 11 | def: 888, 12 | cast: '', 13 | cnd: true, 14 | init(col) { 15 | const e = col.exists; 16 | return this; 17 | }, 18 | skip(col) { 19 | const e = col.exists; 20 | return false; 21 | } 22 | }); 23 | 24 | const col2: Column = new pgp.helpers.Column('col'); 25 | 26 | const data = [ 27 | { 28 | first: 1, 29 | second: 'two' 30 | }, { 31 | first: 111, 32 | second: 'hello' 33 | } 34 | ]; 35 | 36 | const table1: TableName = new pgp.helpers.TableName(''); 37 | const table2: TableName = new pgp.helpers.TableName({table: ''}); 38 | const table3: TableName = new pgp.helpers.TableName({table: '', schema: ''}); 39 | 40 | const cs: ColumnSet = new pgp.helpers.ColumnSet([ 41 | 'first', 'second', { 42 | name: 'third-col', 43 | prop: 'third', 44 | def: 888, 45 | cast: '', 46 | cnd: true, 47 | init() { 48 | }, 49 | skip() { 50 | } 51 | } 52 | ], {table: 'my-table', inherit: true}); 53 | 54 | var r = cs.columns; 55 | 56 | const insert: string = pgp.helpers.insert(data, cs, 'my-table'); 57 | const update: string = pgp.helpers.update(data, cs, table1, {tableAlias: 'W'}); 58 | 59 | const values1: string = pgp.helpers.values({}); 60 | const values2: string = pgp.helpers.values({}, []); 61 | const values3: string = pgp.helpers.values([{}], []); 62 | const values4: string = pgp.helpers.values([], cs); 63 | 64 | const names: string = cs.names; 65 | const variables: string = cs.variables; 66 | 67 | let obj = cs.prepare(Date); 68 | obj = cs.prepare({}); 69 | 70 | const sets1: string = pgp.helpers.sets({}); 71 | const sets2: string = pgp.helpers.sets([]); 72 | const sets3: string = pgp.helpers.sets({}, cs); 73 | 74 | const cs1: ColumnSet = cs.extend(['']); 75 | const cs2: ColumnSet = cs1.merge(cs); 76 | 77 | const c1: string = pgp.helpers.concat(['first', {query: 'second'}]); 78 | const c2: string = pgp.helpers.concat(['first', new pgp.QueryFile(''), { 79 | query: '', 80 | values: 123, 81 | options: {partial: true} 82 | }]); 83 | 84 | cs1.assignColumns(); 85 | cs1.assignColumns({}); 86 | cs1.assignColumns({from: 'source'}); 87 | cs1.assignColumns({to: 'target', skip: 'one'}); 88 | cs1.assignColumns({skip: ['one', 'two']}); 89 | -------------------------------------------------------------------------------- /test/typescript/init.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | import {IConnectionParameters} from '../../typescript/pg-subset'; 3 | 4 | const pgp: pgPromise.IMain = pgPromise({ 5 | capSQL: true, 6 | pgFormatting: true, 7 | pgNative: true 8 | }); 9 | 10 | let c: IConnectionParameters = {}; 11 | c.binary = true; 12 | 13 | const spex = pgp.spex; 14 | 15 | async function test() { 16 | const [first, second] = await spex.batch<number, string>([1, 'hello']); 17 | 18 | const res = await spex.batch<number, string>([1, 'hello']); 19 | const d = res.duration; 20 | } 21 | 22 | interface Test { 23 | hello: string; 24 | } 25 | 26 | const db = <pgPromise.IDatabase<Test> & Test>pgp({ 27 | isDomainSocket: true, 28 | max: 20, 29 | application_name: 'my-app' 30 | }); 31 | 32 | const connection1: string = <string>db.$cn; 33 | const connection2: IConnectionParameters = <IConnectionParameters>db.$cn; 34 | 35 | const context: any = db.$dc; 36 | const pool: any = db.$pool; 37 | 38 | db.one(''); 39 | 40 | db.one(new pgPromise.QueryFile('')); 41 | 42 | db.tx({tag: 123}, function (t) { 43 | const w = t.one(''); 44 | const q = t.hello; 45 | }); 46 | -------------------------------------------------------------------------------- /test/typescript/parameterized.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | 3 | const pgp: pgPromise.IMain = pgPromise(); 4 | const db: pgPromise.IDatabase<any> = pgp('connection'); 5 | 6 | const pq0 = new pgp.ParameterizedQuery(); 7 | const pq01 = new pgp.ParameterizedQuery({}); 8 | const pq1 = new pgp.ParameterizedQuery(''); 9 | const pq2 = new pgp.ParameterizedQuery({text: ''}); 10 | const pq3 = new pgp.ParameterizedQuery(pq1); 11 | 12 | const qf = new pgPromise.QueryFile('file'); 13 | const pq4 = new pgp.ParameterizedQuery({text: qf, values: [123], rowMode: 'array'}); 14 | 15 | pq4.text = 'test'; 16 | pq4.text = qf; 17 | 18 | db.one(pq1); 19 | 20 | db.one({ 21 | name: '', 22 | text: '' 23 | }); 24 | 25 | db.one({ 26 | name: '', 27 | text: qf 28 | }); 29 | 30 | const test1 = <pgPromise.errors.ParameterizedQueryError>pq1.parse(); 31 | const file = test1.error.file; 32 | -------------------------------------------------------------------------------- /test/typescript/pg.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | import {IColumn, IResult} from '../../typescript/pg-subset'; 3 | import {TypeId} from '../../typescript/pg-subset'; 4 | 5 | const pgp: pgPromise.IMain = pgPromise(); 6 | 7 | class MyClient extends pgp.pg.Client { 8 | 9 | version?: string; 10 | 11 | constructor(config: any) { 12 | super(config); 13 | this.connection.on('parameterStatus', msg => { 14 | // See the following question: 15 | // https://stackoverflow.com/questions/58725659/get-the-postgresql-server-version-from-connection 16 | if (msg.parameterName === 'server_version') { 17 | this.version = msg.parameterValue; 18 | } 19 | }); 20 | this.connection.stream.on('something', () => { 21 | 22 | }); 23 | 24 | // any event handlers are allowed: 25 | this.on('whatever', (whatever1: any, whatever2: any) => { 26 | 27 | }); 28 | 29 | this.query('test', [1, 2]).then(); 30 | } 31 | } 32 | 33 | const db = pgp({ 34 | Client: MyClient 35 | }); 36 | 37 | const pg = pgp.pg; 38 | 39 | pg.types.setTypeParser(TypeId.INT8, parseInt); 40 | pg.types.setTypeParser(pg.types.builtins.INT8, parseInt); 41 | 42 | db.connect() 43 | .then(t => { 44 | const v = t.client.version; 45 | t.client.on('notification', (message: any) => { 46 | const s = message.anything; 47 | }); 48 | t.client.removeAllListeners(); 49 | }); 50 | 51 | const database = pg.defaults.database; 52 | 53 | let col: IColumn; 54 | let res: IResult; 55 | -------------------------------------------------------------------------------- /test/typescript/pool.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | 3 | const pgp: pgPromise.IMain = pgPromise({}); 4 | 5 | const db = pgp('connection'); 6 | 7 | (async function () { 8 | const res: undefined = await db.$pool.end(); 9 | 10 | const res2: void = await db.$pool.end((err: Error) => { 11 | console.log(err.stack); 12 | }); 13 | 14 | const {totalCount, idleCount, waitingCount} = db.$pool; 15 | 16 | db.$pool.on('error', err => { 17 | // handle errors 18 | }) 19 | 20 | const database = db.$pool.options.database; 21 | })(); 22 | -------------------------------------------------------------------------------- /test/typescript/prepared.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | 3 | const pgp: pgPromise.IMain = pgPromise(); 4 | const db: pgPromise.IDatabase<any> = pgp('connection'); 5 | 6 | const ps1 = new pgp.PreparedStatement({name: '', text: ''}); 7 | const ps2 = new pgp.PreparedStatement(ps1); 8 | 9 | const qf = new pgPromise.QueryFile('file'); 10 | 11 | ps1.text = 'some text'; 12 | ps1.text = qf; 13 | 14 | const ps11 = new pgp.PreparedStatement(); 15 | const ps22 = new pgp.PreparedStatement({}); 16 | const ps3 = new pgp.PreparedStatement({name: '', text: qf, values: [], rowMode: null, rows: 123}); 17 | const ps4 = new pgp.PreparedStatement({name: '', text: qf, values: [], rowMode: 'array', rows: 0}); 18 | 19 | db.one(ps1); 20 | 21 | db.one({ 22 | name: '', 23 | text: '' 24 | }); 25 | 26 | db.one({ 27 | name: '', 28 | text: qf 29 | }); 30 | 31 | const test1 = <pgPromise.errors.PreparedStatementError>ps1.parse(); 32 | const file = test1.error.file; 33 | -------------------------------------------------------------------------------- /test/typescript/queries.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | 3 | const pgp: pgPromise.IMain = pgPromise(); 4 | 5 | const db: pgPromise.IDatabase<any> = pgp('connection'); 6 | 7 | const qrm = pgPromise.queryResult; 8 | 9 | db.query('', [], qrm.one | qrm.none) 10 | .then(data => { 11 | const d1 = data.value; 12 | const d2 = data[0].value; 13 | }); 14 | 15 | db.any<number>('').then(data => { 16 | const a: number = data[0]; 17 | }); 18 | 19 | db.any('').then(data => { 20 | const a: number = data[0]; 21 | }); 22 | 23 | db.none('') 24 | .then(data => { 25 | }); 26 | 27 | db.one('', [], value => { 28 | return {value: 123}; 29 | }, 'this') 30 | .then(data => { 31 | const value = data.value; 32 | }); 33 | 34 | db.oneOrNone('') 35 | .then(data => { 36 | const value = data.value; 37 | }); 38 | 39 | db.many('') 40 | .then(data => { 41 | const value = data[0].ops; 42 | }); 43 | 44 | const r = db.result('', [], data => { 45 | for (const t of data) { 46 | // can iterate 47 | } 48 | return data; 49 | }, 123); 50 | 51 | db.func('func_name').then(); 52 | db.func('func_name', [123]).then(); 53 | db.func('func_name', [123]).then(); 54 | 55 | db.proc('proc_name').then(); 56 | db.proc('proc_name', [123]).then(); 57 | db.proc('proc_name', [123], a => a.value).then(); 58 | db.proc<number>('proc_name', [123], a => a && a.value) 59 | .then((value: number | null) => { 60 | }); 61 | 62 | db.multiResult('', []) 63 | .then(data => { 64 | const tableID = data[0].fields[0].tableID; 65 | }); 66 | 67 | db.multi('', []) 68 | .then(data => { 69 | const length = data[0].length; 70 | }); 71 | 72 | db.map('', null, row => { 73 | return row.value; 74 | }) 75 | .then((data: any[]) => { 76 | const d: number = data[0]; 77 | }); 78 | 79 | db.each('', null, row => { 80 | const v = row.value; 81 | }) 82 | .then((data: any[]) => { 83 | }); 84 | 85 | db.task(t => { 86 | return t.batch([ 87 | t.one('') 88 | ]) 89 | .then((data: any[]) => { 90 | return data; 91 | }); 92 | }) 93 | .then(data => { 94 | const d1 = data.value; 95 | const d2 = data[0].value; 96 | }); 97 | 98 | function getQuery() { 99 | return getNested; 100 | } 101 | 102 | function getNested(): string { 103 | return 'SELECT 123;'; 104 | } 105 | 106 | db.any(getQuery, 123); 107 | 108 | function getOne() { 109 | return {text: ''}; 110 | } 111 | 112 | db.one(getOne); 113 | -------------------------------------------------------------------------------- /test/typescript/tasks.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | 3 | import {ITaskIfOptions, ITxIfOptions} from '../../typescript/pg-promise'; 4 | 5 | const pgp: pgPromise.IMain = pgPromise(); 6 | const db: pgPromise.IDatabase<any> = pgp('connection'); 7 | 8 | db.task(t => { 9 | const d: Date = t.ctx.start; 10 | const duration: number = t.ctx.duration; 11 | const parentTag = t.ctx.parent.tag; 12 | const connected: boolean = t.ctx.connected; 13 | return t.batch([]); 14 | }); 15 | 16 | db.task('with a name', t => { 17 | const d: Date = t.ctx.start; 18 | t.sequence(() => { 19 | }); 20 | }); 21 | 22 | db.tx(t => { 23 | const d: Date = t.ctx.start; 24 | t.page(() => { 25 | }); 26 | }); 27 | 28 | db.tx('with a name', t => { 29 | const d: Date = t.ctx.start; 30 | }); 31 | 32 | db.task<number>(t => { 33 | return 123; 34 | }) 35 | .then(data => { 36 | }); 37 | 38 | db.task<number>(t => { 39 | return Promise.resolve(123); 40 | }) 41 | .then(data => { 42 | }); 43 | 44 | const res = db.taskIf({cnd: true, tag: 123}, t => { 45 | return 123; 46 | }); 47 | 48 | type TestExt = { 49 | bla(): any; 50 | } 51 | 52 | const txOpt: ITxIfOptions<TestExt> = { 53 | cnd: ctx => { 54 | return true 55 | }, tag: 123, mode: null, reusable: true 56 | }; 57 | 58 | const res1 = db.txIf(txOpt, async t => { 59 | const w = t.bla(); 60 | return true; 61 | }); 62 | -------------------------------------------------------------------------------- /test/typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "target": "esnext", 6 | "sourceMap": true, 7 | "strict": true 8 | }, 9 | "exclude": [ 10 | "node_modules" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /test/typescript/tx-mode.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | 3 | const pgp: pgPromise.IMain = pgPromise(); 4 | const db: pgPromise.IDatabase<any> = pgp('connection'); 5 | 6 | const {TransactionMode} = pgPromise.txMode; 7 | 8 | const mode = new TransactionMode({ 9 | deferrable: true, 10 | readOnly: true, 11 | tiLevel: pgPromise.txMode.isolationLevel.readCommitted 12 | }); 13 | 14 | db.tx<number>({mode: null}, t => { 15 | return 123; 16 | }); 17 | 18 | db.tx<number>({mode}, t => { 19 | return 123; 20 | }); 21 | 22 | db.txIf<boolean>({cnd: true, tag: 123, mode: null, reusable: true}, t => { 23 | return true; 24 | }); 25 | 26 | db.txIf<boolean>({cnd: true, tag: 123, mode, reusable: true}, t => { 27 | return true; 28 | }); 29 | -------------------------------------------------------------------------------- /test/typescript/utils.ts: -------------------------------------------------------------------------------- 1 | import * as pgPromise from '../../typescript/pg-promise'; 2 | 3 | const utils = pgPromise.utils; 4 | 5 | const pgp: pgPromise.IMain = pgPromise(); 6 | 7 | const utilsExtra = pgp.utils; 8 | 9 | utils.camelize(''); 10 | 11 | utils.camelizeVar(''); 12 | 13 | const tree = utils.enumSql('', {recursive: true, ignoreErrors: true}, (file: string, name: string, path: string) => { 14 | 15 | }); 16 | 17 | function testTaskArgs() { 18 | const args = utils.taskArgs<{ first: string, second: boolean }>(arguments); 19 | args.options.tag = 123; 20 | args.options.mode = null; 21 | args.options.cnd = new Error(); 22 | 23 | args.options.first = ''; 24 | args.options.second = true; 25 | } 26 | 27 | testTaskArgs(); 28 | -------------------------------------------------------------------------------- /typescript/README.md: -------------------------------------------------------------------------------- 1 | ## TypeScript for pg-promise 2 | 3 | Complete TypeScript declarations for [pg-promise]. 4 | 5 | ### Inclusion 6 | 7 | Typescript should be able to pick up the definitions without any manual configuration. 8 | 9 | ### Simple Usage 10 | 11 | ```ts 12 | import pgPromise from 'pg-promise'; 13 | 14 | const pgp = pgPromise({/* Initialization Options */}); 15 | 16 | const db = pgp('postgres://username:password@host:port/database'); 17 | 18 | const {value} = await db.one('SELECT 123 as value'); 19 | ``` 20 | 21 | #### With Extensions 22 | 23 | The library supports dynamic protocol extensions, via event [extend], which requires 24 | an explicit extension interface to be declared and parameterized, as shown below. 25 | 26 | ```ts 27 | import * as pgPromise from 'pg-promise'; 28 | 29 | // your protocol extensions: 30 | interface IExtensions { 31 | findUser(userId: number): Promise<any>; 32 | } 33 | 34 | // pg-promise initialization options: 35 | const options: pgPromise.IInitOptions<IExtensions> = { 36 | extend(obj) { 37 | obj.findUser = userId => { 38 | return obj.one('SELECT * FROM Users WHERE id = $1', [userId]); 39 | } 40 | } 41 | }; 42 | 43 | // initializing the library: 44 | const pgp = pgPromise(options); 45 | 46 | // database object: 47 | const db = pgp('postgres://username:password@host:port/database'); 48 | 49 | // protocol is extended on each level: 50 | const user = await db.findUser(123); 51 | 52 | // ...including inside tasks and transactions: 53 | await db.task(async t => { 54 | const user = await t.findUser(123); 55 | // ...etc 56 | }); 57 | ``` 58 | 59 | For a comprehensive example, check out [pg-promise-demo]. 60 | 61 | [pg-promise-demo]:https://github.com/vitaly-t/pg-promise-demo 62 | [extend]:https://vitaly-t.github.io/pg-promise/global.html#event:extend 63 | [pg-promise]:https://github.com/vitaly-t/pg-promise 64 | -------------------------------------------------------------------------------- /typescript/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "trailing-comma": [ 4 | true, 5 | { 6 | "multiline": "never", 7 | "singleline": "never" 8 | } 9 | ], 10 | "quotemark": [ 11 | true, 12 | "single" 13 | ], 14 | "class-name": true, 15 | "interface-name": [ 16 | true, 17 | "always-prefix" 18 | ], 19 | "no-consecutive-blank-lines": true 20 | } 21 | } --------------------------------------------------------------------------------