plain.postgres 0.84.0__py3-none-any.whl

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.
Files changed (93) hide show
  1. plain/postgres/CHANGELOG.md +1028 -0
  2. plain/postgres/README.md +925 -0
  3. plain/postgres/__init__.py +120 -0
  4. plain/postgres/agents/.claude/rules/plain-postgres.md +78 -0
  5. plain/postgres/aggregates.py +236 -0
  6. plain/postgres/backups/__init__.py +0 -0
  7. plain/postgres/backups/cli.py +148 -0
  8. plain/postgres/backups/clients.py +94 -0
  9. plain/postgres/backups/core.py +172 -0
  10. plain/postgres/base.py +1415 -0
  11. plain/postgres/cli/__init__.py +3 -0
  12. plain/postgres/cli/db.py +142 -0
  13. plain/postgres/cli/migrations.py +1085 -0
  14. plain/postgres/config.py +18 -0
  15. plain/postgres/connection.py +1331 -0
  16. plain/postgres/connections.py +77 -0
  17. plain/postgres/constants.py +13 -0
  18. plain/postgres/constraints.py +495 -0
  19. plain/postgres/database_url.py +94 -0
  20. plain/postgres/db.py +59 -0
  21. plain/postgres/default_settings.py +38 -0
  22. plain/postgres/deletion.py +475 -0
  23. plain/postgres/dialect.py +640 -0
  24. plain/postgres/entrypoints.py +4 -0
  25. plain/postgres/enums.py +103 -0
  26. plain/postgres/exceptions.py +217 -0
  27. plain/postgres/expressions.py +1912 -0
  28. plain/postgres/fields/__init__.py +2118 -0
  29. plain/postgres/fields/encrypted.py +354 -0
  30. plain/postgres/fields/json.py +413 -0
  31. plain/postgres/fields/mixins.py +30 -0
  32. plain/postgres/fields/related.py +1192 -0
  33. plain/postgres/fields/related_descriptors.py +290 -0
  34. plain/postgres/fields/related_lookups.py +223 -0
  35. plain/postgres/fields/related_managers.py +661 -0
  36. plain/postgres/fields/reverse_descriptors.py +229 -0
  37. plain/postgres/fields/reverse_related.py +328 -0
  38. plain/postgres/fields/timezones.py +143 -0
  39. plain/postgres/forms.py +773 -0
  40. plain/postgres/functions/__init__.py +189 -0
  41. plain/postgres/functions/comparison.py +127 -0
  42. plain/postgres/functions/datetime.py +454 -0
  43. plain/postgres/functions/math.py +140 -0
  44. plain/postgres/functions/mixins.py +59 -0
  45. plain/postgres/functions/text.py +282 -0
  46. plain/postgres/functions/window.py +125 -0
  47. plain/postgres/indexes.py +286 -0
  48. plain/postgres/lookups.py +758 -0
  49. plain/postgres/meta.py +584 -0
  50. plain/postgres/migrations/__init__.py +53 -0
  51. plain/postgres/migrations/autodetector.py +1379 -0
  52. plain/postgres/migrations/exceptions.py +54 -0
  53. plain/postgres/migrations/executor.py +188 -0
  54. plain/postgres/migrations/graph.py +364 -0
  55. plain/postgres/migrations/loader.py +377 -0
  56. plain/postgres/migrations/migration.py +180 -0
  57. plain/postgres/migrations/operations/__init__.py +34 -0
  58. plain/postgres/migrations/operations/base.py +139 -0
  59. plain/postgres/migrations/operations/fields.py +373 -0
  60. plain/postgres/migrations/operations/models.py +798 -0
  61. plain/postgres/migrations/operations/special.py +184 -0
  62. plain/postgres/migrations/optimizer.py +74 -0
  63. plain/postgres/migrations/questioner.py +340 -0
  64. plain/postgres/migrations/recorder.py +119 -0
  65. plain/postgres/migrations/serializer.py +378 -0
  66. plain/postgres/migrations/state.py +882 -0
  67. plain/postgres/migrations/utils.py +147 -0
  68. plain/postgres/migrations/writer.py +302 -0
  69. plain/postgres/options.py +207 -0
  70. plain/postgres/otel.py +231 -0
  71. plain/postgres/preflight.py +336 -0
  72. plain/postgres/query.py +2242 -0
  73. plain/postgres/query_utils.py +456 -0
  74. plain/postgres/registry.py +217 -0
  75. plain/postgres/schema.py +1885 -0
  76. plain/postgres/sql/__init__.py +40 -0
  77. plain/postgres/sql/compiler.py +1869 -0
  78. plain/postgres/sql/constants.py +22 -0
  79. plain/postgres/sql/datastructures.py +222 -0
  80. plain/postgres/sql/query.py +2947 -0
  81. plain/postgres/sql/where.py +374 -0
  82. plain/postgres/test/__init__.py +0 -0
  83. plain/postgres/test/pytest.py +117 -0
  84. plain/postgres/test/utils.py +18 -0
  85. plain/postgres/transaction.py +222 -0
  86. plain/postgres/types.py +92 -0
  87. plain/postgres/types.pyi +751 -0
  88. plain/postgres/utils.py +345 -0
  89. plain_postgres-0.84.0.dist-info/METADATA +937 -0
  90. plain_postgres-0.84.0.dist-info/RECORD +93 -0
  91. plain_postgres-0.84.0.dist-info/WHEEL +4 -0
  92. plain_postgres-0.84.0.dist-info/entry_points.txt +5 -0
  93. plain_postgres-0.84.0.dist-info/licenses/LICENSE +61 -0
@@ -0,0 +1,22 @@
1
+ """
2
+ Constants specific to the SQL storage portion of the ORM.
3
+ """
4
+
5
+ # Size of each "chunk" for get_iterator calls.
6
+ # Larger values are slightly faster at the expense of more storage space.
7
+ GET_ITERATOR_CHUNK_SIZE = 100
8
+
9
+ # How many results to expect from a cursor.execute call
10
+ MULTI = "multi"
11
+ SINGLE = "single"
12
+ CURSOR = "cursor"
13
+ NO_RESULTS = "no results"
14
+
15
+ ORDER_DIR = {
16
+ "ASC": ("ASC", "DESC"),
17
+ "DESC": ("DESC", "ASC"),
18
+ }
19
+
20
+ # SQL join types.
21
+ INNER = "INNER JOIN"
22
+ LOUTER = "LEFT OUTER JOIN"
@@ -0,0 +1,222 @@
1
+ """
2
+ Useful auxiliary data structures for query construction. Not useful outside
3
+ the SQL domain.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ from plain.postgres.dialect import quote_name
11
+ from plain.postgres.exceptions import FullResultSet
12
+ from plain.postgres.sql.constants import INNER, LOUTER
13
+
14
+ if TYPE_CHECKING:
15
+ from plain.postgres.connection import DatabaseConnection
16
+ from plain.postgres.fields.related import ForeignKeyField
17
+ from plain.postgres.fields.reverse_related import ForeignObjectRel
18
+ from plain.postgres.sql.compiler import SQLCompiler
19
+
20
+
21
+ class MultiJoin(Exception):
22
+ """
23
+ Used by join construction code to indicate the point at which a
24
+ multi-valued join was attempted (if the caller wants to treat that
25
+ exceptionally).
26
+ """
27
+
28
+ def __init__(
29
+ self, names_pos: int, path_with_names: list[tuple[str, list[Any]]]
30
+ ) -> None:
31
+ self.level = names_pos
32
+ # The path travelled, this includes the path to the multijoin.
33
+ self.names_with_path = path_with_names
34
+
35
+
36
+ class Empty:
37
+ pass
38
+
39
+
40
+ class Join:
41
+ """
42
+ Used by sql.Query and sql.SQLCompiler to generate JOIN clauses into the
43
+ FROM entry. For example, the SQL generated could be
44
+ LEFT OUTER JOIN "sometable" T1
45
+ ON ("othertable"."sometable_id" = "sometable"."id")
46
+
47
+ This class is primarily used in Query.alias_map. All entries in alias_map
48
+ must be Join compatible by providing the following attributes and methods:
49
+ - table_name (string)
50
+ - table_alias (possible alias for the table, can be None)
51
+ - join_type (can be None for those entries that aren't joined from
52
+ anything)
53
+ - parent_alias (which table is this join's parent, can be None similarly
54
+ to join_type)
55
+ - as_sql()
56
+ - relabeled_clone()
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ table_name: str,
62
+ parent_alias: str,
63
+ table_alias: str,
64
+ join_type: str,
65
+ join_field: ForeignKeyField | ForeignObjectRel,
66
+ nullable: bool,
67
+ filtered_relation: Any = None,
68
+ ) -> None:
69
+ # Join table
70
+ self.table_name = table_name
71
+ self.parent_alias = parent_alias
72
+ # Note: table_alias is not necessarily known at instantiation time.
73
+ self.table_alias = table_alias
74
+ # LOUTER or INNER
75
+ self.join_type = join_type
76
+ # A list of 2-tuples to use in the ON clause of the JOIN.
77
+ # Each 2-tuple will create one join condition in the ON clause.
78
+ self.join_cols = join_field.get_joining_columns()
79
+ # Along which field (or ForeignObjectRel in the reverse join case)
80
+ self.join_field = join_field
81
+ # Is this join nullabled?
82
+ self.nullable = nullable
83
+ self.filtered_relation = filtered_relation
84
+
85
+ def as_sql(
86
+ self, compiler: SQLCompiler, connection: DatabaseConnection
87
+ ) -> tuple[str, list[Any]]:
88
+ """
89
+ Generate the full
90
+ LEFT OUTER JOIN sometable ON sometable.somecol = othertable.othercol, params
91
+ clause for this join.
92
+ """
93
+ join_conditions = []
94
+ params = []
95
+ qn = compiler.quote_name_unless_alias
96
+ qn2 = quote_name
97
+
98
+ # Add a join condition for each pair of joining columns.
99
+ for lhs_col, rhs_col in self.join_cols:
100
+ join_conditions.append(
101
+ f"{qn(self.parent_alias)}.{qn2(lhs_col)} = {qn(self.table_alias)}.{qn2(rhs_col)}"
102
+ )
103
+
104
+ if self.filtered_relation:
105
+ try:
106
+ extra_sql, extra_params = compiler.compile(self.filtered_relation)
107
+ except FullResultSet:
108
+ pass
109
+ else:
110
+ join_conditions.append(f"({extra_sql})")
111
+ params.extend(extra_params)
112
+ if not join_conditions:
113
+ # This might be a rel on the other end of an actual declared field.
114
+ declared_field = getattr(self.join_field, "field", self.join_field)
115
+ raise ValueError(
116
+ f"Join generated an empty ON clause. {declared_field.__class__} did not yield either "
117
+ "joining columns or extra restrictions."
118
+ )
119
+ on_clause_sql = " AND ".join(join_conditions)
120
+ alias_str = (
121
+ "" if self.table_alias == self.table_name else (f" {self.table_alias}")
122
+ )
123
+ sql = f"{self.join_type} {qn(self.table_name)}{alias_str} ON ({on_clause_sql})"
124
+ return sql, params
125
+
126
+ def relabeled_clone(self, change_map: dict[str, str]) -> Join:
127
+ new_parent_alias = change_map.get(self.parent_alias, self.parent_alias)
128
+ new_table_alias = change_map.get(self.table_alias, self.table_alias)
129
+ if self.filtered_relation is not None:
130
+ filtered_relation = self.filtered_relation.clone()
131
+ filtered_relation.path = [
132
+ change_map.get(p, p) for p in self.filtered_relation.path
133
+ ]
134
+ else:
135
+ filtered_relation = None
136
+ return self.__class__(
137
+ self.table_name,
138
+ new_parent_alias,
139
+ new_table_alias,
140
+ self.join_type,
141
+ self.join_field,
142
+ self.nullable,
143
+ filtered_relation=filtered_relation,
144
+ )
145
+
146
+ @property
147
+ def identity(self) -> tuple[type[Join], str, str, Any, Any]:
148
+ return (
149
+ self.__class__,
150
+ self.table_name,
151
+ self.parent_alias,
152
+ self.join_field,
153
+ self.filtered_relation,
154
+ )
155
+
156
+ def __eq__(self, other: object) -> bool:
157
+ if not isinstance(other, Join):
158
+ return NotImplemented
159
+ return self.identity == other.identity
160
+
161
+ def __hash__(self) -> int:
162
+ return hash(self.identity)
163
+
164
+ def equals(self, other: Join) -> bool:
165
+ # Ignore filtered_relation in equality check.
166
+ return self.identity[:-1] == other.identity[:-1]
167
+
168
+ def demote(self) -> Join:
169
+ new = self.relabeled_clone({})
170
+ new.join_type = INNER
171
+ return new
172
+
173
+ def promote(self) -> Join:
174
+ new = self.relabeled_clone({})
175
+ new.join_type = LOUTER
176
+ return new
177
+
178
+
179
+ class BaseTable:
180
+ """
181
+ The BaseTable class is used for base table references in FROM clause. For
182
+ example, the SQL "foo" in
183
+ SELECT * FROM "foo" WHERE somecond
184
+ could be generated by this class.
185
+ """
186
+
187
+ join_type = None
188
+ parent_alias = None
189
+ filtered_relation = None
190
+
191
+ def __init__(self, table_name: str, alias: str) -> None:
192
+ self.table_name = table_name
193
+ self.table_alias = alias
194
+
195
+ def as_sql(
196
+ self, compiler: SQLCompiler, connection: DatabaseConnection
197
+ ) -> tuple[str, list[Any]]:
198
+ alias_str = (
199
+ "" if self.table_alias == self.table_name else (f" {self.table_alias}")
200
+ )
201
+ base_sql = compiler.quote_name_unless_alias(self.table_name)
202
+ return base_sql + alias_str, []
203
+
204
+ def relabeled_clone(self, change_map: dict[str, str]) -> BaseTable:
205
+ return self.__class__(
206
+ self.table_name, change_map.get(self.table_alias, self.table_alias)
207
+ )
208
+
209
+ @property
210
+ def identity(self) -> tuple[type[BaseTable], str, str]:
211
+ return self.__class__, self.table_name, self.table_alias
212
+
213
+ def __eq__(self, other: object) -> bool:
214
+ if not isinstance(other, BaseTable):
215
+ return NotImplemented
216
+ return self.identity == other.identity
217
+
218
+ def __hash__(self) -> int:
219
+ return hash(self.identity)
220
+
221
+ def equals(self, other: BaseTable) -> bool:
222
+ return self.identity == other.identity