supabase-pydantic 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Kevin Boehm <kmbhm1@gmail.com>
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.
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.1
2
+ Name: supabase-pydantic
3
+ Version: 0.2.1
4
+ Summary: A project to translate Supabase schemas into Pydantic models
5
+ License: MIT
6
+ Author: K Boehm
7
+ Author-email: kmbhm1@gmail.com
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: Development Status :: 1 - Planning
10
+ Classifier: Environment :: Console
11
+ Classifier: Framework :: FastAPI
12
+ Classifier: Framework :: Pydantic
13
+ Classifier: Framework :: Pydantic :: 2
14
+ Classifier: Framework :: Pytest
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Natural Language :: English
18
+ Classifier: Operating System :: MacOS :: MacOS X
19
+ Classifier: Programming Language :: Python
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Requires-Dist: click (>=8.1.7,<9.0.0)
25
+ Requires-Dist: faker (>=24.11.0,<25.0.0)
26
+ Requires-Dist: isort (>=5.13.2,<6.0.0)
27
+ Requires-Dist: psycopg2-binary (>=2.9.9,<3.0.0)
28
+ Requires-Dist: pydantic (>=2.6.3,<3.0.0)
29
+ Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
30
+ Requires-Dist: types-psycopg2 (>=2.9.21.20240417,<3.0.0.0)
31
+ Description-Content-Type: text/markdown
32
+
33
+ # Supabase Pydantic Schemas
34
+
35
+ A project for digesting Supabase (or Postgres) table & view schemas into Pydantic models. Currently, this is ideal for projects that integrate [FastAPI](https://fastapi.tiangolo.com/) with [Supabase-Python](https://supabase.com/docs/reference/python/introduction), but more updates are coming soon ...
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ $ pip install supabase-pydantic # install
41
+ $ touch .env # create .env file
42
+ $ echo "DB_NAME=<your_db_name>" >> .env # add your postgres db name
43
+ $ echo "DB_USER=<your_db_user>" >> .env # add your postgres db user
44
+ $ echo "DB_PASS=<your_db_password>" >> .env # add your postgres db password
45
+ $ echo "DB_HOST=<your_db_host>" >> .env # add your postgres db host
46
+ $ echo "DB_PORT=<your_db_port>" >> .env # add your postgres db port
47
+ ```
48
+
49
+ ## Usage
50
+
51
+ ```bash
52
+ $ sb-pydantic # generate default schemas.py to ./entities/fastapi
53
+ ```
54
+
@@ -0,0 +1,21 @@
1
+ # Supabase Pydantic Schemas
2
+
3
+ A project for digesting Supabase (or Postgres) table & view schemas into Pydantic models. Currently, this is ideal for projects that integrate [FastAPI](https://fastapi.tiangolo.com/) with [Supabase-Python](https://supabase.com/docs/reference/python/introduction), but more updates are coming soon ...
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ $ pip install supabase-pydantic # install
9
+ $ touch .env # create .env file
10
+ $ echo "DB_NAME=<your_db_name>" >> .env # add your postgres db name
11
+ $ echo "DB_USER=<your_db_user>" >> .env # add your postgres db user
12
+ $ echo "DB_PASS=<your_db_password>" >> .env # add your postgres db password
13
+ $ echo "DB_HOST=<your_db_host>" >> .env # add your postgres db host
14
+ $ echo "DB_PORT=<your_db_port>" >> .env # add your postgres db port
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```bash
20
+ $ sb-pydantic # generate default schemas.py to ./entities/fastapi
21
+ ```
@@ -0,0 +1,211 @@
1
+ [tool.poetry]
2
+ name = "supabase-pydantic"
3
+ version = "0.2.1"
4
+ description = "A project to translate Supabase schemas into Pydantic models"
5
+ authors = ["K Boehm <kmbhm1@gmail.com>"]
6
+ license = "MIT"
7
+ readme = "README.md"
8
+ classifiers = [
9
+ "Development Status :: 1 - Planning",
10
+ "Environment :: Console",
11
+ "Framework :: FastAPI",
12
+ "Framework :: Pydantic",
13
+ "Framework :: Pydantic :: 2",
14
+ "Framework :: Pytest",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Natural Language :: English",
18
+ "Operating System :: MacOS :: MacOS X",
19
+ "Programming Language :: Python",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ ]
23
+
24
+ [tool.poetry.scripts]
25
+ sb-pydantic = 'supabase_pydantic.cli:main'
26
+ supabase-pydantic = 'supabase_pydantic.cli:main'
27
+
28
+ [tool.poetry.dependencies]
29
+ python = "^3.10"
30
+ psycopg2-binary = "^2.9.9"
31
+ pydantic = "^2.6.3"
32
+ types-psycopg2 = "^2.9.21.20240417"
33
+ faker = "^24.11.0"
34
+ isort = "^5.13.2"
35
+ python-dotenv = "^1.0.1"
36
+ click = "^8.1.7"
37
+
38
+ [tool.poetry.group.dev.dependencies]
39
+ ruff = "^0.4.1"
40
+ sqlalchemy = {extras = ["mypy"], version = "^2.0.31"}
41
+ fastapi-jsonapi = "^2.8.0"
42
+ pytest = "^8.2.2"
43
+ python-semantic-release = "^9.8.5"
44
+
45
+ [tool.ruff]
46
+ line-length = 120
47
+ indent-width = 4
48
+ target-version = "py310"
49
+
50
+ [tool.ruff.lint]
51
+ select = [
52
+ "E", # pycodestyle
53
+ "F", # Pyflakes
54
+ "UP", # pyupgrade
55
+ "D", # pydocstyle
56
+ "I", # isort
57
+ "Q", # flake8-quotes
58
+ "W", # pycodestyle
59
+ ]
60
+ exclude = [
61
+ ".git",
62
+ ".venv",
63
+ "__pycache__",
64
+ "build",
65
+ "dist",
66
+ "docs",
67
+ # "notebooks",
68
+ "**/*tests*",
69
+ "scripts",
70
+ "setup.py",
71
+ "setup.cfg",
72
+ "pyproject.toml",
73
+ "poetry.lock",
74
+ "pyrightconfig.json",
75
+ ".*.yml",
76
+ ".*.yaml",
77
+ ".*.toml",
78
+ ".*.md",
79
+ ".*.txt",
80
+ ".*.rst",
81
+ ".*.json",
82
+ ".*.html",
83
+ ".*.css",
84
+ ".*.js",
85
+ ".*.png",
86
+ ".*.jpg",
87
+ ".*.jpeg",
88
+ ".*.gif",
89
+ ".*.svg",
90
+ ".*.ico",
91
+ ".*.eot",
92
+ ".*.ttf",
93
+ ".*.woff",
94
+ ".*.woff2",
95
+ ".*.map",
96
+ # ".*.ipynb",
97
+ ".*.pyi",
98
+ ".*.pyc",
99
+ ".*.pyd",
100
+ ".*.pyo",
101
+ ".*.pyw",
102
+ ".*.pyz",
103
+ ".*.pyzw",
104
+ ".*.DS_Store",
105
+ ".*.gitignore",
106
+ ".*.gitkeep",
107
+ ".*.gitmodules",
108
+ ".*.gitattributes",
109
+ ".*.git",
110
+ ".*.hgignore",
111
+ ".*.hgkeep",
112
+ ".*.",
113
+ ]
114
+ ignore = [
115
+ "D100", # Missing docstring in public module
116
+ "D101", # Missing docstring in public class
117
+ "D104", # Missing docstring in public package
118
+ "D105", # Missing docstring in magic method
119
+ "D107", # Missing docstring in __init__
120
+ "D202", # No blank lines allowed after function docstring
121
+ "D206", # Docstring should be indented with spaces, not tabs
122
+ "E101", # indentation contains mixed spaces and tabs
123
+ "F403", # 'from module import *' used; unable to detect undefined names
124
+ "Q000", # Remove bad quotes
125
+ "Q002", # Remove bad quotes
126
+ "Q003", # Unnecessary single quotes
127
+ "W191", # Indentation contains tabs
128
+ ]
129
+
130
+ [tool.ruff.lint.flake8-quotes]
131
+ docstring-quotes = "single"
132
+
133
+ [tool.ruff.lint.pydocstyle]
134
+ convention = "google"
135
+
136
+ [tool.ruff.format]
137
+ quote-style = "single"
138
+ indent-style = "space"
139
+ skip-magic-trailing-comma = true
140
+ line-ending = "auto"
141
+
142
+ [tool.mypy]
143
+ plugins = ["sqlalchemy.ext.mypy.plugin"]
144
+
145
+ [build-system]
146
+ requires = ["poetry-core"]
147
+ build-backend = "poetry.core.masonry.api"
148
+
149
+ [tool.semantic_release]
150
+ allow_zero_version = true
151
+ assets = []
152
+ branch = "main"
153
+ build_command = "poetry build"
154
+ build_command_env = []
155
+ changelog_file = "CHANGELOG.md"
156
+ commit_message = "{version}\n\nAutomatically generated by python-semantic-release"
157
+ commit_parser = "angular"
158
+ dist_path = "dist/"
159
+ logging_use_named_masks = false
160
+ major_on_zero = true
161
+ no_git_verify = false
162
+ patch_without_tag = true
163
+ remove_dist = false
164
+ tag_format = "v{version}"
165
+ upload_to_pypi = false
166
+ upload_to_release = true
167
+ version_toml = ["pyproject.toml:tool.poetry.version"]
168
+
169
+ [tool.semantic_release.branches.main]
170
+ match = "(main|master)"
171
+ prerelease_token = "rc"
172
+ prerelease = false
173
+
174
+ [tool.semantic_release.changelog]
175
+ changelog_file = "CHANGELOG.md"
176
+ exclude_commit_patterns = []
177
+
178
+ [tool.semantic_release.changelog.environment]
179
+ block_start_string = "{%"
180
+ block_end_string = "%}"
181
+ variable_start_string = "{{"
182
+ variable_end_string = "}}"
183
+ comment_start_string = "{#"
184
+ comment_end_string = "#}"
185
+ trim_blocks = false
186
+ lstrip_blocks = false
187
+ newline_sequence = "\n"
188
+ keep_trailing_newline = false
189
+ extensions = []
190
+ autoescape = true
191
+
192
+ [tool.semantic_release.commit_author]
193
+ env = "GIT_COMMIT_AUTHOR"
194
+ default = "KB <kmbhm1@gmail.com>"
195
+
196
+ [tool.semantic_release.commit_parser_options]
197
+ allowed_tags = ["build", "chore", "ci", "docs", "feat", "feature", "fix", "perf", "style", "refactor", "test"]
198
+ minor_tags = ["feat", "feature"]
199
+ patch_tags = ["fix", "perf"]
200
+ default_bump_level = 0
201
+
202
+ [tool.semantic_release.remote]
203
+ name = "origin"
204
+ type = "github"
205
+ ignore_token_for_push = false
206
+ insecure = false
207
+
208
+ [tool.semantic_release.publish]
209
+ dist_glob_patterns = ["dist/*"]
210
+ upload_to_vcs_release = true
211
+
@@ -0,0 +1,125 @@
1
+ import os
2
+ import pprint
3
+
4
+ from dotenv import find_dotenv, load_dotenv
5
+
6
+ from supabase_pydantic.util import (
7
+ construct_table_info,
8
+ create_connection,
9
+ query_database,
10
+ check_connection,
11
+ GET_ALL_PUBLIC_TABLES_AND_COLUMNS,
12
+ GET_TABLE_COLUMN_DETAILS,
13
+ write_pydantic_model_string,
14
+ run_isort,
15
+ # write_sqlalchemy_model_string,
16
+ # write_jsonapi_pydantic_model_string,
17
+ )
18
+ from supabase_pydantic.util.constants import GET_CONSTRAINTS
19
+ import click
20
+
21
+ # Pretty print for testing
22
+ pp = pprint.PrettyPrinter(indent=4)
23
+
24
+ # Load environment variables from .env file
25
+ load_dotenv(find_dotenv())
26
+
27
+ # Replace these variables with your database connection details
28
+ db_name = os.environ.get('DB_NAME')
29
+ user = os.environ.get('DB_USER')
30
+ password = os.environ.get('DB_PASS')
31
+ host = os.environ.get('DB_HOST')
32
+ port = os.environ.get('DB_PORT')
33
+
34
+
35
+ def reload_env() -> tuple:
36
+ # Load environment variables from .env file
37
+ load_dotenv(find_dotenv())
38
+
39
+ # Replace these variables with your database connection details
40
+ db_name = os.environ.get('DB_NAME')
41
+ user = os.environ.get('DB_USER')
42
+ password = os.environ.get('DB_PASS')
43
+ host = os.environ.get('DB_HOST')
44
+ port = os.environ.get('DB_PORT')
45
+
46
+ return db_name, user, password, host, port
47
+
48
+
49
+ def check_readiness():
50
+ """Check if environment variables are set correctly."""
51
+ # db_name, user, password, host, port = reload_env()
52
+ check = {'DB_NAME': db_name, 'DB_USER': user, 'DB_PASS': password, 'DB_HOST': host, 'DB_PORT': port}
53
+ for k, v in check.items():
54
+ # print(k, v)
55
+ if v is None:
56
+ print(f'Environment variables not set correctly. {k} is missing. Please set it in .env file.')
57
+ print('Exiting...')
58
+ return False
59
+
60
+ return True
61
+
62
+
63
+ @click.command()
64
+ @click.option('--directory', default='entities', help='The directory .')
65
+ @click.option('--verbose', is_flag=True, help='Print verbose output.')
66
+ def main(directory: str, verbose: bool = False):
67
+ """A CLI tool to generate Pydantic models from a PostgreSQL database."""
68
+
69
+ # Load environment variables from .env file & check if they are set correctly
70
+ load_dotenv(find_dotenv())
71
+ assert check_readiness()
72
+
73
+ default_directory = directory
74
+ # jsonapi_directory = os.path.join(default_directory, 'fastapi_jsonapi')
75
+ fastapi_directory = os.path.join(default_directory, 'fastapi')
76
+
77
+ try:
78
+ # Create a connection to the database & check if connection is successful
79
+ conn = create_connection(db_name, user, password, host, port)
80
+ assert check_connection(conn)
81
+
82
+ # Fetch table column details & foreign key details
83
+ column_details = query_database(conn, GET_ALL_PUBLIC_TABLES_AND_COLUMNS)
84
+ fk_details = query_database(conn, GET_TABLE_COLUMN_DETAILS)
85
+ constraints = query_database(conn, GET_CONSTRAINTS)
86
+ tables = construct_table_info(column_details, fk_details, constraints)
87
+ except Exception as e:
88
+ raise e
89
+ finally:
90
+ if conn:
91
+ conn.close()
92
+ print('Connection closed.')
93
+
94
+ # Create Pydantic models
95
+ pydantic_models_string = write_pydantic_model_string(tables)
96
+ pydantic_schemas_path = os.path.join(fastapi_directory, 'schemas.py')
97
+
98
+ # sql_alchemy_models_string = write_sqlalchemy_model_string(tables)
99
+ # sql_alchemy_models_path = os.path.join(jsonapi_directory, 'database.py')
100
+
101
+ # pydantic_jsonapi_models_string = write_jsonapi_pydantic_model_string(tables)
102
+ # pydantic_jsonapi_schemas_path = os.path.join(jsonapi_directory, 'schemas.py')
103
+
104
+ content = [pydantic_models_string] # , sql_alchemy_models_string, pydantic_jsonapi_models_string]
105
+ path = [pydantic_schemas_path] # , sql_alchemy_models_path, pydantic_jsonapi_schemas_path]
106
+
107
+ # Check if the directory exists, if not, create it
108
+ for d in [default_directory, fastapi_directory]: # , jsonapi_directory]:
109
+ if not os.path.exists(d):
110
+ os.makedirs(d)
111
+
112
+ # Define the full path to the file
113
+ for s, fp in zip(content, path):
114
+ with open(fp, 'w') as file:
115
+ file.write(s)
116
+
117
+ try:
118
+ run_isort(fp)
119
+ except Exception as e:
120
+ print('An error occurred while running isort.')
121
+ print(e)
122
+
123
+
124
+ if __name__ == '__main__':
125
+ main()
@@ -0,0 +1,43 @@
1
+ from .constants import (
2
+ RelationType,
3
+ ModelGenerationType,
4
+ GET_ALL_PUBLIC_TABLES_AND_COLUMNS,
5
+ GET_TABLE_COLUMN_DETAILS,
6
+ PYDANTIC_TYPE_MAP,
7
+ )
8
+ from .json import CustomJsonEncoder
9
+ from .fake import generate_fake_data
10
+ from .dataclasses import AsDictParent, ColumnInfo, ForeignKeyInfo, TableInfo
11
+ from .db import create_connection, query_database, check_connection
12
+ from .marshalers import construct_table_info
13
+ from .string import to_pascal_case
14
+ from .generator_helpers import (
15
+ write_pydantic_model_string,
16
+ run_isort,
17
+ write_sqlalchemy_model_string,
18
+ write_jsonapi_pydantic_model_string,
19
+ )
20
+
21
+ __all__ = [
22
+ 'to_pascal_case',
23
+ 'create_connection',
24
+ 'check_connection',
25
+ 'CustomJsonEncoder',
26
+ 'generate_fake_data',
27
+ 'AsDictParent',
28
+ 'ColumnInfo',
29
+ 'ForeignKeyInfo',
30
+ 'RelationType',
31
+ 'Model',
32
+ 'ModelGenerationType',
33
+ 'GET_ALL_PUBLIC_TABLES_AND_COLUMNS',
34
+ 'GET_TABLE_COLUMN_DETAILS',
35
+ 'TableInfo',
36
+ 'PYDANTIC_TYPE_MAP',
37
+ 'construct_table_info',
38
+ 'query_database',
39
+ 'write_pydantic_model_string',
40
+ 'run_isort',
41
+ 'write_sqlalchemy_model_string',
42
+ 'write_jsonapi_pydantic_model_string',
43
+ ]
@@ -0,0 +1,128 @@
1
+ from enum import Enum
2
+
3
+
4
+ class RelationType(str, Enum):
5
+ ONE_TO_ONE = 'One-to-One'
6
+ ONE_TO_MANY = 'One-to-Many'
7
+ MANY_TO_MANY = 'Many-to-Many'
8
+
9
+
10
+ class ModelGenerationType(str, Enum):
11
+ PARENT = 'PARENT'
12
+ MAIN = 'MAIN'
13
+
14
+
15
+ CUSTOM_MODEL_NAME = 'CustomModel'
16
+ CUSTOM_JSONAPI_META_MODEL_NAME = 'PydanticBaseModel'
17
+ BASE_CLASS_POSTFIX = 'BaseSchema'
18
+
19
+ CONSTRAINT_TYPE_MAP = {'p': 'PRIMARY KEY', 'f': 'FOREIGN KEY', 'u': 'UNIQUE', 'c': 'CHECK', 'x': 'EXCLUDE'}
20
+
21
+
22
+ PYDANTIC_TYPE_MAP: dict[str, tuple[str, str | None]] = {
23
+ 'integer': ('int', None),
24
+ 'bigint': ('int', None),
25
+ 'smallint': ('int', None),
26
+ 'numeric': ('float', None),
27
+ 'text': ('str', None),
28
+ 'varchar': ('str', None),
29
+ 'character varying': ('str', None),
30
+ 'boolean': ('bool', None),
31
+ 'timestamp': ('datetime', 'from datetime import datetime'),
32
+ 'timestamp with time zone': ('datetime', 'from datetime import datetime'),
33
+ 'timestamp without time zone': ('datetime', 'from datetime import datetime'),
34
+ 'date': ('datetime.date', 'from datetime import date'),
35
+ 'uuid': ('UUID4', 'from pydantic import UUID4'),
36
+ 'json': ('dict | Json', 'from pydantic import Json'),
37
+ 'jsonb': ('dict | Json', 'from pydantic import Json'),
38
+ 'ARRAY': ('list', None),
39
+ # Add more data types and their imports as necessary.
40
+ }
41
+
42
+
43
+ SQLALCHEMY_TYPE_MAP: dict[str, tuple[str, str | None]] = {
44
+ 'integer': ('Integer', 'from sqlalchemy import Integer'),
45
+ 'bigint': ('BigInteger', 'from sqlalchemy import BigInteger'),
46
+ 'smallint': ('SmallInteger', 'from sqlalchemy import SmallInteger'),
47
+ 'numeric': ('Numeric', 'from sqlalchemy import Numeric'),
48
+ 'text': ('Text', 'from sqlalchemy import Text'),
49
+ 'varchar': ('String', 'from sqlalchemy import String'),
50
+ 'character varying': ('String', 'from sqlalchemy import String'),
51
+ 'boolean': ('Boolean', 'from sqlalchemy import Boolean'),
52
+ 'timestamp': ('DateTime', 'from sqlalchemy import DateTime'),
53
+ 'timestamp with time zone': ('DateTime', 'from sqlalchemy.dialects.postgresql import TIMESTAMP'),
54
+ 'timestamp without time zone': ('DateTime', 'from sqlalchemy import DateTime'),
55
+ 'date': ('Date', 'from sqlalchemy import Date'),
56
+ 'uuid': ('UUID', 'from sqlalchemy.dialects.postgresql import UUID'),
57
+ 'json': ('JSON', 'from sqlalchemy import JSON'),
58
+ 'jsonb': ('JSONB', 'from sqlalchemy.dialects.postgresql import JSONB'),
59
+ 'ARRAY': ('ARRAY', 'from sqlalchemy.dialects.postgresql import ARRAY'),
60
+ # Add more data types and their imports as necessary.
61
+ }
62
+
63
+
64
+ # Queries
65
+
66
+ GET_TABLE_COLUMN_DETAILS = """
67
+ SELECT
68
+ tc.table_schema,
69
+ tc.table_name,
70
+ kcu.column_name,
71
+ ccu.table_schema AS foreign_table_schema,
72
+ ccu.table_name AS foreign_table_name,
73
+ ccu.column_name AS foreign_column_name,
74
+ tc.constraint_name
75
+ FROM
76
+ information_schema.table_constraints AS tc
77
+ JOIN
78
+ information_schema.key_column_usage AS kcu
79
+ ON tc.constraint_name = kcu.constraint_name
80
+ AND tc.table_schema = kcu.table_schema
81
+ JOIN
82
+ information_schema.constraint_column_usage AS ccu
83
+ ON ccu.constraint_name = tc.constraint_name
84
+ AND ccu.table_schema = tc.table_schema
85
+ WHERE
86
+ tc.constraint_type = 'FOREIGN KEY'
87
+ AND tc.table_schema = 'public';
88
+ """
89
+
90
+ GET_ALL_PUBLIC_TABLES_AND_COLUMNS = """
91
+ SELECT
92
+ c.table_schema,
93
+ c.table_name,
94
+ c.column_name,
95
+ c.column_default,
96
+ c.is_nullable,
97
+ c.data_type,
98
+ c.character_maximum_length,
99
+ t.table_type
100
+ FROM
101
+ information_schema.columns AS c
102
+ JOIN
103
+ information_schema.tables AS t ON c.table_name = t.table_name AND c.table_schema = t.table_schema
104
+ WHERE
105
+ c.table_schema = 'public'
106
+ AND (t.table_type = 'BASE TABLE' OR t.table_type = 'VIEW')
107
+ ORDER BY
108
+ c.table_schema, c.table_name, c.ordinal_position;
109
+ """
110
+
111
+ GET_CONSTRAINTS = """
112
+ SELECT
113
+ conname AS constraint_name,
114
+ conrelid::regclass AS table_name,
115
+ array_agg(a.attname) AS columns,
116
+ contype AS constraint_type,
117
+ pg_get_constraintdef(oid) AS constraint_definition
118
+ FROM
119
+ pg_constraint
120
+ JOIN
121
+ pg_attribute AS a ON a.attnum = ANY (conkey) AND a.attrelid = conrelid
122
+ WHERE
123
+ connamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public')
124
+ GROUP BY
125
+ conname, conrelid, contype, oid
126
+ ORDER BY
127
+ conrelid::regclass::text, contype DESC;
128
+ """