├── .editorconfig ├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── deploy-gh-pages.yml │ └── lint-and-test.yml ├── .gitignore ├── .np-config.json ├── .npmignore ├── .vscode └── tasks.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example ├── README.md ├── pagila-schema.sql └── pagila │ ├── actor.ts │ ├── actorInfo.ts │ ├── address.ts │ ├── category.ts │ ├── city.ts │ ├── country.ts │ ├── customer.ts │ ├── customerList.ts │ ├── film.ts │ ├── filmActor.ts │ ├── filmCategory.ts │ ├── filmList.ts │ ├── index.ts │ ├── inventory.ts │ ├── language.ts │ ├── nicerButSlowerFilmList.ts │ ├── payment.ts │ ├── paymentP2020_01.ts │ ├── paymentP2020_02.ts │ ├── paymentP2020_03.ts │ ├── paymentP2020_04.ts │ ├── paymentP2020_05.ts │ ├── paymentP2020_06.ts │ ├── rental.ts │ ├── salesByFilmCategory.ts │ ├── salesByStore.ts │ ├── staff.ts │ ├── staffList.ts │ └── store.ts ├── jest.config.js ├── package.json ├── renovate.json ├── src ├── index.spec.ts ├── index.ts └── lib │ └── createPool.ts ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | max_line_length = 0 14 | trim_trailing_whitespace = true 15 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-undef 2 | module.exports = { 3 | root: true, 4 | parser: '@typescript-eslint/parser', 5 | plugins: [ 6 | '@typescript-eslint', 7 | ], 8 | extends: [ 9 | 'eslint:recommended', 10 | 'plugin:@typescript-eslint/recommended', 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected Behavior 2 | 3 | 4 | ## Actual Behavior 5 | 6 | 7 | ## Steps to Reproduce the Problem 8 | 9 | 1. 10 | 1. 11 | 1. 12 | 13 | ## Specifications 14 | 15 | - Version: 16 | - Platform: 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | > It's a good idea to open an issue first for discussion. 4 | 5 | - [ ] Tests pass 6 | - [ ] Appropriate changes to README included in the PR 7 | -------------------------------------------------------------------------------- /.github/workflows/deploy-gh-pages.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Builds the docs and deploys to GitHub pages 3 | # 4 | # https://github.com/actions/setup-node 5 | # Using https://github.com/marketplace/actions/deploy-to-github-pages 6 | name: Deploy to Github pages 7 | 8 | on: 9 | push: 10 | #tags: 11 | # - v* 12 | branches: 13 | - main 14 | 15 | jobs: 16 | deploy_pages: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: actions/setup-node@v2 22 | with: 23 | node-version: "14" 24 | cache: "yarn" 25 | - run: yarn install 26 | - run: yarn docs 27 | 28 | - run: touch docs/.nojekyll 29 | - name: Deploy docs 🚀 30 | uses: JamesIves/github-pages-deploy-action@releases/v3 31 | with: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | BRANCH: gh-pages # The branch the action should deploy to. 34 | FOLDER: docs # The folder the action should deploy. 35 | -------------------------------------------------------------------------------- /.github/workflows/lint-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Lint and test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build_and_test: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | nodejs: [14, 16] 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | # https://github.com/actions/setup-node 17 | - uses: actions/setup-node@v2-beta 18 | with: 19 | node-version: ${{ matrix.nodejs }} 20 | 21 | - run: yarn install 22 | - run: yarn test 23 | - run: yarn lint 24 | - run: yarn build 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /build/ 3 | /lib/ 4 | /dist/ 5 | /docs/ 6 | .idea/* 7 | 8 | .DS_Store 9 | coverage 10 | *.log 11 | .env 12 | -------------------------------------------------------------------------------- /.np-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "yarn": true, 3 | "branch": "main" 4 | } 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /.github/ 2 | /.vscode/ 3 | /node_modules/ 4 | /build/ 5 | /tmp/ 6 | .idea/* 7 | /docs/ 8 | 9 | coverage 10 | *.log 11 | .gitlab-ci.yml 12 | 13 | package-lock.json 14 | /*.tgz 15 | /tmp* 16 | /mnt/ 17 | /package/ 18 | /example 19 | /src 20 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "build", 7 | "group": { 8 | "kind": "build", 9 | "isDefault": true 10 | }, 11 | "problemMatcher": [], 12 | "label": "npm: build", 13 | "detail": "tsc -p tsconfig.json" 14 | }, 15 | { 16 | "type": "npm", 17 | "script": "test", 18 | "group": { 19 | "kind": "test", 20 | "isDefault": true 21 | }, 22 | "problemMatcher": [], 23 | "label": "npm: test", 24 | "detail": "jest" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v1.0.0 4 | 5 | ### Added or Changed 6 | - Added this changelog :) 7 | - Created documentation site 8 | - Added a default command to transform a PostgreSQL schema into Zod validators/types 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Guzmán Monné 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | [![Contributors][contributors-shield]][contributors-url] 4 | [![Forks][forks-shield]][forks-url] 5 | [![Stargazers][stars-shield]][stars-url] 6 | [![Issues][issues-shield]][issues-url] 7 | [![MIT License][license-shield]][license-url] 8 | 9 | 10 | 11 | 12 |
13 |
14 | 15 | Logo 16 | 17 | 18 |

pgzod

19 | 20 |

21 | Transform PostgreSQL schemas into Zod validators and types 22 |
23 | Explore the docs » 24 | 25 |
26 | 27 | 28 | Report Bug 29 | · 30 | Request Feature 31 |

32 |
33 | 34 | 35 |

Table of Contents

36 | 37 | 38 | - [1. About The Project](#1-about-the-project) 39 | - [1.1. Built With](#11-built-with) 40 | - [2. Getting Started](#2-getting-started) 41 | - [3. Usage](#3-usage) 42 | - [4. Roadmap](#4-roadmap) 43 | - [5. Contributing](#5-contributing) 44 | - [6. License](#6-license) 45 | - [7. Contact](#7-contact) 46 | 47 | 48 | ## 1. About The Project 49 | 50 | Keeping Typescript types in sync with your PostgreSQL database schemas is very useful but challenging. 51 | With PGZod that you can maintain all your tables in sync with Typescript through 52 | ["Zod"][zod]. Zod is, in their own words: 53 | 54 | > a TypeScript-first schema declaration and validation library. You can use a Zod schema for validation or as a normal type. 55 | 56 | It works great when you couple it with a PostgreSQL client like [slonik][slonik]. 57 | 58 |

(back to top)

59 | 60 | ### 1.1. Built With 61 | 62 | * [Slonik][slonik] 63 | * [Typescript][typescript] 64 | * [Yargs][yargs] 65 | * [Zod][zod] 66 | 67 |

(back to top)

68 | 69 | 70 | ## 2. Getting Started 71 | 72 | The best way to run PGZod is by installing it globally or using it through `npx`. 73 | 74 | * npm 75 | ```sh 76 | npm install pgzod -g 77 | ``` 78 | * yarn 79 | ```sh 80 | yarn global add pgzod 81 | ``` 82 | 83 | If you want to use `npx`, you don't have to install PGZod run: 84 | 85 | ```sh 86 | # Shows the command help. 87 | npx pgzod --help 88 | ``` 89 | 90 |

(back to top)

91 | 92 | 93 | ## 3. Usage 94 | 95 | You can use PGZod from the command line. It needs the credentials and the address of the live 96 | database from where to read the schema. You can provide this information through command options or 97 | environment variables. All the [variables from PostgreSQL][postgresql-env-vars] are supported. 98 | 99 | Your database credentials could be stored on a local `.env` file, or even better, on a secret 100 | manager. You then load the credentials to your current session and run the PGZod command. 101 | 102 | For example, you could build the following `.env` file: 103 | 104 | ```ini 105 | PGHOST=some.postgresql.host 106 | PGPORT=12345 107 | PGDATABASE=app 108 | PGSSLMODE=require 109 | PGUSER=postgres 110 | PGPASSWORD=yourpassword! 111 | ``` 112 | 113 | Load the `.env` variables, and override others through command options. 114 | 115 | ```sh 116 | env $(xargs < .env) pgzod --pghost 127.0.0.1 --pgport 5432 --schema public 117 | ``` 118 | 119 | > PGZod will not look for tables on your whole database just a single `schema`. You can 120 | > indicate your database `schema` through the`--schema` option. If you don't provide it, PGZod 121 | > runs against the `public` schemas. 122 | 123 | _For more examples, please refer to the [Documentation][docs]_ 124 | 125 | ### Strategies 126 | 127 | PGZod offers the concept of `strategies` to define how it should create the `Zod` validator files. Here 128 | is the current list of strategies exposed: 129 | 130 | | Value | Description | 131 | | --- | --- | 132 | | `write` | Creates `Zod` validators considering only writes to the table. Columns that have default values will be marked as `optional`. | 133 | | `readwrite` | Creates `Zod` validators for both read and write actions. PGZod will create two validators for each `table` with the `Read` and `Write` suffix to differentiate both actions. **This is probably the strategy you want to work with**. | 134 | 135 | 136 |

(back to top)

137 | 138 | 139 | ## 4. Roadmap 140 | 141 | - Find a better solution to deal with dates, and currently, we treat them as strings. 142 | - Add support for more types. 143 | - Allow the user to override the current type mappings. 144 | - Add tests. 145 | 146 | See the [open issues](https://github.com/owncoral/pgzod/issues) for a complete list of proposed features (and known issues). 147 | 148 |

(back to top)

149 | 150 | 151 | ## 5. Contributing 152 | 153 | Your contributions make the open-source community a fantastic place to learn, inspire, and create. 154 | 155 | To suggest how to improve this project, please fork the repo and create a pull request. 156 | 157 | You can also create an issue with the tag "enhancement." 158 | 159 | Don't forget to give the project a star! Thanks again! 160 | 161 | 1. Fork the Project 162 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 163 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 164 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 165 | 5. Open a Pull Request 166 | 167 |

(back to top)

168 | 169 | 170 | ## 6. License 171 | 172 | Distributed under the MIT License. See `LICENSE.txt` for more information. 173 | 174 |

(back to top)

175 | 176 | 177 | ## 7. Contact 178 | 179 | Your Name - [@owncoral](https://twitter.com/owncoral) - support@owncoral.com 180 | 181 | Project Link: [https://github.com/owncoral/pgzod](https://github.com/owncoral/pgzod) 182 | 183 |

(back to top)

184 | 185 | 186 | 187 | [contributors-shield]: https://img.shields.io/github/contributors/owncoral/pgzod.svg?style=for-the-badge 188 | [contributors-url]: https://github.com/owncoral/pgzod/graphs/contributors 189 | [docs]: https://github.com/owncoral/pgzod 190 | [forks-shield]: https://img.shields.io/github/forks/owncoral/pgzod.svg?style=for-the-badge 191 | [forks-url]: https://github.com/owncoral/pgzod/network/members 192 | [issues-shield]: https://img.shields.io/github/issues/owncoral/pgzod.svg?style=for-the-badge 193 | [issues-url]: https://github.com/owncoral/pgzod/issues 194 | [license-shield]: https://img.shields.io/github/license/owncoral/pgzod.svg?style=for-the-badge 195 | [license-url]: https://github.com/owncoral/pgzod/blob/master/LICENSE.txt 196 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 197 | [postgresql-env-vars]: https://www.postgresql.org/docs/current/libpq-envars.html 198 | [slonik]: https://www.npmjs.com/package/slonik 199 | [stars-shield]: https://img.shields.io/github/stars/owncoral/pgzod.svg?style=for-the-badge 200 | [stars-url]: https://github.com/owncoral/pgzod/stargazers 201 | [typescript]: https://typescript.com 202 | [yargs]: https://www.npmjs.com/package/yargs 203 | [zod]: https://www.npmjs.com/package/zod 204 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | Here is the result of running `pgzod` against the [`pagila schema`][pagila]. 4 | 5 | # Procedure 6 | 7 | > Before yoy begin, load all the connection details as valid [PostgreSQL environment variables][postgresql-env-vars]. 8 | 9 | Create a new database and load the [schema][pagila] into it. 10 | 11 | ```sh 12 | # Create the pagila database 13 | psql -c "CREATE DATABASE pagila;" 14 | 15 | # Load the pagila-schema 16 | PGDATABASE=pagila psql -f pagila-schema.sql 17 | ``` 18 | 19 | Then run `pgzod` with the output folder set to `./pagila`. 20 | 21 | ```sh 22 | npx pgzod --output=./pagila --pgdatabase=pagila 23 | ``` 24 | 25 | That's it. Enjoy your types. 26 | 27 | [pagila]: https://github.com/devrimgunduz/pagila/blob/master/pagila-schema.sql 28 | [postgresql-env-vars]: https://www.postgresql.org/docs/current/libpq-envars.html 29 | -------------------------------------------------------------------------------- /example/pagila-schema.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- PostgreSQL database dump 3 | -- 4 | 5 | -- Dumped from database version 13beta3 6 | -- Dumped by pg_dump version 13beta3 7 | 8 | SET statement_timeout = 0; 9 | SET lock_timeout = 0; 10 | SET idle_in_transaction_session_timeout = 0; 11 | SET client_encoding = 'UTF8'; 12 | SET standard_conforming_strings = on; 13 | SELECT pg_catalog.set_config('search_path', '', false); 14 | SET check_function_bodies = false; 15 | SET xmloption = content; 16 | SET client_min_messages = warning; 17 | SET row_security = off; 18 | 19 | -- 20 | -- Name: mpaa_rating; Type: TYPE; Schema: public; Owner: postgres 21 | -- 22 | 23 | CREATE TYPE public.mpaa_rating AS ENUM ( 24 | 'G', 25 | 'PG', 26 | 'PG-13', 27 | 'R', 28 | 'NC-17' 29 | ); 30 | 31 | 32 | ALTER TYPE public.mpaa_rating OWNER TO postgres; 33 | 34 | -- 35 | -- Name: year; Type: DOMAIN; Schema: public; Owner: postgres 36 | -- 37 | 38 | CREATE DOMAIN public.year AS integer 39 | CONSTRAINT year_check CHECK (((VALUE >= 1901) AND (VALUE <= 2155))); 40 | 41 | 42 | ALTER DOMAIN public.year OWNER TO postgres; 43 | 44 | -- 45 | -- Name: _group_concat(text, text); Type: FUNCTION; Schema: public; Owner: postgres 46 | -- 47 | 48 | CREATE FUNCTION public._group_concat(text, text) RETURNS text 49 | LANGUAGE sql IMMUTABLE 50 | AS $_$ 51 | SELECT CASE 52 | WHEN $2 IS NULL THEN $1 53 | WHEN $1 IS NULL THEN $2 54 | ELSE $1 || ', ' || $2 55 | END 56 | $_$; 57 | 58 | 59 | ALTER FUNCTION public._group_concat(text, text) OWNER TO postgres; 60 | 61 | -- 62 | -- Name: film_in_stock(integer, integer); Type: FUNCTION; Schema: public; Owner: postgres 63 | -- 64 | 65 | CREATE FUNCTION public.film_in_stock(p_film_id integer, p_store_id integer, OUT p_film_count integer) RETURNS SETOF integer 66 | LANGUAGE sql 67 | AS $_$ 68 | SELECT inventory_id 69 | FROM inventory 70 | WHERE film_id = $1 71 | AND store_id = $2 72 | AND inventory_in_stock(inventory_id); 73 | $_$; 74 | 75 | 76 | ALTER FUNCTION public.film_in_stock(p_film_id integer, p_store_id integer, OUT p_film_count integer) OWNER TO postgres; 77 | 78 | -- 79 | -- Name: film_not_in_stock(integer, integer); Type: FUNCTION; Schema: public; Owner: postgres 80 | -- 81 | 82 | CREATE FUNCTION public.film_not_in_stock(p_film_id integer, p_store_id integer, OUT p_film_count integer) RETURNS SETOF integer 83 | LANGUAGE sql 84 | AS $_$ 85 | SELECT inventory_id 86 | FROM inventory 87 | WHERE film_id = $1 88 | AND store_id = $2 89 | AND NOT inventory_in_stock(inventory_id); 90 | $_$; 91 | 92 | 93 | ALTER FUNCTION public.film_not_in_stock(p_film_id integer, p_store_id integer, OUT p_film_count integer) OWNER TO postgres; 94 | 95 | -- 96 | -- Name: get_customer_balance(integer, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: postgres 97 | -- 98 | 99 | CREATE FUNCTION public.get_customer_balance(p_customer_id integer, p_effective_date timestamp with time zone) RETURNS numeric 100 | LANGUAGE plpgsql 101 | AS $$ 102 | --#OK, WE NEED TO CALCULATE THE CURRENT BALANCE GIVEN A CUSTOMER_ID AND A DATE 103 | --#THAT WE WANT THE BALANCE TO BE EFFECTIVE FOR. THE BALANCE IS: 104 | --# 1) RENTAL FEES FOR ALL PREVIOUS RENTALS 105 | --# 2) ONE DOLLAR FOR EVERY DAY THE PREVIOUS RENTALS ARE OVERDUE 106 | --# 3) IF A FILM IS MORE THAN RENTAL_DURATION * 2 OVERDUE, CHARGE THE REPLACEMENT_COST 107 | --# 4) SUBTRACT ALL PAYMENTS MADE BEFORE THE DATE SPECIFIED 108 | DECLARE 109 | v_rentfees DECIMAL(5,2); --#FEES PAID TO RENT THE VIDEOS INITIALLY 110 | v_overfees INTEGER; --#LATE FEES FOR PRIOR RENTALS 111 | v_payments DECIMAL(5,2); --#SUM OF PAYMENTS MADE PREVIOUSLY 112 | BEGIN 113 | SELECT COALESCE(SUM(film.rental_rate),0) INTO v_rentfees 114 | FROM film, inventory, rental 115 | WHERE film.film_id = inventory.film_id 116 | AND inventory.inventory_id = rental.inventory_id 117 | AND rental.rental_date <= p_effective_date 118 | AND rental.customer_id = p_customer_id; 119 | 120 | SELECT COALESCE(SUM(IF((rental.return_date - rental.rental_date) > (film.rental_duration * '1 day'::interval), 121 | ((rental.return_date - rental.rental_date) - (film.rental_duration * '1 day'::interval)),0)),0) INTO v_overfees 122 | FROM rental, inventory, film 123 | WHERE film.film_id = inventory.film_id 124 | AND inventory.inventory_id = rental.inventory_id 125 | AND rental.rental_date <= p_effective_date 126 | AND rental.customer_id = p_customer_id; 127 | 128 | SELECT COALESCE(SUM(payment.amount),0) INTO v_payments 129 | FROM payment 130 | WHERE payment.payment_date <= p_effective_date 131 | AND payment.customer_id = p_customer_id; 132 | 133 | RETURN v_rentfees + v_overfees - v_payments; 134 | END 135 | $$; 136 | 137 | 138 | ALTER FUNCTION public.get_customer_balance(p_customer_id integer, p_effective_date timestamp with time zone) OWNER TO postgres; 139 | 140 | -- 141 | -- Name: inventory_held_by_customer(integer); Type: FUNCTION; Schema: public; Owner: postgres 142 | -- 143 | 144 | CREATE FUNCTION public.inventory_held_by_customer(p_inventory_id integer) RETURNS integer 145 | LANGUAGE plpgsql 146 | AS $$ 147 | DECLARE 148 | v_customer_id INTEGER; 149 | BEGIN 150 | 151 | SELECT customer_id INTO v_customer_id 152 | FROM rental 153 | WHERE return_date IS NULL 154 | AND inventory_id = p_inventory_id; 155 | 156 | RETURN v_customer_id; 157 | END $$; 158 | 159 | 160 | ALTER FUNCTION public.inventory_held_by_customer(p_inventory_id integer) OWNER TO postgres; 161 | 162 | -- 163 | -- Name: inventory_in_stock(integer); Type: FUNCTION; Schema: public; Owner: postgres 164 | -- 165 | 166 | CREATE FUNCTION public.inventory_in_stock(p_inventory_id integer) RETURNS boolean 167 | LANGUAGE plpgsql 168 | AS $$ 169 | DECLARE 170 | v_rentals INTEGER; 171 | v_out INTEGER; 172 | BEGIN 173 | -- AN ITEM IS IN-STOCK IF THERE ARE EITHER NO ROWS IN THE rental TABLE 174 | -- FOR THE ITEM OR ALL ROWS HAVE return_date POPULATED 175 | 176 | SELECT count(*) INTO v_rentals 177 | FROM rental 178 | WHERE inventory_id = p_inventory_id; 179 | 180 | IF v_rentals = 0 THEN 181 | RETURN TRUE; 182 | END IF; 183 | 184 | SELECT COUNT(rental_id) INTO v_out 185 | FROM inventory LEFT JOIN rental USING(inventory_id) 186 | WHERE inventory.inventory_id = p_inventory_id 187 | AND rental.return_date IS NULL; 188 | 189 | IF v_out > 0 THEN 190 | RETURN FALSE; 191 | ELSE 192 | RETURN TRUE; 193 | END IF; 194 | END $$; 195 | 196 | 197 | ALTER FUNCTION public.inventory_in_stock(p_inventory_id integer) OWNER TO postgres; 198 | 199 | -- 200 | -- Name: last_day(timestamp with time zone); Type: FUNCTION; Schema: public; Owner: postgres 201 | -- 202 | 203 | CREATE FUNCTION public.last_day(timestamp with time zone) RETURNS date 204 | LANGUAGE sql IMMUTABLE STRICT 205 | AS $_$ 206 | SELECT CASE 207 | WHEN EXTRACT(MONTH FROM $1) = 12 THEN 208 | (((EXTRACT(YEAR FROM $1) + 1) operator(pg_catalog.||) '-01-01')::date - INTERVAL '1 day')::date 209 | ELSE 210 | ((EXTRACT(YEAR FROM $1) operator(pg_catalog.||) '-' operator(pg_catalog.||) (EXTRACT(MONTH FROM $1) + 1) operator(pg_catalog.||) '-01')::date - INTERVAL '1 day')::date 211 | END 212 | $_$; 213 | 214 | 215 | ALTER FUNCTION public.last_day(timestamp with time zone) OWNER TO postgres; 216 | 217 | -- 218 | -- Name: last_updated(); Type: FUNCTION; Schema: public; Owner: postgres 219 | -- 220 | 221 | CREATE FUNCTION public.last_updated() RETURNS trigger 222 | LANGUAGE plpgsql 223 | AS $$ 224 | BEGIN 225 | NEW.last_update = CURRENT_TIMESTAMP; 226 | RETURN NEW; 227 | END $$; 228 | 229 | 230 | ALTER FUNCTION public.last_updated() OWNER TO postgres; 231 | 232 | -- 233 | -- Name: customer_customer_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 234 | -- 235 | 236 | CREATE SEQUENCE public.customer_customer_id_seq 237 | START WITH 1 238 | INCREMENT BY 1 239 | NO MINVALUE 240 | NO MAXVALUE 241 | CACHE 1; 242 | 243 | 244 | ALTER TABLE public.customer_customer_id_seq OWNER TO postgres; 245 | 246 | SET default_tablespace = ''; 247 | 248 | SET default_table_access_method = heap; 249 | 250 | -- 251 | -- Name: customer; Type: TABLE; Schema: public; Owner: postgres 252 | -- 253 | 254 | CREATE TABLE public.customer ( 255 | customer_id integer DEFAULT nextval('public.customer_customer_id_seq'::regclass) NOT NULL, 256 | store_id integer NOT NULL, 257 | first_name text NOT NULL, 258 | last_name text NOT NULL, 259 | email text, 260 | address_id integer NOT NULL, 261 | activebool boolean DEFAULT true NOT NULL, 262 | create_date date DEFAULT CURRENT_DATE NOT NULL, 263 | last_update timestamp with time zone DEFAULT now(), 264 | active integer 265 | ); 266 | 267 | 268 | ALTER TABLE public.customer OWNER TO postgres; 269 | 270 | -- 271 | -- Name: rewards_report(integer, numeric); Type: FUNCTION; Schema: public; Owner: postgres 272 | -- 273 | 274 | CREATE FUNCTION public.rewards_report(min_monthly_purchases integer, min_dollar_amount_purchased numeric) RETURNS SETOF public.customer 275 | LANGUAGE plpgsql SECURITY DEFINER 276 | AS $_$ 277 | DECLARE 278 | last_month_start DATE; 279 | last_month_end DATE; 280 | rr RECORD; 281 | tmpSQL TEXT; 282 | BEGIN 283 | 284 | /* Some sanity checks... */ 285 | IF min_monthly_purchases = 0 THEN 286 | RAISE EXCEPTION 'Minimum monthly purchases parameter must be > 0'; 287 | END IF; 288 | IF min_dollar_amount_purchased = 0.00 THEN 289 | RAISE EXCEPTION 'Minimum monthly dollar amount purchased parameter must be > $0.00'; 290 | END IF; 291 | 292 | last_month_start := CURRENT_DATE - '3 month'::interval; 293 | last_month_start := to_date((extract(YEAR FROM last_month_start) || '-' || extract(MONTH FROM last_month_start) || '-01'),'YYYY-MM-DD'); 294 | last_month_end := LAST_DAY(last_month_start); 295 | 296 | /* 297 | Create a temporary storage area for Customer IDs. 298 | */ 299 | CREATE TEMPORARY TABLE tmpCustomer (customer_id INTEGER NOT NULL PRIMARY KEY); 300 | 301 | /* 302 | Find all customers meeting the monthly purchase requirements 303 | */ 304 | 305 | tmpSQL := 'INSERT INTO tmpCustomer (customer_id) 306 | SELECT p.customer_id 307 | FROM payment AS p 308 | WHERE DATE(p.payment_date) BETWEEN '||quote_literal(last_month_start) ||' AND '|| quote_literal(last_month_end) || ' 309 | GROUP BY customer_id 310 | HAVING SUM(p.amount) > '|| min_dollar_amount_purchased || ' 311 | AND COUNT(customer_id) > ' ||min_monthly_purchases ; 312 | 313 | EXECUTE tmpSQL; 314 | 315 | /* 316 | Output ALL customer information of matching rewardees. 317 | Customize output as needed. 318 | */ 319 | FOR rr IN EXECUTE 'SELECT c.* FROM tmpCustomer AS t INNER JOIN customer AS c ON t.customer_id = c.customer_id' LOOP 320 | RETURN NEXT rr; 321 | END LOOP; 322 | 323 | /* Clean up */ 324 | tmpSQL := 'DROP TABLE tmpCustomer'; 325 | EXECUTE tmpSQL; 326 | 327 | RETURN; 328 | END 329 | $_$; 330 | 331 | 332 | ALTER FUNCTION public.rewards_report(min_monthly_purchases integer, min_dollar_amount_purchased numeric) OWNER TO postgres; 333 | 334 | -- 335 | -- Name: group_concat(text); Type: AGGREGATE; Schema: public; Owner: postgres 336 | -- 337 | 338 | CREATE AGGREGATE public.group_concat(text) ( 339 | SFUNC = public._group_concat, 340 | STYPE = text 341 | ); 342 | 343 | 344 | ALTER AGGREGATE public.group_concat(text) OWNER TO postgres; 345 | 346 | -- 347 | -- Name: actor_actor_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 348 | -- 349 | 350 | CREATE SEQUENCE public.actor_actor_id_seq 351 | START WITH 1 352 | INCREMENT BY 1 353 | NO MINVALUE 354 | NO MAXVALUE 355 | CACHE 1; 356 | 357 | 358 | ALTER TABLE public.actor_actor_id_seq OWNER TO postgres; 359 | 360 | -- 361 | -- Name: actor; Type: TABLE; Schema: public; Owner: postgres 362 | -- 363 | 364 | CREATE TABLE public.actor ( 365 | actor_id integer DEFAULT nextval('public.actor_actor_id_seq'::regclass) NOT NULL, 366 | first_name text NOT NULL, 367 | last_name text NOT NULL, 368 | last_update timestamp with time zone DEFAULT now() NOT NULL 369 | ); 370 | 371 | 372 | ALTER TABLE public.actor OWNER TO postgres; 373 | 374 | -- 375 | -- Name: category_category_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 376 | -- 377 | 378 | CREATE SEQUENCE public.category_category_id_seq 379 | START WITH 1 380 | INCREMENT BY 1 381 | NO MINVALUE 382 | NO MAXVALUE 383 | CACHE 1; 384 | 385 | 386 | ALTER TABLE public.category_category_id_seq OWNER TO postgres; 387 | 388 | -- 389 | -- Name: category; Type: TABLE; Schema: public; Owner: postgres 390 | -- 391 | 392 | CREATE TABLE public.category ( 393 | category_id integer DEFAULT nextval('public.category_category_id_seq'::regclass) NOT NULL, 394 | name text NOT NULL, 395 | last_update timestamp with time zone DEFAULT now() NOT NULL 396 | ); 397 | 398 | 399 | ALTER TABLE public.category OWNER TO postgres; 400 | 401 | -- 402 | -- Name: film_film_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 403 | -- 404 | 405 | CREATE SEQUENCE public.film_film_id_seq 406 | START WITH 1 407 | INCREMENT BY 1 408 | NO MINVALUE 409 | NO MAXVALUE 410 | CACHE 1; 411 | 412 | 413 | ALTER TABLE public.film_film_id_seq OWNER TO postgres; 414 | 415 | -- 416 | -- Name: film; Type: TABLE; Schema: public; Owner: postgres 417 | -- 418 | 419 | CREATE TABLE public.film ( 420 | film_id integer DEFAULT nextval('public.film_film_id_seq'::regclass) NOT NULL, 421 | title text NOT NULL, 422 | description text, 423 | release_year public.year, 424 | language_id integer NOT NULL, 425 | original_language_id integer, 426 | rental_duration smallint DEFAULT 3 NOT NULL, 427 | rental_rate numeric(4,2) DEFAULT 4.99 NOT NULL, 428 | length smallint, 429 | replacement_cost numeric(5,2) DEFAULT 19.99 NOT NULL, 430 | rating public.mpaa_rating DEFAULT 'G'::public.mpaa_rating, 431 | last_update timestamp with time zone DEFAULT now() NOT NULL, 432 | special_features text[], 433 | fulltext tsvector NOT NULL 434 | ); 435 | 436 | 437 | ALTER TABLE public.film OWNER TO postgres; 438 | 439 | -- 440 | -- Name: film_actor; Type: TABLE; Schema: public; Owner: postgres 441 | -- 442 | 443 | CREATE TABLE public.film_actor ( 444 | actor_id integer NOT NULL, 445 | film_id integer NOT NULL, 446 | last_update timestamp with time zone DEFAULT now() NOT NULL 447 | ); 448 | 449 | 450 | ALTER TABLE public.film_actor OWNER TO postgres; 451 | 452 | -- 453 | -- Name: film_category; Type: TABLE; Schema: public; Owner: postgres 454 | -- 455 | 456 | CREATE TABLE public.film_category ( 457 | film_id integer NOT NULL, 458 | category_id integer NOT NULL, 459 | last_update timestamp with time zone DEFAULT now() NOT NULL 460 | ); 461 | 462 | 463 | ALTER TABLE public.film_category OWNER TO postgres; 464 | 465 | -- 466 | -- Name: actor_info; Type: VIEW; Schema: public; Owner: postgres 467 | -- 468 | 469 | CREATE VIEW public.actor_info AS 470 | SELECT a.actor_id, 471 | a.first_name, 472 | a.last_name, 473 | public.group_concat(DISTINCT ((c.name || ': '::text) || ( SELECT public.group_concat(f.title) AS group_concat 474 | FROM ((public.film f 475 | JOIN public.film_category fc_1 ON ((f.film_id = fc_1.film_id))) 476 | JOIN public.film_actor fa_1 ON ((f.film_id = fa_1.film_id))) 477 | WHERE ((fc_1.category_id = c.category_id) AND (fa_1.actor_id = a.actor_id)) 478 | GROUP BY fa_1.actor_id))) AS film_info 479 | FROM (((public.actor a 480 | LEFT JOIN public.film_actor fa ON ((a.actor_id = fa.actor_id))) 481 | LEFT JOIN public.film_category fc ON ((fa.film_id = fc.film_id))) 482 | LEFT JOIN public.category c ON ((fc.category_id = c.category_id))) 483 | GROUP BY a.actor_id, a.first_name, a.last_name; 484 | 485 | 486 | ALTER TABLE public.actor_info OWNER TO postgres; 487 | 488 | -- 489 | -- Name: address_address_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 490 | -- 491 | 492 | CREATE SEQUENCE public.address_address_id_seq 493 | START WITH 1 494 | INCREMENT BY 1 495 | NO MINVALUE 496 | NO MAXVALUE 497 | CACHE 1; 498 | 499 | 500 | ALTER TABLE public.address_address_id_seq OWNER TO postgres; 501 | 502 | -- 503 | -- Name: address; Type: TABLE; Schema: public; Owner: postgres 504 | -- 505 | 506 | CREATE TABLE public.address ( 507 | address_id integer DEFAULT nextval('public.address_address_id_seq'::regclass) NOT NULL, 508 | address text NOT NULL, 509 | address2 text, 510 | district text NOT NULL, 511 | city_id integer NOT NULL, 512 | postal_code text, 513 | phone text NOT NULL, 514 | last_update timestamp with time zone DEFAULT now() NOT NULL 515 | ); 516 | 517 | 518 | ALTER TABLE public.address OWNER TO postgres; 519 | 520 | -- 521 | -- Name: city_city_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 522 | -- 523 | 524 | CREATE SEQUENCE public.city_city_id_seq 525 | START WITH 1 526 | INCREMENT BY 1 527 | NO MINVALUE 528 | NO MAXVALUE 529 | CACHE 1; 530 | 531 | 532 | ALTER TABLE public.city_city_id_seq OWNER TO postgres; 533 | 534 | -- 535 | -- Name: city; Type: TABLE; Schema: public; Owner: postgres 536 | -- 537 | 538 | CREATE TABLE public.city ( 539 | city_id integer DEFAULT nextval('public.city_city_id_seq'::regclass) NOT NULL, 540 | city text NOT NULL, 541 | country_id integer NOT NULL, 542 | last_update timestamp with time zone DEFAULT now() NOT NULL 543 | ); 544 | 545 | 546 | ALTER TABLE public.city OWNER TO postgres; 547 | 548 | -- 549 | -- Name: country_country_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 550 | -- 551 | 552 | CREATE SEQUENCE public.country_country_id_seq 553 | START WITH 1 554 | INCREMENT BY 1 555 | NO MINVALUE 556 | NO MAXVALUE 557 | CACHE 1; 558 | 559 | 560 | ALTER TABLE public.country_country_id_seq OWNER TO postgres; 561 | 562 | -- 563 | -- Name: country; Type: TABLE; Schema: public; Owner: postgres 564 | -- 565 | 566 | CREATE TABLE public.country ( 567 | country_id integer DEFAULT nextval('public.country_country_id_seq'::regclass) NOT NULL, 568 | country text NOT NULL, 569 | last_update timestamp with time zone DEFAULT now() NOT NULL 570 | ); 571 | 572 | 573 | ALTER TABLE public.country OWNER TO postgres; 574 | 575 | -- 576 | -- Name: customer_list; Type: VIEW; Schema: public; Owner: postgres 577 | -- 578 | 579 | CREATE VIEW public.customer_list AS 580 | SELECT cu.customer_id AS id, 581 | ((cu.first_name || ' '::text) || cu.last_name) AS name, 582 | a.address, 583 | a.postal_code AS "zip code", 584 | a.phone, 585 | city.city, 586 | country.country, 587 | CASE 588 | WHEN cu.activebool THEN 'active'::text 589 | ELSE ''::text 590 | END AS notes, 591 | cu.store_id AS sid 592 | FROM (((public.customer cu 593 | JOIN public.address a ON ((cu.address_id = a.address_id))) 594 | JOIN public.city ON ((a.city_id = city.city_id))) 595 | JOIN public.country ON ((city.country_id = country.country_id))); 596 | 597 | 598 | ALTER TABLE public.customer_list OWNER TO postgres; 599 | 600 | -- 601 | -- Name: film_list; Type: VIEW; Schema: public; Owner: postgres 602 | -- 603 | 604 | CREATE VIEW public.film_list AS 605 | SELECT film.film_id AS fid, 606 | film.title, 607 | film.description, 608 | category.name AS category, 609 | film.rental_rate AS price, 610 | film.length, 611 | film.rating, 612 | public.group_concat(((actor.first_name || ' '::text) || actor.last_name)) AS actors 613 | FROM ((((public.category 614 | LEFT JOIN public.film_category ON ((category.category_id = film_category.category_id))) 615 | LEFT JOIN public.film ON ((film_category.film_id = film.film_id))) 616 | JOIN public.film_actor ON ((film.film_id = film_actor.film_id))) 617 | JOIN public.actor ON ((film_actor.actor_id = actor.actor_id))) 618 | GROUP BY film.film_id, film.title, film.description, category.name, film.rental_rate, film.length, film.rating; 619 | 620 | 621 | ALTER TABLE public.film_list OWNER TO postgres; 622 | 623 | -- 624 | -- Name: inventory_inventory_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 625 | -- 626 | 627 | CREATE SEQUENCE public.inventory_inventory_id_seq 628 | START WITH 1 629 | INCREMENT BY 1 630 | NO MINVALUE 631 | NO MAXVALUE 632 | CACHE 1; 633 | 634 | 635 | ALTER TABLE public.inventory_inventory_id_seq OWNER TO postgres; 636 | 637 | -- 638 | -- Name: inventory; Type: TABLE; Schema: public; Owner: postgres 639 | -- 640 | 641 | CREATE TABLE public.inventory ( 642 | inventory_id integer DEFAULT nextval('public.inventory_inventory_id_seq'::regclass) NOT NULL, 643 | film_id integer NOT NULL, 644 | store_id integer NOT NULL, 645 | last_update timestamp with time zone DEFAULT now() NOT NULL 646 | ); 647 | 648 | 649 | ALTER TABLE public.inventory OWNER TO postgres; 650 | 651 | -- 652 | -- Name: language_language_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 653 | -- 654 | 655 | CREATE SEQUENCE public.language_language_id_seq 656 | START WITH 1 657 | INCREMENT BY 1 658 | NO MINVALUE 659 | NO MAXVALUE 660 | CACHE 1; 661 | 662 | 663 | ALTER TABLE public.language_language_id_seq OWNER TO postgres; 664 | 665 | -- 666 | -- Name: language; Type: TABLE; Schema: public; Owner: postgres 667 | -- 668 | 669 | CREATE TABLE public.language ( 670 | language_id integer DEFAULT nextval('public.language_language_id_seq'::regclass) NOT NULL, 671 | name character(20) NOT NULL, 672 | last_update timestamp with time zone DEFAULT now() NOT NULL 673 | ); 674 | 675 | 676 | ALTER TABLE public.language OWNER TO postgres; 677 | 678 | -- 679 | -- Name: nicer_but_slower_film_list; Type: VIEW; Schema: public; Owner: postgres 680 | -- 681 | 682 | CREATE VIEW public.nicer_but_slower_film_list AS 683 | SELECT film.film_id AS fid, 684 | film.title, 685 | film.description, 686 | category.name AS category, 687 | film.rental_rate AS price, 688 | film.length, 689 | film.rating, 690 | public.group_concat((((upper("substring"(actor.first_name, 1, 1)) || lower("substring"(actor.first_name, 2))) || upper("substring"(actor.last_name, 1, 1))) || lower("substring"(actor.last_name, 2)))) AS actors 691 | FROM ((((public.category 692 | LEFT JOIN public.film_category ON ((category.category_id = film_category.category_id))) 693 | LEFT JOIN public.film ON ((film_category.film_id = film.film_id))) 694 | JOIN public.film_actor ON ((film.film_id = film_actor.film_id))) 695 | JOIN public.actor ON ((film_actor.actor_id = actor.actor_id))) 696 | GROUP BY film.film_id, film.title, film.description, category.name, film.rental_rate, film.length, film.rating; 697 | 698 | 699 | ALTER TABLE public.nicer_but_slower_film_list OWNER TO postgres; 700 | 701 | -- 702 | -- Name: payment_payment_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 703 | -- 704 | 705 | CREATE SEQUENCE public.payment_payment_id_seq 706 | START WITH 1 707 | INCREMENT BY 1 708 | NO MINVALUE 709 | NO MAXVALUE 710 | CACHE 1; 711 | 712 | 713 | ALTER TABLE public.payment_payment_id_seq OWNER TO postgres; 714 | 715 | -- 716 | -- Name: payment; Type: TABLE; Schema: public; Owner: postgres 717 | -- 718 | 719 | CREATE TABLE public.payment ( 720 | payment_id integer DEFAULT nextval('public.payment_payment_id_seq'::regclass) NOT NULL, 721 | customer_id integer NOT NULL, 722 | staff_id integer NOT NULL, 723 | rental_id integer NOT NULL, 724 | amount numeric(5,2) NOT NULL, 725 | payment_date timestamp with time zone NOT NULL 726 | ) 727 | PARTITION BY RANGE (payment_date); 728 | 729 | 730 | ALTER TABLE public.payment OWNER TO postgres; 731 | 732 | -- 733 | -- Name: payment_p2020_01; Type: TABLE; Schema: public; Owner: postgres 734 | -- 735 | 736 | CREATE TABLE public.payment_p2020_01 ( 737 | payment_id integer DEFAULT nextval('public.payment_payment_id_seq'::regclass) NOT NULL, 738 | customer_id integer NOT NULL, 739 | staff_id integer NOT NULL, 740 | rental_id integer NOT NULL, 741 | amount numeric(5,2) NOT NULL, 742 | payment_date timestamp with time zone NOT NULL 743 | ); 744 | ALTER TABLE ONLY public.payment ATTACH PARTITION public.payment_p2020_01 FOR VALUES FROM ('2020-01-01 00:00:00+00') TO ('2020-02-01 00:00:00+00'); 745 | 746 | 747 | ALTER TABLE public.payment_p2020_01 OWNER TO postgres; 748 | 749 | -- 750 | -- Name: payment_p2020_02; Type: TABLE; Schema: public; Owner: postgres 751 | -- 752 | 753 | CREATE TABLE public.payment_p2020_02 ( 754 | payment_id integer DEFAULT nextval('public.payment_payment_id_seq'::regclass) NOT NULL, 755 | customer_id integer NOT NULL, 756 | staff_id integer NOT NULL, 757 | rental_id integer NOT NULL, 758 | amount numeric(5,2) NOT NULL, 759 | payment_date timestamp with time zone NOT NULL 760 | ); 761 | ALTER TABLE ONLY public.payment ATTACH PARTITION public.payment_p2020_02 FOR VALUES FROM ('2020-02-01 00:00:00+00') TO ('2020-03-01 00:00:00+00'); 762 | 763 | 764 | ALTER TABLE public.payment_p2020_02 OWNER TO postgres; 765 | 766 | -- 767 | -- Name: payment_p2020_03; Type: TABLE; Schema: public; Owner: postgres 768 | -- 769 | 770 | CREATE TABLE public.payment_p2020_03 ( 771 | payment_id integer DEFAULT nextval('public.payment_payment_id_seq'::regclass) NOT NULL, 772 | customer_id integer NOT NULL, 773 | staff_id integer NOT NULL, 774 | rental_id integer NOT NULL, 775 | amount numeric(5,2) NOT NULL, 776 | payment_date timestamp with time zone NOT NULL 777 | ); 778 | ALTER TABLE ONLY public.payment ATTACH PARTITION public.payment_p2020_03 FOR VALUES FROM ('2020-03-01 00:00:00+00') TO ('2020-04-01 01:00:00+01'); 779 | 780 | 781 | ALTER TABLE public.payment_p2020_03 OWNER TO postgres; 782 | 783 | -- 784 | -- Name: payment_p2020_04; Type: TABLE; Schema: public; Owner: postgres 785 | -- 786 | 787 | CREATE TABLE public.payment_p2020_04 ( 788 | payment_id integer DEFAULT nextval('public.payment_payment_id_seq'::regclass) NOT NULL, 789 | customer_id integer NOT NULL, 790 | staff_id integer NOT NULL, 791 | rental_id integer NOT NULL, 792 | amount numeric(5,2) NOT NULL, 793 | payment_date timestamp with time zone NOT NULL 794 | ); 795 | ALTER TABLE ONLY public.payment ATTACH PARTITION public.payment_p2020_04 FOR VALUES FROM ('2020-04-01 01:00:00+01') TO ('2020-05-01 01:00:00+01'); 796 | 797 | 798 | ALTER TABLE public.payment_p2020_04 OWNER TO postgres; 799 | 800 | -- 801 | -- Name: payment_p2020_05; Type: TABLE; Schema: public; Owner: postgres 802 | -- 803 | 804 | CREATE TABLE public.payment_p2020_05 ( 805 | payment_id integer DEFAULT nextval('public.payment_payment_id_seq'::regclass) NOT NULL, 806 | customer_id integer NOT NULL, 807 | staff_id integer NOT NULL, 808 | rental_id integer NOT NULL, 809 | amount numeric(5,2) NOT NULL, 810 | payment_date timestamp with time zone NOT NULL 811 | ); 812 | ALTER TABLE ONLY public.payment ATTACH PARTITION public.payment_p2020_05 FOR VALUES FROM ('2020-05-01 01:00:00+01') TO ('2020-06-01 01:00:00+01'); 813 | 814 | 815 | ALTER TABLE public.payment_p2020_05 OWNER TO postgres; 816 | 817 | -- 818 | -- Name: payment_p2020_06; Type: TABLE; Schema: public; Owner: postgres 819 | -- 820 | 821 | CREATE TABLE public.payment_p2020_06 ( 822 | payment_id integer DEFAULT nextval('public.payment_payment_id_seq'::regclass) NOT NULL, 823 | customer_id integer NOT NULL, 824 | staff_id integer NOT NULL, 825 | rental_id integer NOT NULL, 826 | amount numeric(5,2) NOT NULL, 827 | payment_date timestamp with time zone NOT NULL 828 | ); 829 | ALTER TABLE ONLY public.payment ATTACH PARTITION public.payment_p2020_06 FOR VALUES FROM ('2020-06-01 01:00:00+01') TO ('2020-07-01 01:00:00+01'); 830 | 831 | 832 | ALTER TABLE public.payment_p2020_06 OWNER TO postgres; 833 | 834 | -- 835 | -- Name: rental_rental_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 836 | -- 837 | 838 | CREATE SEQUENCE public.rental_rental_id_seq 839 | START WITH 1 840 | INCREMENT BY 1 841 | NO MINVALUE 842 | NO MAXVALUE 843 | CACHE 1; 844 | 845 | 846 | ALTER TABLE public.rental_rental_id_seq OWNER TO postgres; 847 | 848 | -- 849 | -- Name: rental; Type: TABLE; Schema: public; Owner: postgres 850 | -- 851 | 852 | CREATE TABLE public.rental ( 853 | rental_id integer DEFAULT nextval('public.rental_rental_id_seq'::regclass) NOT NULL, 854 | rental_date timestamp with time zone NOT NULL, 855 | inventory_id integer NOT NULL, 856 | customer_id integer NOT NULL, 857 | return_date timestamp with time zone, 858 | staff_id integer NOT NULL, 859 | last_update timestamp with time zone DEFAULT now() NOT NULL 860 | ); 861 | 862 | 863 | ALTER TABLE public.rental OWNER TO postgres; 864 | 865 | -- 866 | -- Name: sales_by_film_category; Type: VIEW; Schema: public; Owner: postgres 867 | -- 868 | 869 | CREATE VIEW public.sales_by_film_category AS 870 | SELECT c.name AS category, 871 | sum(p.amount) AS total_sales 872 | FROM (((((public.payment p 873 | JOIN public.rental r ON ((p.rental_id = r.rental_id))) 874 | JOIN public.inventory i ON ((r.inventory_id = i.inventory_id))) 875 | JOIN public.film f ON ((i.film_id = f.film_id))) 876 | JOIN public.film_category fc ON ((f.film_id = fc.film_id))) 877 | JOIN public.category c ON ((fc.category_id = c.category_id))) 878 | GROUP BY c.name 879 | ORDER BY (sum(p.amount)) DESC; 880 | 881 | 882 | ALTER TABLE public.sales_by_film_category OWNER TO postgres; 883 | 884 | -- 885 | -- Name: staff_staff_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 886 | -- 887 | 888 | CREATE SEQUENCE public.staff_staff_id_seq 889 | START WITH 1 890 | INCREMENT BY 1 891 | NO MINVALUE 892 | NO MAXVALUE 893 | CACHE 1; 894 | 895 | 896 | ALTER TABLE public.staff_staff_id_seq OWNER TO postgres; 897 | 898 | -- 899 | -- Name: staff; Type: TABLE; Schema: public; Owner: postgres 900 | -- 901 | 902 | CREATE TABLE public.staff ( 903 | staff_id integer DEFAULT nextval('public.staff_staff_id_seq'::regclass) NOT NULL, 904 | first_name text NOT NULL, 905 | last_name text NOT NULL, 906 | address_id integer NOT NULL, 907 | email text, 908 | store_id integer NOT NULL, 909 | active boolean DEFAULT true NOT NULL, 910 | username text NOT NULL, 911 | password text, 912 | last_update timestamp with time zone DEFAULT now() NOT NULL, 913 | picture bytea 914 | ); 915 | 916 | 917 | ALTER TABLE public.staff OWNER TO postgres; 918 | 919 | -- 920 | -- Name: store_store_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres 921 | -- 922 | 923 | CREATE SEQUENCE public.store_store_id_seq 924 | START WITH 1 925 | INCREMENT BY 1 926 | NO MINVALUE 927 | NO MAXVALUE 928 | CACHE 1; 929 | 930 | 931 | ALTER TABLE public.store_store_id_seq OWNER TO postgres; 932 | 933 | -- 934 | -- Name: store; Type: TABLE; Schema: public; Owner: postgres 935 | -- 936 | 937 | CREATE TABLE public.store ( 938 | store_id integer DEFAULT nextval('public.store_store_id_seq'::regclass) NOT NULL, 939 | manager_staff_id integer NOT NULL, 940 | address_id integer NOT NULL, 941 | last_update timestamp with time zone DEFAULT now() NOT NULL 942 | ); 943 | 944 | 945 | ALTER TABLE public.store OWNER TO postgres; 946 | 947 | -- 948 | -- Name: sales_by_store; Type: VIEW; Schema: public; Owner: postgres 949 | -- 950 | 951 | CREATE VIEW public.sales_by_store AS 952 | SELECT ((c.city || ','::text) || cy.country) AS store, 953 | ((m.first_name || ' '::text) || m.last_name) AS manager, 954 | sum(p.amount) AS total_sales 955 | FROM (((((((public.payment p 956 | JOIN public.rental r ON ((p.rental_id = r.rental_id))) 957 | JOIN public.inventory i ON ((r.inventory_id = i.inventory_id))) 958 | JOIN public.store s ON ((i.store_id = s.store_id))) 959 | JOIN public.address a ON ((s.address_id = a.address_id))) 960 | JOIN public.city c ON ((a.city_id = c.city_id))) 961 | JOIN public.country cy ON ((c.country_id = cy.country_id))) 962 | JOIN public.staff m ON ((s.manager_staff_id = m.staff_id))) 963 | GROUP BY cy.country, c.city, s.store_id, m.first_name, m.last_name 964 | ORDER BY cy.country, c.city; 965 | 966 | 967 | ALTER TABLE public.sales_by_store OWNER TO postgres; 968 | 969 | -- 970 | -- Name: staff_list; Type: VIEW; Schema: public; Owner: postgres 971 | -- 972 | 973 | CREATE VIEW public.staff_list AS 974 | SELECT s.staff_id AS id, 975 | ((s.first_name || ' '::text) || s.last_name) AS name, 976 | a.address, 977 | a.postal_code AS "zip code", 978 | a.phone, 979 | city.city, 980 | country.country, 981 | s.store_id AS sid 982 | FROM (((public.staff s 983 | JOIN public.address a ON ((s.address_id = a.address_id))) 984 | JOIN public.city ON ((a.city_id = city.city_id))) 985 | JOIN public.country ON ((city.country_id = country.country_id))); 986 | 987 | 988 | ALTER TABLE public.staff_list OWNER TO postgres; 989 | 990 | -- 991 | -- Name: actor actor_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 992 | -- 993 | 994 | ALTER TABLE ONLY public.actor 995 | ADD CONSTRAINT actor_pkey PRIMARY KEY (actor_id); 996 | 997 | 998 | -- 999 | -- Name: address address_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 1000 | -- 1001 | 1002 | ALTER TABLE ONLY public.address 1003 | ADD CONSTRAINT address_pkey PRIMARY KEY (address_id); 1004 | 1005 | 1006 | -- 1007 | -- Name: category category_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 1008 | -- 1009 | 1010 | ALTER TABLE ONLY public.category 1011 | ADD CONSTRAINT category_pkey PRIMARY KEY (category_id); 1012 | 1013 | 1014 | -- 1015 | -- Name: city city_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 1016 | -- 1017 | 1018 | ALTER TABLE ONLY public.city 1019 | ADD CONSTRAINT city_pkey PRIMARY KEY (city_id); 1020 | 1021 | 1022 | -- 1023 | -- Name: country country_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 1024 | -- 1025 | 1026 | ALTER TABLE ONLY public.country 1027 | ADD CONSTRAINT country_pkey PRIMARY KEY (country_id); 1028 | 1029 | 1030 | -- 1031 | -- Name: customer customer_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 1032 | -- 1033 | 1034 | ALTER TABLE ONLY public.customer 1035 | ADD CONSTRAINT customer_pkey PRIMARY KEY (customer_id); 1036 | 1037 | 1038 | -- 1039 | -- Name: film_actor film_actor_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 1040 | -- 1041 | 1042 | ALTER TABLE ONLY public.film_actor 1043 | ADD CONSTRAINT film_actor_pkey PRIMARY KEY (actor_id, film_id); 1044 | 1045 | 1046 | -- 1047 | -- Name: film_category film_category_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 1048 | -- 1049 | 1050 | ALTER TABLE ONLY public.film_category 1051 | ADD CONSTRAINT film_category_pkey PRIMARY KEY (film_id, category_id); 1052 | 1053 | 1054 | -- 1055 | -- Name: film film_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 1056 | -- 1057 | 1058 | ALTER TABLE ONLY public.film 1059 | ADD CONSTRAINT film_pkey PRIMARY KEY (film_id); 1060 | 1061 | 1062 | -- 1063 | -- Name: inventory inventory_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 1064 | -- 1065 | 1066 | ALTER TABLE ONLY public.inventory 1067 | ADD CONSTRAINT inventory_pkey PRIMARY KEY (inventory_id); 1068 | 1069 | 1070 | -- 1071 | -- Name: language language_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 1072 | -- 1073 | 1074 | ALTER TABLE ONLY public.language 1075 | ADD CONSTRAINT language_pkey PRIMARY KEY (language_id); 1076 | 1077 | 1078 | -- 1079 | -- Name: rental rental_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 1080 | -- 1081 | 1082 | ALTER TABLE ONLY public.rental 1083 | ADD CONSTRAINT rental_pkey PRIMARY KEY (rental_id); 1084 | 1085 | 1086 | -- 1087 | -- Name: staff staff_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 1088 | -- 1089 | 1090 | ALTER TABLE ONLY public.staff 1091 | ADD CONSTRAINT staff_pkey PRIMARY KEY (staff_id); 1092 | 1093 | 1094 | -- 1095 | -- Name: store store_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 1096 | -- 1097 | 1098 | ALTER TABLE ONLY public.store 1099 | ADD CONSTRAINT store_pkey PRIMARY KEY (store_id); 1100 | 1101 | 1102 | -- 1103 | -- Name: film_fulltext_idx; Type: INDEX; Schema: public; Owner: postgres 1104 | -- 1105 | 1106 | CREATE INDEX film_fulltext_idx ON public.film USING gist (fulltext); 1107 | 1108 | 1109 | -- 1110 | -- Name: idx_actor_last_name; Type: INDEX; Schema: public; Owner: postgres 1111 | -- 1112 | 1113 | CREATE INDEX idx_actor_last_name ON public.actor USING btree (last_name); 1114 | 1115 | 1116 | -- 1117 | -- Name: idx_fk_address_id; Type: INDEX; Schema: public; Owner: postgres 1118 | -- 1119 | 1120 | CREATE INDEX idx_fk_address_id ON public.customer USING btree (address_id); 1121 | 1122 | 1123 | -- 1124 | -- Name: idx_fk_city_id; Type: INDEX; Schema: public; Owner: postgres 1125 | -- 1126 | 1127 | CREATE INDEX idx_fk_city_id ON public.address USING btree (city_id); 1128 | 1129 | 1130 | -- 1131 | -- Name: idx_fk_country_id; Type: INDEX; Schema: public; Owner: postgres 1132 | -- 1133 | 1134 | CREATE INDEX idx_fk_country_id ON public.city USING btree (country_id); 1135 | 1136 | 1137 | -- 1138 | -- Name: idx_fk_customer_id; Type: INDEX; Schema: public; Owner: postgres 1139 | -- 1140 | 1141 | CREATE INDEX idx_fk_customer_id ON ONLY public.payment USING btree (customer_id); 1142 | 1143 | 1144 | -- 1145 | -- Name: idx_fk_film_id; Type: INDEX; Schema: public; Owner: postgres 1146 | -- 1147 | 1148 | CREATE INDEX idx_fk_film_id ON public.film_actor USING btree (film_id); 1149 | 1150 | 1151 | -- 1152 | -- Name: idx_fk_inventory_id; Type: INDEX; Schema: public; Owner: postgres 1153 | -- 1154 | 1155 | CREATE INDEX idx_fk_inventory_id ON public.rental USING btree (inventory_id); 1156 | 1157 | 1158 | -- 1159 | -- Name: idx_fk_language_id; Type: INDEX; Schema: public; Owner: postgres 1160 | -- 1161 | 1162 | CREATE INDEX idx_fk_language_id ON public.film USING btree (language_id); 1163 | 1164 | 1165 | -- 1166 | -- Name: idx_fk_original_language_id; Type: INDEX; Schema: public; Owner: postgres 1167 | -- 1168 | 1169 | CREATE INDEX idx_fk_original_language_id ON public.film USING btree (original_language_id); 1170 | 1171 | 1172 | -- 1173 | -- Name: idx_fk_payment_p2020_01_customer_id; Type: INDEX; Schema: public; Owner: postgres 1174 | -- 1175 | 1176 | CREATE INDEX idx_fk_payment_p2020_01_customer_id ON public.payment_p2020_01 USING btree (customer_id); 1177 | 1178 | 1179 | -- 1180 | -- Name: idx_fk_staff_id; Type: INDEX; Schema: public; Owner: postgres 1181 | -- 1182 | 1183 | CREATE INDEX idx_fk_staff_id ON ONLY public.payment USING btree (staff_id); 1184 | 1185 | 1186 | -- 1187 | -- Name: idx_fk_payment_p2020_01_staff_id; Type: INDEX; Schema: public; Owner: postgres 1188 | -- 1189 | 1190 | CREATE INDEX idx_fk_payment_p2020_01_staff_id ON public.payment_p2020_01 USING btree (staff_id); 1191 | 1192 | 1193 | -- 1194 | -- Name: idx_fk_payment_p2020_02_customer_id; Type: INDEX; Schema: public; Owner: postgres 1195 | -- 1196 | 1197 | CREATE INDEX idx_fk_payment_p2020_02_customer_id ON public.payment_p2020_02 USING btree (customer_id); 1198 | 1199 | 1200 | -- 1201 | -- Name: idx_fk_payment_p2020_02_staff_id; Type: INDEX; Schema: public; Owner: postgres 1202 | -- 1203 | 1204 | CREATE INDEX idx_fk_payment_p2020_02_staff_id ON public.payment_p2020_02 USING btree (staff_id); 1205 | 1206 | 1207 | -- 1208 | -- Name: idx_fk_payment_p2020_03_customer_id; Type: INDEX; Schema: public; Owner: postgres 1209 | -- 1210 | 1211 | CREATE INDEX idx_fk_payment_p2020_03_customer_id ON public.payment_p2020_03 USING btree (customer_id); 1212 | 1213 | 1214 | -- 1215 | -- Name: idx_fk_payment_p2020_03_staff_id; Type: INDEX; Schema: public; Owner: postgres 1216 | -- 1217 | 1218 | CREATE INDEX idx_fk_payment_p2020_03_staff_id ON public.payment_p2020_03 USING btree (staff_id); 1219 | 1220 | 1221 | -- 1222 | -- Name: idx_fk_payment_p2020_04_customer_id; Type: INDEX; Schema: public; Owner: postgres 1223 | -- 1224 | 1225 | CREATE INDEX idx_fk_payment_p2020_04_customer_id ON public.payment_p2020_04 USING btree (customer_id); 1226 | 1227 | 1228 | -- 1229 | -- Name: idx_fk_payment_p2020_04_staff_id; Type: INDEX; Schema: public; Owner: postgres 1230 | -- 1231 | 1232 | CREATE INDEX idx_fk_payment_p2020_04_staff_id ON public.payment_p2020_04 USING btree (staff_id); 1233 | 1234 | 1235 | -- 1236 | -- Name: idx_fk_payment_p2020_05_customer_id; Type: INDEX; Schema: public; Owner: postgres 1237 | -- 1238 | 1239 | CREATE INDEX idx_fk_payment_p2020_05_customer_id ON public.payment_p2020_05 USING btree (customer_id); 1240 | 1241 | 1242 | -- 1243 | -- Name: idx_fk_payment_p2020_05_staff_id; Type: INDEX; Schema: public; Owner: postgres 1244 | -- 1245 | 1246 | CREATE INDEX idx_fk_payment_p2020_05_staff_id ON public.payment_p2020_05 USING btree (staff_id); 1247 | 1248 | 1249 | -- 1250 | -- Name: idx_fk_payment_p2020_06_customer_id; Type: INDEX; Schema: public; Owner: postgres 1251 | -- 1252 | 1253 | CREATE INDEX idx_fk_payment_p2020_06_customer_id ON public.payment_p2020_06 USING btree (customer_id); 1254 | 1255 | 1256 | -- 1257 | -- Name: idx_fk_payment_p2020_06_staff_id; Type: INDEX; Schema: public; Owner: postgres 1258 | -- 1259 | 1260 | CREATE INDEX idx_fk_payment_p2020_06_staff_id ON public.payment_p2020_06 USING btree (staff_id); 1261 | 1262 | 1263 | -- 1264 | -- Name: idx_fk_store_id; Type: INDEX; Schema: public; Owner: postgres 1265 | -- 1266 | 1267 | CREATE INDEX idx_fk_store_id ON public.customer USING btree (store_id); 1268 | 1269 | 1270 | -- 1271 | -- Name: idx_last_name; Type: INDEX; Schema: public; Owner: postgres 1272 | -- 1273 | 1274 | CREATE INDEX idx_last_name ON public.customer USING btree (last_name); 1275 | 1276 | 1277 | -- 1278 | -- Name: idx_store_id_film_id; Type: INDEX; Schema: public; Owner: postgres 1279 | -- 1280 | 1281 | CREATE INDEX idx_store_id_film_id ON public.inventory USING btree (store_id, film_id); 1282 | 1283 | 1284 | -- 1285 | -- Name: idx_title; Type: INDEX; Schema: public; Owner: postgres 1286 | -- 1287 | 1288 | CREATE INDEX idx_title ON public.film USING btree (title); 1289 | 1290 | 1291 | -- 1292 | -- Name: idx_unq_manager_staff_id; Type: INDEX; Schema: public; Owner: postgres 1293 | -- 1294 | 1295 | CREATE UNIQUE INDEX idx_unq_manager_staff_id ON public.store USING btree (manager_staff_id); 1296 | 1297 | 1298 | -- 1299 | -- Name: idx_unq_rental_rental_date_inventory_id_customer_id; Type: INDEX; Schema: public; Owner: postgres 1300 | -- 1301 | 1302 | CREATE UNIQUE INDEX idx_unq_rental_rental_date_inventory_id_customer_id ON public.rental USING btree (rental_date, inventory_id, customer_id); 1303 | 1304 | 1305 | -- 1306 | -- Name: payment_p2020_01_customer_id_idx; Type: INDEX; Schema: public; Owner: postgres 1307 | -- 1308 | 1309 | CREATE INDEX payment_p2020_01_customer_id_idx ON public.payment_p2020_01 USING btree (customer_id); 1310 | 1311 | 1312 | -- 1313 | -- Name: payment_p2020_02_customer_id_idx; Type: INDEX; Schema: public; Owner: postgres 1314 | -- 1315 | 1316 | CREATE INDEX payment_p2020_02_customer_id_idx ON public.payment_p2020_02 USING btree (customer_id); 1317 | 1318 | 1319 | -- 1320 | -- Name: payment_p2020_03_customer_id_idx; Type: INDEX; Schema: public; Owner: postgres 1321 | -- 1322 | 1323 | CREATE INDEX payment_p2020_03_customer_id_idx ON public.payment_p2020_03 USING btree (customer_id); 1324 | 1325 | 1326 | -- 1327 | -- Name: payment_p2020_04_customer_id_idx; Type: INDEX; Schema: public; Owner: postgres 1328 | -- 1329 | 1330 | CREATE INDEX payment_p2020_04_customer_id_idx ON public.payment_p2020_04 USING btree (customer_id); 1331 | 1332 | 1333 | -- 1334 | -- Name: payment_p2020_05_customer_id_idx; Type: INDEX; Schema: public; Owner: postgres 1335 | -- 1336 | 1337 | CREATE INDEX payment_p2020_05_customer_id_idx ON public.payment_p2020_05 USING btree (customer_id); 1338 | 1339 | 1340 | -- 1341 | -- Name: payment_p2020_06_customer_id_idx; Type: INDEX; Schema: public; Owner: postgres 1342 | -- 1343 | 1344 | CREATE INDEX payment_p2020_06_customer_id_idx ON public.payment_p2020_06 USING btree (customer_id); 1345 | 1346 | 1347 | -- 1348 | -- Name: idx_fk_payment_p2020_01_staff_id; Type: INDEX ATTACH; Schema: public; Owner: - 1349 | -- 1350 | 1351 | ALTER INDEX public.idx_fk_staff_id ATTACH PARTITION public.idx_fk_payment_p2020_01_staff_id; 1352 | 1353 | 1354 | -- 1355 | -- Name: idx_fk_payment_p2020_02_staff_id; Type: INDEX ATTACH; Schema: public; Owner: - 1356 | -- 1357 | 1358 | ALTER INDEX public.idx_fk_staff_id ATTACH PARTITION public.idx_fk_payment_p2020_02_staff_id; 1359 | 1360 | 1361 | -- 1362 | -- Name: idx_fk_payment_p2020_03_staff_id; Type: INDEX ATTACH; Schema: public; Owner: - 1363 | -- 1364 | 1365 | ALTER INDEX public.idx_fk_staff_id ATTACH PARTITION public.idx_fk_payment_p2020_03_staff_id; 1366 | 1367 | 1368 | -- 1369 | -- Name: idx_fk_payment_p2020_04_staff_id; Type: INDEX ATTACH; Schema: public; Owner: - 1370 | -- 1371 | 1372 | ALTER INDEX public.idx_fk_staff_id ATTACH PARTITION public.idx_fk_payment_p2020_04_staff_id; 1373 | 1374 | 1375 | -- 1376 | -- Name: idx_fk_payment_p2020_05_staff_id; Type: INDEX ATTACH; Schema: public; Owner: - 1377 | -- 1378 | 1379 | ALTER INDEX public.idx_fk_staff_id ATTACH PARTITION public.idx_fk_payment_p2020_05_staff_id; 1380 | 1381 | 1382 | -- 1383 | -- Name: idx_fk_payment_p2020_06_staff_id; Type: INDEX ATTACH; Schema: public; Owner: - 1384 | -- 1385 | 1386 | ALTER INDEX public.idx_fk_staff_id ATTACH PARTITION public.idx_fk_payment_p2020_06_staff_id; 1387 | 1388 | 1389 | -- 1390 | -- Name: payment_p2020_01_customer_id_idx; Type: INDEX ATTACH; Schema: public; Owner: - 1391 | -- 1392 | 1393 | ALTER INDEX public.idx_fk_customer_id ATTACH PARTITION public.payment_p2020_01_customer_id_idx; 1394 | 1395 | 1396 | -- 1397 | -- Name: payment_p2020_02_customer_id_idx; Type: INDEX ATTACH; Schema: public; Owner: - 1398 | -- 1399 | 1400 | ALTER INDEX public.idx_fk_customer_id ATTACH PARTITION public.payment_p2020_02_customer_id_idx; 1401 | 1402 | 1403 | -- 1404 | -- Name: payment_p2020_03_customer_id_idx; Type: INDEX ATTACH; Schema: public; Owner: - 1405 | -- 1406 | 1407 | ALTER INDEX public.idx_fk_customer_id ATTACH PARTITION public.payment_p2020_03_customer_id_idx; 1408 | 1409 | 1410 | -- 1411 | -- Name: payment_p2020_04_customer_id_idx; Type: INDEX ATTACH; Schema: public; Owner: - 1412 | -- 1413 | 1414 | ALTER INDEX public.idx_fk_customer_id ATTACH PARTITION public.payment_p2020_04_customer_id_idx; 1415 | 1416 | 1417 | -- 1418 | -- Name: payment_p2020_05_customer_id_idx; Type: INDEX ATTACH; Schema: public; Owner: - 1419 | -- 1420 | 1421 | ALTER INDEX public.idx_fk_customer_id ATTACH PARTITION public.payment_p2020_05_customer_id_idx; 1422 | 1423 | 1424 | -- 1425 | -- Name: payment_p2020_06_customer_id_idx; Type: INDEX ATTACH; Schema: public; Owner: - 1426 | -- 1427 | 1428 | ALTER INDEX public.idx_fk_customer_id ATTACH PARTITION public.payment_p2020_06_customer_id_idx; 1429 | 1430 | 1431 | -- 1432 | -- Name: film film_fulltext_trigger; Type: TRIGGER; Schema: public; Owner: postgres 1433 | -- 1434 | 1435 | CREATE TRIGGER film_fulltext_trigger BEFORE INSERT OR UPDATE ON public.film FOR EACH ROW EXECUTE FUNCTION tsvector_update_trigger('fulltext', 'pg_catalog.english', 'title', 'description'); 1436 | 1437 | 1438 | -- 1439 | -- Name: actor last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1440 | -- 1441 | 1442 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.actor FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1443 | 1444 | 1445 | -- 1446 | -- Name: address last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1447 | -- 1448 | 1449 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.address FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1450 | 1451 | 1452 | -- 1453 | -- Name: category last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1454 | -- 1455 | 1456 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.category FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1457 | 1458 | 1459 | -- 1460 | -- Name: city last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1461 | -- 1462 | 1463 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.city FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1464 | 1465 | 1466 | -- 1467 | -- Name: country last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1468 | -- 1469 | 1470 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.country FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1471 | 1472 | 1473 | -- 1474 | -- Name: customer last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1475 | -- 1476 | 1477 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.customer FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1478 | 1479 | 1480 | -- 1481 | -- Name: film last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1482 | -- 1483 | 1484 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.film FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1485 | 1486 | 1487 | -- 1488 | -- Name: film_actor last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1489 | -- 1490 | 1491 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.film_actor FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1492 | 1493 | 1494 | -- 1495 | -- Name: film_category last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1496 | -- 1497 | 1498 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.film_category FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1499 | 1500 | 1501 | -- 1502 | -- Name: inventory last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1503 | -- 1504 | 1505 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.inventory FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1506 | 1507 | 1508 | -- 1509 | -- Name: language last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1510 | -- 1511 | 1512 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.language FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1513 | 1514 | 1515 | -- 1516 | -- Name: rental last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1517 | -- 1518 | 1519 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.rental FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1520 | 1521 | 1522 | -- 1523 | -- Name: staff last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1524 | -- 1525 | 1526 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.staff FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1527 | 1528 | 1529 | -- 1530 | -- Name: store last_updated; Type: TRIGGER; Schema: public; Owner: postgres 1531 | -- 1532 | 1533 | CREATE TRIGGER last_updated BEFORE UPDATE ON public.store FOR EACH ROW EXECUTE FUNCTION public.last_updated(); 1534 | 1535 | 1536 | -- 1537 | -- Name: address address_city_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1538 | -- 1539 | 1540 | ALTER TABLE ONLY public.address 1541 | ADD CONSTRAINT address_city_id_fkey FOREIGN KEY (city_id) REFERENCES public.city(city_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1542 | 1543 | 1544 | -- 1545 | -- Name: city city_country_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1546 | -- 1547 | 1548 | ALTER TABLE ONLY public.city 1549 | ADD CONSTRAINT city_country_id_fkey FOREIGN KEY (country_id) REFERENCES public.country(country_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1550 | 1551 | 1552 | -- 1553 | -- Name: customer customer_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1554 | -- 1555 | 1556 | ALTER TABLE ONLY public.customer 1557 | ADD CONSTRAINT customer_address_id_fkey FOREIGN KEY (address_id) REFERENCES public.address(address_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1558 | 1559 | 1560 | -- 1561 | -- Name: customer customer_store_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1562 | -- 1563 | 1564 | ALTER TABLE ONLY public.customer 1565 | ADD CONSTRAINT customer_store_id_fkey FOREIGN KEY (store_id) REFERENCES public.store(store_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1566 | 1567 | 1568 | -- 1569 | -- Name: film_actor film_actor_actor_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1570 | -- 1571 | 1572 | ALTER TABLE ONLY public.film_actor 1573 | ADD CONSTRAINT film_actor_actor_id_fkey FOREIGN KEY (actor_id) REFERENCES public.actor(actor_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1574 | 1575 | 1576 | -- 1577 | -- Name: film_actor film_actor_film_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1578 | -- 1579 | 1580 | ALTER TABLE ONLY public.film_actor 1581 | ADD CONSTRAINT film_actor_film_id_fkey FOREIGN KEY (film_id) REFERENCES public.film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1582 | 1583 | 1584 | -- 1585 | -- Name: film_category film_category_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1586 | -- 1587 | 1588 | ALTER TABLE ONLY public.film_category 1589 | ADD CONSTRAINT film_category_category_id_fkey FOREIGN KEY (category_id) REFERENCES public.category(category_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1590 | 1591 | 1592 | -- 1593 | -- Name: film_category film_category_film_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1594 | -- 1595 | 1596 | ALTER TABLE ONLY public.film_category 1597 | ADD CONSTRAINT film_category_film_id_fkey FOREIGN KEY (film_id) REFERENCES public.film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1598 | 1599 | 1600 | -- 1601 | -- Name: film film_language_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1602 | -- 1603 | 1604 | ALTER TABLE ONLY public.film 1605 | ADD CONSTRAINT film_language_id_fkey FOREIGN KEY (language_id) REFERENCES public.language(language_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1606 | 1607 | 1608 | -- 1609 | -- Name: film film_original_language_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1610 | -- 1611 | 1612 | ALTER TABLE ONLY public.film 1613 | ADD CONSTRAINT film_original_language_id_fkey FOREIGN KEY (original_language_id) REFERENCES public.language(language_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1614 | 1615 | 1616 | -- 1617 | -- Name: inventory inventory_film_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1618 | -- 1619 | 1620 | ALTER TABLE ONLY public.inventory 1621 | ADD CONSTRAINT inventory_film_id_fkey FOREIGN KEY (film_id) REFERENCES public.film(film_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1622 | 1623 | 1624 | -- 1625 | -- Name: inventory inventory_store_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1626 | -- 1627 | 1628 | ALTER TABLE ONLY public.inventory 1629 | ADD CONSTRAINT inventory_store_id_fkey FOREIGN KEY (store_id) REFERENCES public.store(store_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1630 | 1631 | 1632 | -- 1633 | -- Name: payment_p2020_01 payment_p2020_01_customer_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1634 | -- 1635 | 1636 | ALTER TABLE ONLY public.payment_p2020_01 1637 | ADD CONSTRAINT payment_p2020_01_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES public.customer(customer_id); 1638 | 1639 | 1640 | -- 1641 | -- Name: payment_p2020_01 payment_p2020_01_rental_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1642 | -- 1643 | 1644 | ALTER TABLE ONLY public.payment_p2020_01 1645 | ADD CONSTRAINT payment_p2020_01_rental_id_fkey FOREIGN KEY (rental_id) REFERENCES public.rental(rental_id); 1646 | 1647 | 1648 | -- 1649 | -- Name: payment_p2020_01 payment_p2020_01_staff_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1650 | -- 1651 | 1652 | ALTER TABLE ONLY public.payment_p2020_01 1653 | ADD CONSTRAINT payment_p2020_01_staff_id_fkey FOREIGN KEY (staff_id) REFERENCES public.staff(staff_id); 1654 | 1655 | 1656 | -- 1657 | -- Name: payment_p2020_02 payment_p2020_02_customer_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1658 | -- 1659 | 1660 | ALTER TABLE ONLY public.payment_p2020_02 1661 | ADD CONSTRAINT payment_p2020_02_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES public.customer(customer_id); 1662 | 1663 | 1664 | -- 1665 | -- Name: payment_p2020_02 payment_p2020_02_rental_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1666 | -- 1667 | 1668 | ALTER TABLE ONLY public.payment_p2020_02 1669 | ADD CONSTRAINT payment_p2020_02_rental_id_fkey FOREIGN KEY (rental_id) REFERENCES public.rental(rental_id); 1670 | 1671 | 1672 | -- 1673 | -- Name: payment_p2020_02 payment_p2020_02_staff_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1674 | -- 1675 | 1676 | ALTER TABLE ONLY public.payment_p2020_02 1677 | ADD CONSTRAINT payment_p2020_02_staff_id_fkey FOREIGN KEY (staff_id) REFERENCES public.staff(staff_id); 1678 | 1679 | 1680 | -- 1681 | -- Name: payment_p2020_03 payment_p2020_03_customer_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1682 | -- 1683 | 1684 | ALTER TABLE ONLY public.payment_p2020_03 1685 | ADD CONSTRAINT payment_p2020_03_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES public.customer(customer_id); 1686 | 1687 | 1688 | -- 1689 | -- Name: payment_p2020_03 payment_p2020_03_rental_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1690 | -- 1691 | 1692 | ALTER TABLE ONLY public.payment_p2020_03 1693 | ADD CONSTRAINT payment_p2020_03_rental_id_fkey FOREIGN KEY (rental_id) REFERENCES public.rental(rental_id); 1694 | 1695 | 1696 | -- 1697 | -- Name: payment_p2020_03 payment_p2020_03_staff_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1698 | -- 1699 | 1700 | ALTER TABLE ONLY public.payment_p2020_03 1701 | ADD CONSTRAINT payment_p2020_03_staff_id_fkey FOREIGN KEY (staff_id) REFERENCES public.staff(staff_id); 1702 | 1703 | 1704 | -- 1705 | -- Name: payment_p2020_04 payment_p2020_04_customer_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1706 | -- 1707 | 1708 | ALTER TABLE ONLY public.payment_p2020_04 1709 | ADD CONSTRAINT payment_p2020_04_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES public.customer(customer_id); 1710 | 1711 | 1712 | -- 1713 | -- Name: payment_p2020_04 payment_p2020_04_rental_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1714 | -- 1715 | 1716 | ALTER TABLE ONLY public.payment_p2020_04 1717 | ADD CONSTRAINT payment_p2020_04_rental_id_fkey FOREIGN KEY (rental_id) REFERENCES public.rental(rental_id); 1718 | 1719 | 1720 | -- 1721 | -- Name: payment_p2020_04 payment_p2020_04_staff_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1722 | -- 1723 | 1724 | ALTER TABLE ONLY public.payment_p2020_04 1725 | ADD CONSTRAINT payment_p2020_04_staff_id_fkey FOREIGN KEY (staff_id) REFERENCES public.staff(staff_id); 1726 | 1727 | 1728 | -- 1729 | -- Name: payment_p2020_05 payment_p2020_05_customer_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1730 | -- 1731 | 1732 | ALTER TABLE ONLY public.payment_p2020_05 1733 | ADD CONSTRAINT payment_p2020_05_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES public.customer(customer_id); 1734 | 1735 | 1736 | -- 1737 | -- Name: payment_p2020_05 payment_p2020_05_rental_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1738 | -- 1739 | 1740 | ALTER TABLE ONLY public.payment_p2020_05 1741 | ADD CONSTRAINT payment_p2020_05_rental_id_fkey FOREIGN KEY (rental_id) REFERENCES public.rental(rental_id); 1742 | 1743 | 1744 | -- 1745 | -- Name: payment_p2020_05 payment_p2020_05_staff_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1746 | -- 1747 | 1748 | ALTER TABLE ONLY public.payment_p2020_05 1749 | ADD CONSTRAINT payment_p2020_05_staff_id_fkey FOREIGN KEY (staff_id) REFERENCES public.staff(staff_id); 1750 | 1751 | 1752 | -- 1753 | -- Name: payment_p2020_06 payment_p2020_06_customer_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1754 | -- 1755 | 1756 | ALTER TABLE ONLY public.payment_p2020_06 1757 | ADD CONSTRAINT payment_p2020_06_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES public.customer(customer_id); 1758 | 1759 | 1760 | -- 1761 | -- Name: payment_p2020_06 payment_p2020_06_rental_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1762 | -- 1763 | 1764 | ALTER TABLE ONLY public.payment_p2020_06 1765 | ADD CONSTRAINT payment_p2020_06_rental_id_fkey FOREIGN KEY (rental_id) REFERENCES public.rental(rental_id); 1766 | 1767 | 1768 | -- 1769 | -- Name: payment_p2020_06 payment_p2020_06_staff_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1770 | -- 1771 | 1772 | ALTER TABLE ONLY public.payment_p2020_06 1773 | ADD CONSTRAINT payment_p2020_06_staff_id_fkey FOREIGN KEY (staff_id) REFERENCES public.staff(staff_id); 1774 | 1775 | 1776 | -- 1777 | -- Name: rental rental_customer_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1778 | -- 1779 | 1780 | ALTER TABLE ONLY public.rental 1781 | ADD CONSTRAINT rental_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES public.customer(customer_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1782 | 1783 | 1784 | -- 1785 | -- Name: rental rental_inventory_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1786 | -- 1787 | 1788 | ALTER TABLE ONLY public.rental 1789 | ADD CONSTRAINT rental_inventory_id_fkey FOREIGN KEY (inventory_id) REFERENCES public.inventory(inventory_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1790 | 1791 | 1792 | -- 1793 | -- Name: rental rental_staff_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1794 | -- 1795 | 1796 | ALTER TABLE ONLY public.rental 1797 | ADD CONSTRAINT rental_staff_id_fkey FOREIGN KEY (staff_id) REFERENCES public.staff(staff_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1798 | 1799 | 1800 | -- 1801 | -- Name: staff staff_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1802 | -- 1803 | 1804 | ALTER TABLE ONLY public.staff 1805 | ADD CONSTRAINT staff_address_id_fkey FOREIGN KEY (address_id) REFERENCES public.address(address_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1806 | 1807 | 1808 | -- 1809 | -- Name: staff staff_store_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1810 | -- 1811 | 1812 | ALTER TABLE ONLY public.staff 1813 | ADD CONSTRAINT staff_store_id_fkey FOREIGN KEY (store_id) REFERENCES public.store(store_id); 1814 | 1815 | 1816 | -- 1817 | -- Name: store store_address_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres 1818 | -- 1819 | 1820 | ALTER TABLE ONLY public.store 1821 | ADD CONSTRAINT store_address_id_fkey FOREIGN KEY (address_id) REFERENCES public.address(address_id) ON UPDATE CASCADE ON DELETE RESTRICT; 1822 | 1823 | 1824 | -- 1825 | -- PostgreSQL database dump complete 1826 | -- 1827 | -------------------------------------------------------------------------------- /example/pagila/actor.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const Actor = z.object({ 4 | actor_id: z.number().int().optional(), 5 | first_name: z.string(), 6 | last_name: z.string(), 7 | last_update: z.string().optional(), 8 | }); 9 | 10 | export type ActorT = z.infer; 11 | -------------------------------------------------------------------------------- /example/pagila/actorInfo.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const ActorInfo = z.object({ 4 | actor_id: z.number().int().nullable().optional(), 5 | first_name: z.string().nullable().optional(), 6 | last_name: z.string().nullable().optional(), 7 | film_info: z.string().nullable().optional(), 8 | }); 9 | 10 | export type ActorInfoT = z.infer; 11 | -------------------------------------------------------------------------------- /example/pagila/address.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const Address = z.object({ 4 | address_id: z.number().int().optional(), 5 | address: z.string(), 6 | address2: z.string().nullable().optional(), 7 | district: z.string(), 8 | city_id: z.number().int(), 9 | postal_code: z.string().nullable().optional(), 10 | phone: z.string(), 11 | last_update: z.string().optional(), 12 | }); 13 | 14 | export type AddressT = z.infer; 15 | -------------------------------------------------------------------------------- /example/pagila/category.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const Category = z.object({ 4 | category_id: z.number().int().optional(), 5 | name: z.string(), 6 | last_update: z.string().optional(), 7 | }); 8 | 9 | export type CategoryT = z.infer; 10 | -------------------------------------------------------------------------------- /example/pagila/city.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const City = z.object({ 4 | city_id: z.number().int().optional(), 5 | city: z.string(), 6 | country_id: z.number().int(), 7 | last_update: z.string().optional(), 8 | }); 9 | 10 | export type CityT = z.infer; 11 | -------------------------------------------------------------------------------- /example/pagila/country.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const Country = z.object({ 4 | country_id: z.number().int().optional(), 5 | country: z.string(), 6 | last_update: z.string().optional(), 7 | }); 8 | 9 | export type CountryT = z.infer; 10 | -------------------------------------------------------------------------------- /example/pagila/customer.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const Customer = z.object({ 4 | customer_id: z.number().int().optional(), 5 | store_id: z.number().int(), 6 | first_name: z.string(), 7 | last_name: z.string(), 8 | email: z.string().nullable().optional(), 9 | address_id: z.number().int(), 10 | activebool: z.boolean().optional(), 11 | create_date: z.string().optional(), 12 | last_update: z.string().nullable().optional().optional(), 13 | active: z.number().int().nullable().optional(), 14 | }); 15 | 16 | export type CustomerT = z.infer; 17 | -------------------------------------------------------------------------------- /example/pagila/customerList.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const CustomerList = z.object({ 4 | id: z.number().int().nullable().optional(), 5 | name: z.string().nullable().optional(), 6 | address: z.string().nullable().optional(), 7 | zip code: z.string().nullable().optional(), 8 | phone: z.string().nullable().optional(), 9 | city: z.string().nullable().optional(), 10 | country: z.string().nullable().optional(), 11 | notes: z.string().nullable().optional(), 12 | sid: z.number().int().nullable().optional(), 13 | }); 14 | 15 | export type CustomerListT = z.infer; 16 | -------------------------------------------------------------------------------- /example/pagila/film.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const Film = z.object({ 4 | film_id: z.number().int().optional(), 5 | title: z.string(), 6 | description: z.string().nullable().optional(), 7 | release_year: z.number().int().nullable().optional(), 8 | language_id: z.number().int(), 9 | original_language_id: z.number().int().nullable().optional(), 10 | rental_duration: z.int2().optional(), 11 | rental_rate: z.number().optional(), 12 | length: z.int2().nullable().optional(), 13 | replacement_cost: z.number().optional(), 14 | rating: z.mpaa_rating().nullable().optional().optional(), 15 | last_update: z.string().optional(), 16 | special_features: z._text().nullable().optional(), 17 | fulltext: z.tsvector(), 18 | }); 19 | 20 | export type FilmT = z.infer; 21 | -------------------------------------------------------------------------------- /example/pagila/filmActor.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const FilmActor = z.object({ 4 | actor_id: z.number().int(), 5 | film_id: z.number().int(), 6 | last_update: z.string().optional(), 7 | }); 8 | 9 | export type FilmActorT = z.infer; 10 | -------------------------------------------------------------------------------- /example/pagila/filmCategory.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const FilmCategory = z.object({ 4 | film_id: z.number().int(), 5 | category_id: z.number().int(), 6 | last_update: z.string().optional(), 7 | }); 8 | 9 | export type FilmCategoryT = z.infer; 10 | -------------------------------------------------------------------------------- /example/pagila/filmList.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const FilmList = z.object({ 4 | fid: z.number().int().nullable().optional(), 5 | title: z.string().nullable().optional(), 6 | description: z.string().nullable().optional(), 7 | category: z.string().nullable().optional(), 8 | price: z.number().nullable().optional(), 9 | length: z.int2().nullable().optional(), 10 | rating: z.mpaa_rating().nullable().optional(), 11 | actors: z.string().nullable().optional(), 12 | }); 13 | 14 | export type FilmListT = z.infer; 15 | -------------------------------------------------------------------------------- /example/pagila/index.ts: -------------------------------------------------------------------------------- 1 | export type { CustomerT } from './customer'; 2 | export { Customer } from './customer'; 3 | export type { ActorT } from './actor'; 4 | export { Actor } from './actor'; 5 | export type { CategoryT } from './category'; 6 | export { Category } from './category'; 7 | export type { FilmT } from './film'; 8 | export { Film } from './film'; 9 | export type { FilmActorT } from './filmActor'; 10 | export { FilmActor } from './filmActor'; 11 | export type { FilmCategoryT } from './filmCategory'; 12 | export { FilmCategory } from './filmCategory'; 13 | export type { ActorInfoT } from './actorInfo'; 14 | export { ActorInfo } from './actorInfo'; 15 | export type { AddressT } from './address'; 16 | export { Address } from './address'; 17 | export type { CityT } from './city'; 18 | export { City } from './city'; 19 | export type { CountryT } from './country'; 20 | export { Country } from './country'; 21 | export type { CustomerListT } from './customerList'; 22 | export { CustomerList } from './customerList'; 23 | export type { FilmListT } from './filmList'; 24 | export { FilmList } from './filmList'; 25 | export type { InventoryT } from './inventory'; 26 | export { Inventory } from './inventory'; 27 | export type { LanguageT } from './language'; 28 | export { Language } from './language'; 29 | export type { NicerButSlowerFilmListT } from './nicerButSlowerFilmList'; 30 | export { NicerButSlowerFilmList } from './nicerButSlowerFilmList'; 31 | export type { PaymentT } from './payment'; 32 | export { Payment } from './payment'; 33 | export type { PaymentP2020_02T } from './paymentP2020_02'; 34 | export { PaymentP2020_02 } from './paymentP2020_02'; 35 | export type { PaymentP2020_03T } from './paymentP2020_03'; 36 | export { PaymentP2020_03 } from './paymentP2020_03'; 37 | export type { PaymentP2020_04T } from './paymentP2020_04'; 38 | export { PaymentP2020_04 } from './paymentP2020_04'; 39 | export type { PaymentP2020_05T } from './paymentP2020_05'; 40 | export { PaymentP2020_05 } from './paymentP2020_05'; 41 | export type { PaymentP2020_06T } from './paymentP2020_06'; 42 | export { PaymentP2020_06 } from './paymentP2020_06'; 43 | export type { RentalT } from './rental'; 44 | export { Rental } from './rental'; 45 | export type { SalesByFilmCategoryT } from './salesByFilmCategory'; 46 | export { SalesByFilmCategory } from './salesByFilmCategory'; 47 | export type { StaffT } from './staff'; 48 | export { Staff } from './staff'; 49 | export type { StoreT } from './store'; 50 | export { Store } from './store'; 51 | export type { SalesByStoreT } from './salesByStore'; 52 | export { SalesByStore } from './salesByStore'; 53 | export type { StaffListT } from './staffList'; 54 | export { StaffList } from './staffList'; 55 | export type { PaymentP2020_01T } from './paymentP2020_01'; 56 | export { PaymentP2020_01 } from './paymentP2020_01'; 57 | 58 | -------------------------------------------------------------------------------- /example/pagila/inventory.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const Inventory = z.object({ 4 | inventory_id: z.number().int().optional(), 5 | film_id: z.number().int(), 6 | store_id: z.number().int(), 7 | last_update: z.string().optional(), 8 | }); 9 | 10 | export type InventoryT = z.infer; 11 | -------------------------------------------------------------------------------- /example/pagila/language.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const Language = z.object({ 4 | language_id: z.number().int().optional(), 5 | name: z.bpchar(), 6 | last_update: z.string().optional(), 7 | }); 8 | 9 | export type LanguageT = z.infer; 10 | -------------------------------------------------------------------------------- /example/pagila/nicerButSlowerFilmList.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const NicerButSlowerFilmList = z.object({ 4 | fid: z.number().int().nullable().optional(), 5 | title: z.string().nullable().optional(), 6 | description: z.string().nullable().optional(), 7 | category: z.string().nullable().optional(), 8 | price: z.number().nullable().optional(), 9 | length: z.int2().nullable().optional(), 10 | rating: z.mpaa_rating().nullable().optional(), 11 | actors: z.string().nullable().optional(), 12 | }); 13 | 14 | export type NicerButSlowerFilmListT = z.infer; 15 | -------------------------------------------------------------------------------- /example/pagila/payment.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const Payment = z.object({ 4 | payment_id: z.number().int().optional(), 5 | customer_id: z.number().int(), 6 | staff_id: z.number().int(), 7 | rental_id: z.number().int(), 8 | amount: z.number(), 9 | payment_date: z.string(), 10 | }); 11 | 12 | export type PaymentT = z.infer; 13 | -------------------------------------------------------------------------------- /example/pagila/paymentP2020_01.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const PaymentP2020_01 = z.object({ 4 | payment_id: z.number().int().optional(), 5 | customer_id: z.number().int(), 6 | staff_id: z.number().int(), 7 | rental_id: z.number().int(), 8 | amount: z.number(), 9 | payment_date: z.string(), 10 | }); 11 | 12 | export type PaymentP2020_01T = z.infer; 13 | -------------------------------------------------------------------------------- /example/pagila/paymentP2020_02.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const PaymentP2020_02 = z.object({ 4 | payment_id: z.number().int().optional(), 5 | customer_id: z.number().int(), 6 | staff_id: z.number().int(), 7 | rental_id: z.number().int(), 8 | amount: z.number(), 9 | payment_date: z.string(), 10 | }); 11 | 12 | export type PaymentP2020_02T = z.infer; 13 | -------------------------------------------------------------------------------- /example/pagila/paymentP2020_03.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const PaymentP2020_03 = z.object({ 4 | payment_id: z.number().int().optional(), 5 | customer_id: z.number().int(), 6 | staff_id: z.number().int(), 7 | rental_id: z.number().int(), 8 | amount: z.number(), 9 | payment_date: z.string(), 10 | }); 11 | 12 | export type PaymentP2020_03T = z.infer; 13 | -------------------------------------------------------------------------------- /example/pagila/paymentP2020_04.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const PaymentP2020_04 = z.object({ 4 | payment_id: z.number().int().optional(), 5 | customer_id: z.number().int(), 6 | staff_id: z.number().int(), 7 | rental_id: z.number().int(), 8 | amount: z.number(), 9 | payment_date: z.string(), 10 | }); 11 | 12 | export type PaymentP2020_04T = z.infer; 13 | -------------------------------------------------------------------------------- /example/pagila/paymentP2020_05.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const PaymentP2020_05 = z.object({ 4 | payment_id: z.number().int().optional(), 5 | customer_id: z.number().int(), 6 | staff_id: z.number().int(), 7 | rental_id: z.number().int(), 8 | amount: z.number(), 9 | payment_date: z.string(), 10 | }); 11 | 12 | export type PaymentP2020_05T = z.infer; 13 | -------------------------------------------------------------------------------- /example/pagila/paymentP2020_06.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const PaymentP2020_06 = z.object({ 4 | payment_id: z.number().int().optional(), 5 | customer_id: z.number().int(), 6 | staff_id: z.number().int(), 7 | rental_id: z.number().int(), 8 | amount: z.number(), 9 | payment_date: z.string(), 10 | }); 11 | 12 | export type PaymentP2020_06T = z.infer; 13 | -------------------------------------------------------------------------------- /example/pagila/rental.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const Rental = z.object({ 4 | rental_id: z.number().int().optional(), 5 | rental_date: z.string(), 6 | inventory_id: z.number().int(), 7 | customer_id: z.number().int(), 8 | return_date: z.string().nullable().optional(), 9 | staff_id: z.number().int(), 10 | last_update: z.string().optional(), 11 | }); 12 | 13 | export type RentalT = z.infer; 14 | -------------------------------------------------------------------------------- /example/pagila/salesByFilmCategory.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const SalesByFilmCategory = z.object({ 4 | category: z.string().nullable().optional(), 5 | total_sales: z.number().nullable().optional(), 6 | }); 7 | 8 | export type SalesByFilmCategoryT = z.infer; 9 | -------------------------------------------------------------------------------- /example/pagila/salesByStore.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const SalesByStore = z.object({ 4 | store: z.string().nullable().optional(), 5 | manager: z.string().nullable().optional(), 6 | total_sales: z.number().nullable().optional(), 7 | }); 8 | 9 | export type SalesByStoreT = z.infer; 10 | -------------------------------------------------------------------------------- /example/pagila/staff.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const Staff = z.object({ 4 | staff_id: z.number().int().optional(), 5 | first_name: z.string(), 6 | last_name: z.string(), 7 | address_id: z.number().int(), 8 | email: z.string().nullable().optional(), 9 | store_id: z.number().int(), 10 | active: z.boolean().optional(), 11 | username: z.string(), 12 | password: z.string().nullable().optional(), 13 | last_update: z.string().optional(), 14 | picture: z.bytea().nullable().optional(), 15 | }); 16 | 17 | export type StaffT = z.infer; 18 | -------------------------------------------------------------------------------- /example/pagila/staffList.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const StaffList = z.object({ 4 | id: z.number().int().nullable().optional(), 5 | name: z.string().nullable().optional(), 6 | address: z.string().nullable().optional(), 7 | zip code: z.string().nullable().optional(), 8 | phone: z.string().nullable().optional(), 9 | city: z.string().nullable().optional(), 10 | country: z.string().nullable().optional(), 11 | sid: z.number().int().nullable().optional(), 12 | }); 13 | 14 | export type StaffListT = z.infer; 15 | -------------------------------------------------------------------------------- /example/pagila/store.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const Store = z.object({ 4 | store_id: z.number().int().optional(), 5 | manager_staff_id: z.number().int(), 6 | address_id: z.number().int(), 7 | last_update: z.string().optional(), 8 | }); 9 | 10 | export type StoreT = z.infer; 11 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-undef 2 | module.exports = { 3 | roots: ['/src'], 4 | testMatch: [ 5 | "**/__tests__/**/*.+(ts|tsx|js)", 6 | "**/?(*.)+(spec|test).+(ts|tsx|js)" 7 | ], 8 | transform: { 9 | "^.+\\.(ts|tsx)$": "ts-jest" 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pgzod", 3 | "version": "3.3.0", 4 | "description": "Convert a database schema into a Zod schema", 5 | "author": "Guzmán Monné ", 6 | "repository": "https://github.com/guzmonne/pgzod", 7 | "license": "MIT", 8 | "keywords": [ 9 | "typescript", 10 | "postgresql", 11 | "zod", 12 | "schema" 13 | ], 14 | "main": "./dist/index.js", 15 | "types": "./dist/index.d.ts", 16 | "bin": { 17 | "pgzod": "./dist/index.js" 18 | }, 19 | "files": [ 20 | "dist", 21 | "dist/lib" 22 | ], 23 | "scripts": { 24 | "cli": "ts-node src/index.ts", 25 | "lint": "eslint src/ --ext .js,.jsx,.ts,.tsx", 26 | "test": "jest", 27 | "clean": "rm -rf dist build package", 28 | "ts-node": "ts-node", 29 | "docs": "typedoc --entryPoints src/index.ts", 30 | "prebuild": "yarn clean", 31 | "build": "tsc -p tsconfig.json", 32 | "prepublish": "yarn build" 33 | }, 34 | "devDependencies": { 35 | "@types/cli-progress": "^3.9.2", 36 | "@types/dedent": "^0.7.0", 37 | "@types/jest": "^26.0.21", 38 | "@types/node": "^15.0.1", 39 | "@types/yargs": "^17.0.4", 40 | "@typescript-eslint/eslint-plugin": "^4.19.0", 41 | "@typescript-eslint/parser": "^4.19.0", 42 | "esbuild": "^0.11.11", 43 | "eslint": "^7.22.0", 44 | "jest": "^26.6.3", 45 | "ts-jest": "^26.5.4", 46 | "ts-node": "^9.1.1", 47 | "typedoc": "^0.20.35", 48 | "typescript": "^4.2.3" 49 | }, 50 | "dependencies": { 51 | "change-case": "^4.1.2", 52 | "cli-progress": "^3.9.1", 53 | "cli-spinners": "^2.6.1", 54 | "dedent": "^0.7.0", 55 | "ora": "^6.1.0", 56 | "slonik": "^28.1.0", 57 | "yargs": "^17.2.1", 58 | "zod": "^3.11.4" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { handler } from '.'; 2 | 3 | describe('pgzod', () => { 4 | 5 | test('should exist', () => expect(handler).not.toBeUndefined()); 6 | 7 | }); 8 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { camelCase, pascalCase } from "change-case"; 4 | import { hideBin } from "yargs/helpers"; 5 | import { join } from "path"; 6 | import { mkdir, rm, writeFile } from "fs/promises"; 7 | import { sql, DatabasePool } from "slonik"; 8 | import type { Arguments, Argv } from "yargs"; 9 | import yargs from "yargs/yargs"; 10 | import { z } from "zod"; 11 | 12 | import { createPool } from "./lib/createPool"; 13 | import type { CreatePoolProps } from "./lib/createPool"; 14 | 15 | /** 16 | * PGZod strategy validator. 17 | */ 18 | const Strategy = z.union([z.literal("write"), z.literal("readwrite")]); 19 | type StrategyT = z.infer; 20 | /** 21 | * Default command name. 22 | */ 23 | export const command = "pgzod"; 24 | /** 25 | * Default command description. 26 | */ 27 | export const describe = "create Zod types for a postgres schema"; 28 | /** 29 | * Default command options. 30 | */ 31 | export type Options = { 32 | /** 33 | * Clean run flag 34 | */ 35 | clean: boolean; 36 | /** 37 | * Custom PosgreSQL types to Zod validators separated by an equal (=) sign. 38 | * E.g. --customZodTypes timestamptx=z.date() date=z.date() 39 | */ 40 | customZodTypes: string[]; 41 | /** 42 | * Validators ouput folder 43 | */ 44 | output: string; 45 | /** 46 | * Name of the schema to convert into zod validators. 47 | */ 48 | schema: string; 49 | /** 50 | * Select the strategy to be used to create the `Zod` schemas. 51 | */ 52 | strategy?: string; 53 | } & CreatePoolProps; 54 | /** 55 | * Yargs default command builder function. 56 | * Please refer to Yargs documentation to see how it works: 57 | * https://www.npmjs.com/package/yargs 58 | */ 59 | export const builder: (args: Argv>) => Argv = ( 60 | y 61 | ) => 62 | y 63 | .options({ 64 | clean: { 65 | type: "boolean", 66 | describe: "delete the current zod schema folder", 67 | default: true, 68 | }, 69 | customZodTypes: { 70 | type: "array", 71 | describe: 72 | "Custom PosgreSQL types to Zod validators separated by an equal (=) sign. E.g. --customZodTypes timestamptx=z.date() date=z.date()", 73 | default: [], 74 | }, 75 | output: { 76 | type: "string", 77 | describe: "zod schema output folder", 78 | default: "/tmp/pgzod", 79 | }, 80 | pgdatabase: { 81 | type: "string", 82 | describe: "database name", 83 | default: "postgres", 84 | }, 85 | pghost: { 86 | type: "string", 87 | describe: "database host", 88 | default: "127.0.0.1", 89 | }, 90 | pgpassword: { 91 | type: "string", 92 | describe: "database user password", 93 | default: "", 94 | }, 95 | pgport: { 96 | type: "string", 97 | describe: "database host port", 98 | default: "5432", 99 | }, 100 | pguser: { 101 | type: "string", 102 | describe: "database user", 103 | default: "postgres", 104 | }, 105 | schema: { 106 | type: "string", 107 | describe: "schema to convert into zod schema", 108 | default: "public", 109 | }, 110 | strategy: { 111 | type: "string", 112 | choices: ["write", "readwrite"], 113 | default: "write", 114 | }, 115 | }) 116 | // Deactivate the use of environment variables for option configurations. 117 | .env(true); 118 | /** 119 | * Yargs default command handler function. 120 | * Defaults are duplicated to allow importing this function from another module. 121 | * @param argv - Command options. 122 | */ 123 | export const handler = async ( 124 | argv: Arguments | Options 125 | ): Promise => { 126 | const { 127 | clean = true, 128 | customZodTypes = [], 129 | output = join(__dirname, "."), 130 | schema = "public", 131 | strategy = "write", 132 | pgdatabase, 133 | pghost, 134 | pgpassword, 135 | pgport, 136 | pguser, 137 | } = argv; 138 | 139 | try { 140 | const pool = createPool({ 141 | pgdatabase, 142 | pghost, 143 | pgpassword, 144 | pgport, 145 | pguser, 146 | }); 147 | 148 | console.info(`Fetching table list for schema: ${schema}`); 149 | // Get the list of tables inside the schema. 150 | const tables = await pool.any(sql` 151 | SELECT table_name FROM information_schema.tables WHERE table_schema = ${schema} ORDER BY table_name`); 152 | 153 | if (tables.length === 0) { 154 | console.error(`No tables were found on schema ${schema}`); 155 | process.exit(1); 156 | } 157 | 158 | console.info(`Fetching enums`); 159 | // Get all the enum custom types definitions. 160 | const customEnums = await pool.any(sql` 161 | SELECT t.typname as name, concat('"', string_agg(e.enumlabel, '","'), '"') AS value 162 | FROM pg_type t 163 | JOIN pg_enum e on t.oid = e.enumtypid 164 | JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace 165 | WHERE n.nspname = ${schema} 166 | GROUP BY name;`); 167 | const customEnumsTypesMap = customEnums.reduce( 168 | (acc, { name, value }) => ({ 169 | ...acc, 170 | [name]: `z.enum([${value.split(",").sort().join(", ")}])`, 171 | // Add support for array of custom enum types. From PostgreSQL logs: 172 | // 173 | // > When you define a new base type, PostgreSQL automatically provides support for arrays 174 | // > of that type. The array type typically has the same name as the base type with the 175 | // > underscore character (_) prepended. 176 | [`_${name}`]: `z.array(z.enum([${value 177 | .split(",") 178 | .sort() 179 | .join(", ")}]))`, 180 | }), 181 | {} 182 | ); 183 | 184 | const customZodTypesMap = customZodTypes.reduce((acc, pair) => { 185 | const [postgresType, zodValidator] = pair.split("="); 186 | return { ...acc, [postgresType]: zodValidator }; 187 | }, {}); 188 | 189 | // Create the types map 190 | const typesMap = createTypesMap({ 191 | ...customEnumsTypesMap, 192 | ...customZodTypesMap, 193 | }); 194 | 195 | // Create/Re-create the schema output folder 196 | if (clean) { 197 | await rm(output, { recursive: true }).catch( 198 | () => `output folder ${output} doesn't exist` 199 | ); 200 | } 201 | await mkdir(output).catch(() => `output folder ${output} already exist`); 202 | 203 | // Run the correct strategy to generate the files. 204 | runWithStrategy({ 205 | output, 206 | pool, 207 | strategy: Strategy.parse(strategy), 208 | tables, 209 | typesMap, 210 | }); 211 | } catch (err) { 212 | console.error(err); 213 | } 214 | }; 215 | // ========= 216 | // Functions 217 | // ========= 218 | /** 219 | * Create the PGZod files depending on a strategy. 220 | * 221 | * The `write` strategy only creates types for inserting data on the table. It's configured as the 222 | * default options for backwards compatibility with previous versions of PGZod. This strategy is 223 | * useful of your access patterns on your table are the same for reads and writes. 224 | * 225 | * The `reqdwrite` strategy will make PGZod create two types for each table. One for `reads` and one 226 | * for `writes`. Each type will be suffixed with `Read` or `Write`. This strategy is useful when you 227 | * have access patterns on your table that differ between `reads` and `writes`. For example, if 228 | * you have a `column` with a default value, including it on `writes` is optional, but on `read` it 229 | * will always be there. 230 | */ 231 | async function runWithStrategy({ 232 | output, 233 | pool, 234 | tables, 235 | typesMap, 236 | strategy = "write", 237 | }: StrategyOptions) { 238 | // Create the spinner 239 | console.info("Fetching tables metadata"); 240 | 241 | // The index variable will hold all the lines for the ./index.ts file. 242 | const index: string[] = []; 243 | 244 | for (const table of tables.values()) { 245 | const { table_name } = table; 246 | for (const filetype of strategy === "write" 247 | ? ["write"] 248 | : ["read", "write"]) { 249 | // Set the current progress 250 | console.info(`Fetching columns metadata for table: ${table_name}`); 251 | const columns = await pool.any(sql` 252 | SELECT is_generated, column_name, ordinal_position, column_default, is_nullable, data_type, udt_name 253 | FROM information_schema.columns 254 | WHERE table_name = ${table_name} 255 | ORDER BY ordinal_position`); 256 | 257 | const template = []; 258 | // Remove editorconfig checks on auto-generated files. 259 | template.push(`import { z } from 'zod';\n`); 260 | 261 | // Add json parsing according to Zod documentation. 262 | // https://github.com/colinhacks/zod#json-type 263 | if (columns.some((column) => column.udt_name === "jsonb")) { 264 | template.push(`type Literal = boolean | null | number | string;`); 265 | template.push( 266 | `type Json = Literal | { [key: string]: Json } | Json[];` 267 | ); 268 | template.push( 269 | `const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);` 270 | ); 271 | template.push(`const jsonSchema: z.ZodSchema = z.lazy(() =>`); 272 | template.push( 273 | ` z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)])` 274 | ); 275 | template.push(`);\n`); 276 | } 277 | 278 | const name = pascalCase( 279 | strategy === "write" ? table_name : table_name + "_" + filetype 280 | ); 281 | template.push(`export const ${name} = z.object({`); 282 | 283 | for (const column of columns) { 284 | const name = column.column_name; 285 | let line = `${name}: `; 286 | 287 | const type = typesMap[column.udt_name]; 288 | line += type; 289 | 290 | const isNullable = column.is_nullable === "YES"; 291 | line += isNullable ? ".nullable().optional()" : ""; 292 | 293 | const isOptional = 294 | !isNullable && 295 | filetype === "write" && 296 | (column.is_generated === "ALWAYS" || column.column_default !== null); 297 | line += isOptional ? ".optional()" : ""; 298 | 299 | template.push(` ${line},`); 300 | } 301 | 302 | template.push(`});\n`); 303 | template.push(`export type ${name}T = z.infer;\n`); 304 | 305 | const file = camelCase(name); 306 | await writeFile(join(output, `${file}.ts`), template.join("\n")); 307 | 308 | index.push(`export type { ${name}T } from './${file}';`); 309 | index.push(`export { ${name} } from './${file}';`); 310 | } 311 | } 312 | 313 | console.info("Writing index file"); 314 | await writeFile(join(output, `index.ts`), [...index, "\n"].join("\n")); 315 | 316 | console.info("Done"); 317 | } 318 | /** 319 | * Returns a PostgreSQL to Zod types map. 320 | * @param type - PostgreSQL data type. 321 | */ 322 | function createTypesMap(customZodTypes: Record) { 323 | /** 324 | * Map that translate PostgreSQL data types to Zod functions. 325 | */ 326 | const ZOD_TYPES_OVERRIDE: Record = { 327 | bool: `z.boolean()`, 328 | bpchar: `z.string()`, 329 | citext: `z.string()`, 330 | // TODO: Find a better way to handle dates. 331 | date: `z.string()`, 332 | float8: `z.number()`, 333 | int4: `z.number().int()`, 334 | jsonb: `jsonSchema`, 335 | numeric: `z.number()`, 336 | text: `z.string()`, 337 | // TODO: Find a better way to handle dates. 338 | timestamptz: `z.string()`, 339 | uuid: "z.string().uuid()", 340 | varchar: `z.string()`, 341 | interval: `z.number()`, 342 | }; 343 | const map = { ...ZOD_TYPES_OVERRIDE, ...customZodTypes }; 344 | const proxy = new Proxy(map, { 345 | get: (object, prop: string) => 346 | prop in object ? object[prop] : `z.${prop}()`, 347 | }); 348 | return proxy; 349 | } 350 | // ===== 351 | // Types 352 | // ===== 353 | /** 354 | * Represents the output of the information_schema.tables table. 355 | */ 356 | type InformationSchema = { 357 | table_name: string; 358 | }; 359 | /** 360 | * Represents the output of a query to get the list of custom enum types. 361 | */ 362 | type CustomEnumTypes = { 363 | /** 364 | * Name of the custom enum type. 365 | */ 366 | name: string; 367 | /** 368 | * List of valid enum values as a concatenated list of string separated by a comma (,). 369 | */ 370 | value: string; 371 | }; 372 | /** 373 | * Represents the output of the information_schema.colums table. 374 | */ 375 | type ColumnsInformation = { 376 | column_default: string | null; 377 | column_name: string; 378 | data_type: string; 379 | is_generated: "NEVER" | "ALWAYS"; 380 | is_nullable: "YES" | "NO"; 381 | ordinal_position: number; 382 | udt_name: string; 383 | }; 384 | /** 385 | * Represents the options needed to run a PGZod strategy. 386 | */ 387 | type StrategyOptions = { 388 | output: string; 389 | pool: DatabasePool; 390 | strategy?: StrategyT; 391 | tables: readonly InformationSchema[]; 392 | typesMap: { [key: string]: string }; 393 | }; 394 | // ================= 395 | // Standalone Module 396 | // ================= 397 | if (require.main === module) { 398 | yargs(hideBin(process.argv)) 399 | .env(true) 400 | .command({ 401 | command: "$0", 402 | describe, 403 | builder, 404 | handler, 405 | }) 406 | .parseAsync(); 407 | } 408 | -------------------------------------------------------------------------------- /src/lib/createPool.ts: -------------------------------------------------------------------------------- 1 | import { createPool as createSlonikPool } from 'slonik'; 2 | 3 | /** 4 | * Creates a database connection pool using `slonik`. 5 | * @param props - Create pool properties. 6 | */ 7 | export function createPool(props: CreatePoolProps = {}): ReturnType { 8 | const { 9 | pgdatabase = process.env.PGDATABASE || 'postgres', 10 | pghost = process.env.PGHOST || '127.0.0.1', 11 | pgpassword = process.env.PGPASSWORD || '', 12 | pgport = process.env.PGPORT || '5432', 13 | pguser = process.env.PGUSER || 'postgres', 14 | } = props; 15 | 16 | const connectionUri = `postgres://${pguser}:${pgpassword}@${pghost}:${pgport}/${pgdatabase}`; 17 | 18 | return createSlonikPool(connectionUri); 19 | } 20 | 21 | /** 22 | * Create pool properties. 23 | */ 24 | export type CreatePoolProps = { 25 | /** 26 | * Database name. 27 | */ 28 | pgdatabase?: string; 29 | /** 30 | * Database host. 31 | */ 32 | pghost?: string; 33 | /** 34 | * Database user password. 35 | */ 36 | pgpassword?: string; 37 | /** 38 | * Database host port. 39 | */ 40 | pgport?: string | number; 41 | /** 42 | * Database user. 43 | */ 44 | pguser?: string; 45 | } 46 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, 9 | "strictNullChecks": true /* Enable strict null checks. */, 10 | "strictFunctionTypes": true /* Enable strict checking of function types. */, 11 | "noUnusedLocals": true /* Report errors on unused locals. */, 12 | "noUnusedParameters": true /* Report errors on unused parameters. */, 13 | "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, 14 | "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, 15 | "importHelpers": true, 16 | "skipLibCheck": true, 17 | "esModuleInterop": true, 18 | "allowSyntheticDefaultImports": true, 19 | "experimentalDecorators": true, 20 | "sourceMap": true, 21 | "outDir": "./dist/", 22 | "types": [ 23 | "node", 24 | "jest" 25 | ], 26 | "lib": [ 27 | "ES6", 28 | ] 29 | }, 30 | "include": [ 31 | "src/**/*.ts" 32 | ], 33 | "exclude": [ 34 | "node_modules", 35 | "**/*.spec.ts" 36 | ] 37 | } 38 | --------------------------------------------------------------------------------