half-orm-dev 0.16.0a9__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.
- half_orm_dev/__init__.py +1 -0
- half_orm_dev/cli/__init__.py +9 -0
- half_orm_dev/cli/commands/__init__.py +56 -0
- half_orm_dev/cli/commands/apply.py +13 -0
- half_orm_dev/cli/commands/clone.py +102 -0
- half_orm_dev/cli/commands/init.py +331 -0
- half_orm_dev/cli/commands/new.py +15 -0
- half_orm_dev/cli/commands/patch.py +317 -0
- half_orm_dev/cli/commands/prepare.py +21 -0
- half_orm_dev/cli/commands/prepare_release.py +119 -0
- half_orm_dev/cli/commands/promote_to.py +127 -0
- half_orm_dev/cli/commands/release.py +344 -0
- half_orm_dev/cli/commands/restore.py +14 -0
- half_orm_dev/cli/commands/sync.py +13 -0
- half_orm_dev/cli/commands/todo.py +73 -0
- half_orm_dev/cli/commands/undo.py +17 -0
- half_orm_dev/cli/commands/update.py +73 -0
- half_orm_dev/cli/commands/upgrade.py +191 -0
- half_orm_dev/cli/main.py +103 -0
- half_orm_dev/cli_extension.py +38 -0
- half_orm_dev/database.py +1389 -0
- half_orm_dev/hgit.py +1025 -0
- half_orm_dev/hop.py +167 -0
- half_orm_dev/manifest.py +43 -0
- half_orm_dev/modules.py +456 -0
- half_orm_dev/patch.py +281 -0
- half_orm_dev/patch_manager.py +1694 -0
- half_orm_dev/patch_validator.py +335 -0
- half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +34 -0
- half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +2 -0
- half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +3 -0
- half_orm_dev/patches/log +2 -0
- half_orm_dev/patches/sql/half_orm_meta.sql +208 -0
- half_orm_dev/release_manager.py +2841 -0
- half_orm_dev/repo.py +1562 -0
- half_orm_dev/templates/.gitignore +15 -0
- half_orm_dev/templates/MANIFEST.in +1 -0
- half_orm_dev/templates/Pipfile +13 -0
- half_orm_dev/templates/README +25 -0
- half_orm_dev/templates/conftest_template +42 -0
- half_orm_dev/templates/init_module_template +10 -0
- half_orm_dev/templates/module_template_1 +12 -0
- half_orm_dev/templates/module_template_2 +6 -0
- half_orm_dev/templates/module_template_3 +3 -0
- half_orm_dev/templates/relation_test +23 -0
- half_orm_dev/templates/setup.py +81 -0
- half_orm_dev/templates/sql_adapter +9 -0
- half_orm_dev/templates/warning +12 -0
- half_orm_dev/utils.py +49 -0
- half_orm_dev/version.txt +1 -0
- half_orm_dev-0.16.0a9.dist-info/METADATA +935 -0
- half_orm_dev-0.16.0a9.dist-info/RECORD +58 -0
- half_orm_dev-0.16.0a9.dist-info/WHEEL +5 -0
- half_orm_dev-0.16.0a9.dist-info/licenses/AUTHORS +3 -0
- half_orm_dev-0.16.0a9.dist-info/licenses/LICENSE +14 -0
- half_orm_dev-0.16.0a9.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/conftest.py +329 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Patch ID validation and normalization for half-orm-dev.
|
|
3
|
+
|
|
4
|
+
This module provides validation and normalization of patch identifiers
|
|
5
|
+
used in the patch-centric workflow.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
import unicodedata
|
|
10
|
+
from typing import Optional
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InvalidPatchIdError(Exception):
|
|
15
|
+
"""Raised when patch ID format is invalid."""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DuplicatePatchIdError(Exception):
|
|
20
|
+
"""Raised when patch ID already exists."""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class PatchInfo:
|
|
26
|
+
"""Information about a validated patch ID."""
|
|
27
|
+
original_id: str
|
|
28
|
+
normalized_id: str
|
|
29
|
+
ticket_number: Optional[str]
|
|
30
|
+
description: Optional[str]
|
|
31
|
+
is_numeric_only: bool
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PatchValidator:
|
|
35
|
+
"""
|
|
36
|
+
Validates and normalizes patch IDs for the patch-centric workflow.
|
|
37
|
+
|
|
38
|
+
Handles both formats:
|
|
39
|
+
- Numeric only: "456" -> generates description if possible
|
|
40
|
+
- Full format: "456-user-authentication" -> validates format
|
|
41
|
+
|
|
42
|
+
Examples:
|
|
43
|
+
validator = PatchValidator()
|
|
44
|
+
|
|
45
|
+
# Numeric patch ID
|
|
46
|
+
info = validator.validate_patch_id("456")
|
|
47
|
+
# Returns: PatchInfo(original_id="456", normalized_id="456", ...)
|
|
48
|
+
|
|
49
|
+
# Full patch ID
|
|
50
|
+
info = validator.validate_patch_id("456-user-authentication")
|
|
51
|
+
# Returns: PatchInfo(original_id="456-user-authentication",
|
|
52
|
+
# normalized_id="456-user-authentication", ...)
|
|
53
|
+
|
|
54
|
+
# Invalid format raises exception
|
|
55
|
+
try:
|
|
56
|
+
validator.validate_patch_id("invalid@patch")
|
|
57
|
+
except InvalidPatchIdError as e:
|
|
58
|
+
print(f"Invalid patch ID: {e}")
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
# Regex patterns for validation
|
|
62
|
+
NUMERIC_PATTERN = re.compile(r'^\d+$')
|
|
63
|
+
FULL_PATTERN = re.compile(r'^\d+-[a-z0-9]+(?:-[a-z0-9]+)*$')
|
|
64
|
+
DESCRIPTION_PATTERN = re.compile(r'^[a-z0-9]+(?:-[a-z0-9]+)*$')
|
|
65
|
+
|
|
66
|
+
def __init__(self):
|
|
67
|
+
"""Initialize patch validator."""
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
def validate_patch_id(self, patch_id: str) -> PatchInfo:
|
|
71
|
+
"""
|
|
72
|
+
Validate and parse a patch ID.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
patch_id: The patch identifier to validate
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
PatchInfo object with parsed information
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
InvalidPatchIdError: If patch ID format is invalid
|
|
82
|
+
|
|
83
|
+
Examples:
|
|
84
|
+
# Numeric ID
|
|
85
|
+
info = validator.validate_patch_id("456")
|
|
86
|
+
assert info.ticket_number == "456"
|
|
87
|
+
assert info.is_numeric_only == True
|
|
88
|
+
|
|
89
|
+
# Full ID
|
|
90
|
+
info = validator.validate_patch_id("456-user-auth")
|
|
91
|
+
assert info.ticket_number == "456"
|
|
92
|
+
assert info.description == "user-auth"
|
|
93
|
+
assert info.is_numeric_only == False
|
|
94
|
+
"""
|
|
95
|
+
if not patch_id or not patch_id.strip():
|
|
96
|
+
raise InvalidPatchIdError("Patch ID cannot be empty")
|
|
97
|
+
|
|
98
|
+
patch_id = patch_id.strip()
|
|
99
|
+
|
|
100
|
+
# Check for numeric-only format
|
|
101
|
+
if self.NUMERIC_PATTERN.match(patch_id):
|
|
102
|
+
return PatchInfo(
|
|
103
|
+
original_id=patch_id,
|
|
104
|
+
normalized_id=patch_id,
|
|
105
|
+
ticket_number=patch_id,
|
|
106
|
+
description=None,
|
|
107
|
+
is_numeric_only=True
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Check for full format (number-description)
|
|
111
|
+
if self.FULL_PATTERN.match(patch_id):
|
|
112
|
+
parts = patch_id.split('-', 1)
|
|
113
|
+
ticket_number = parts[0]
|
|
114
|
+
description = parts[1]
|
|
115
|
+
|
|
116
|
+
return PatchInfo(
|
|
117
|
+
original_id=patch_id,
|
|
118
|
+
normalized_id=patch_id,
|
|
119
|
+
ticket_number=ticket_number,
|
|
120
|
+
description=description,
|
|
121
|
+
is_numeric_only=False
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# If we get here, format is invalid
|
|
125
|
+
if not patch_id[0].isdigit():
|
|
126
|
+
raise InvalidPatchIdError("Patch ID must start with a ticket number")
|
|
127
|
+
else:
|
|
128
|
+
raise InvalidPatchIdError(
|
|
129
|
+
f"Invalid patch ID format: '{patch_id}'. "
|
|
130
|
+
f"Expected formats: '123' or '123-description' (lowercase, hyphens only)"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
def normalize_patch_id(self, patch_id: str, suggested_description: Optional[str] = None) -> str:
|
|
134
|
+
"""
|
|
135
|
+
Normalize a patch ID to the standard format.
|
|
136
|
+
|
|
137
|
+
For numeric IDs, tries to generate a meaningful description.
|
|
138
|
+
For full IDs, validates format and returns as-is.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
patch_id: The patch identifier to normalize
|
|
142
|
+
suggested_description: Optional description to use for numeric IDs
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Normalized patch ID in format "number-description"
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
InvalidPatchIdError: If patch ID format is invalid
|
|
149
|
+
|
|
150
|
+
Examples:
|
|
151
|
+
# Numeric with suggestion
|
|
152
|
+
result = validator.normalize_patch_id("456", "user-authentication")
|
|
153
|
+
assert result == "456-user-authentication"
|
|
154
|
+
|
|
155
|
+
# Numeric without suggestion (uses fallback)
|
|
156
|
+
result = validator.normalize_patch_id("456")
|
|
157
|
+
assert result == "456" # or "456-feature" based on context
|
|
158
|
+
|
|
159
|
+
# Already normalized
|
|
160
|
+
result = validator.normalize_patch_id("456-existing")
|
|
161
|
+
assert result == "456-existing"
|
|
162
|
+
"""
|
|
163
|
+
# First validate the input format
|
|
164
|
+
patch_info = self.validate_patch_id(patch_id)
|
|
165
|
+
|
|
166
|
+
# If it's already in full format, return as-is
|
|
167
|
+
if not patch_info.is_numeric_only:
|
|
168
|
+
return patch_info.normalized_id
|
|
169
|
+
|
|
170
|
+
# For numeric-only IDs, we need to add a description
|
|
171
|
+
if suggested_description:
|
|
172
|
+
# Sanitize the suggested description
|
|
173
|
+
clean_description = self.sanitize_description(suggested_description)
|
|
174
|
+
return f"{patch_info.ticket_number}-{clean_description}"
|
|
175
|
+
else:
|
|
176
|
+
# Use fallback description
|
|
177
|
+
fallback_description = self.generate_fallback_description(patch_info.ticket_number)
|
|
178
|
+
return f"{patch_info.ticket_number}-{fallback_description}"
|
|
179
|
+
|
|
180
|
+
def extract_ticket_number(self, patch_id: str) -> Optional[str]:
|
|
181
|
+
"""
|
|
182
|
+
Extract ticket number from patch ID.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
patch_id: The patch identifier
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Ticket number if found, None otherwise
|
|
189
|
+
|
|
190
|
+
Examples:
|
|
191
|
+
assert validator.extract_ticket_number("456-auth") == "456"
|
|
192
|
+
assert validator.extract_ticket_number("456") == "456"
|
|
193
|
+
assert validator.extract_ticket_number("invalid") is None
|
|
194
|
+
"""
|
|
195
|
+
if not patch_id or not patch_id.strip():
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
patch_id = patch_id.strip()
|
|
199
|
+
|
|
200
|
+
# Check numeric-only format
|
|
201
|
+
if self.NUMERIC_PATTERN.match(patch_id):
|
|
202
|
+
return patch_id
|
|
203
|
+
|
|
204
|
+
# Check full format and extract number part
|
|
205
|
+
if self.FULL_PATTERN.match(patch_id):
|
|
206
|
+
return patch_id.split('-', 1)[0]
|
|
207
|
+
|
|
208
|
+
# Invalid format
|
|
209
|
+
return None
|
|
210
|
+
|
|
211
|
+
def extract_description(self, patch_id: str) -> Optional[str]:
|
|
212
|
+
"""
|
|
213
|
+
Extract description part from patch ID.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
patch_id: The patch identifier
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Description if found, None for numeric-only IDs
|
|
220
|
+
|
|
221
|
+
Examples:
|
|
222
|
+
assert validator.extract_description("456-user-auth") == "user-auth"
|
|
223
|
+
assert validator.extract_description("456") is None
|
|
224
|
+
"""
|
|
225
|
+
if not patch_id or not patch_id.strip():
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
patch_id = patch_id.strip()
|
|
229
|
+
|
|
230
|
+
# Numeric-only format has no description
|
|
231
|
+
if self.NUMERIC_PATTERN.match(patch_id):
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
# Extract description from full format
|
|
235
|
+
if self.FULL_PATTERN.match(patch_id):
|
|
236
|
+
parts = patch_id.split('-', 1)
|
|
237
|
+
return parts[1] # Return everything after first hyphen
|
|
238
|
+
|
|
239
|
+
# Invalid format
|
|
240
|
+
return None
|
|
241
|
+
|
|
242
|
+
def is_valid_description(self, description: str) -> bool:
|
|
243
|
+
"""
|
|
244
|
+
Check if description part follows naming conventions.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
description: Description to validate
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
True if description is valid, False otherwise
|
|
251
|
+
|
|
252
|
+
Examples:
|
|
253
|
+
assert validator.is_valid_description("user-authentication") == True
|
|
254
|
+
assert validator.is_valid_description("user_auth") == False # no underscores
|
|
255
|
+
assert validator.is_valid_description("UserAuth") == False # no uppercase
|
|
256
|
+
"""
|
|
257
|
+
if not description:
|
|
258
|
+
return False
|
|
259
|
+
|
|
260
|
+
# Use the DESCRIPTION_PATTERN regex to validate format
|
|
261
|
+
return bool(self.DESCRIPTION_PATTERN.match(description))
|
|
262
|
+
|
|
263
|
+
def generate_fallback_description(self, ticket_number: str) -> str:
|
|
264
|
+
"""
|
|
265
|
+
Generate a fallback description for numeric patch IDs.
|
|
266
|
+
|
|
267
|
+
Returns a simple default description. Developers should provide
|
|
268
|
+
meaningful descriptions themselves for better clarity.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
ticket_number: The numeric ticket identifier
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
Default description "patch"
|
|
275
|
+
|
|
276
|
+
Examples:
|
|
277
|
+
desc = validator.generate_fallback_description("456")
|
|
278
|
+
assert desc == "patch"
|
|
279
|
+
"""
|
|
280
|
+
return "patch"
|
|
281
|
+
|
|
282
|
+
def sanitize_description(self, description: str) -> str:
|
|
283
|
+
"""
|
|
284
|
+
Sanitize a description to follow naming conventions.
|
|
285
|
+
|
|
286
|
+
- Convert to lowercase
|
|
287
|
+
- Replace spaces/underscores with hyphens
|
|
288
|
+
- Remove invalid characters
|
|
289
|
+
- Truncate if too long
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
description: Raw description to sanitize
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
Sanitized description following conventions
|
|
296
|
+
|
|
297
|
+
Examples:
|
|
298
|
+
assert validator.sanitize_description("User Authentication") == "user-authentication"
|
|
299
|
+
assert validator.sanitize_description("user_auth_system") == "user-auth-system"
|
|
300
|
+
assert validator.sanitize_description("Fix Bug #123") == "fix-bug-123"
|
|
301
|
+
"""
|
|
302
|
+
if not description:
|
|
303
|
+
return "patch"
|
|
304
|
+
|
|
305
|
+
# Convert to lowercase
|
|
306
|
+
result = description.lower()
|
|
307
|
+
|
|
308
|
+
# Remove accents (transliteration)
|
|
309
|
+
result = unicodedata.normalize('NFD', result)
|
|
310
|
+
result = ''.join(c for c in result if unicodedata.category(c) != 'Mn')
|
|
311
|
+
|
|
312
|
+
# Replace spaces and underscores with hyphens
|
|
313
|
+
result = re.sub(r'[\s_]+', '-', result)
|
|
314
|
+
|
|
315
|
+
# Replace other separators (dots, @, etc.) with hyphens before removing them
|
|
316
|
+
result = re.sub(r'[^\w\-]', '-', result)
|
|
317
|
+
|
|
318
|
+
# Now remove invalid characters (keep only letters, numbers, hyphens)
|
|
319
|
+
result = re.sub(r'[^a-z0-9\-]', '', result)
|
|
320
|
+
|
|
321
|
+
# Clean up multiple consecutive hyphens
|
|
322
|
+
result = re.sub(r'-+', '-', result)
|
|
323
|
+
|
|
324
|
+
# Remove leading and trailing hyphens
|
|
325
|
+
result = result.strip('-')
|
|
326
|
+
|
|
327
|
+
# If result is empty after cleaning, use fallback
|
|
328
|
+
if not result:
|
|
329
|
+
return "patch"
|
|
330
|
+
|
|
331
|
+
# Truncate if too long (reasonable limit)
|
|
332
|
+
if len(result) > 50:
|
|
333
|
+
result = result[:50].rstrip('-')
|
|
334
|
+
|
|
335
|
+
return result
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
create extension if not exists pgcrypto;
|
|
2
|
+
|
|
3
|
+
create table if not exists half_orm_meta.database (
|
|
4
|
+
id text primary key,
|
|
5
|
+
name text not null,
|
|
6
|
+
description text
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
create or replace function half_orm_meta.check_database(old_dbid text default null) returns text as $$
|
|
11
|
+
DECLARE
|
|
12
|
+
dbname text;
|
|
13
|
+
dbid text;
|
|
14
|
+
BEGIN
|
|
15
|
+
select current_database() into dbname;
|
|
16
|
+
--XXX: use a materialized view.
|
|
17
|
+
BEGIN
|
|
18
|
+
select encode(hmac(dbname, pg_read_file('hop_key'), 'sha1'), 'hex') into dbid;
|
|
19
|
+
EXCEPTION
|
|
20
|
+
when undefined_file then
|
|
21
|
+
raise NOTICE 'No hop_key file for the cluster. Will use % for dbid', dbname;
|
|
22
|
+
dbid := dbname;
|
|
23
|
+
END;
|
|
24
|
+
if old_dbid is not null and old_dbid != dbid
|
|
25
|
+
then
|
|
26
|
+
raise Exception 'Not the same database!';
|
|
27
|
+
end if;
|
|
28
|
+
return dbid;
|
|
29
|
+
END;
|
|
30
|
+
$$ language plpgsql;
|
|
31
|
+
|
|
32
|
+
insert into half_orm_meta.database (id, name) values ((select half_orm_meta.check_database()), (select current_database()));
|
|
33
|
+
|
|
34
|
+
comment on table half_orm_meta.database is 'id identifies the database in the cluster. It uses the key in hop_key.';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
drop view if exists "half_orm_meta.view".hop_penultimate_release;
|
|
2
|
+
create view "half_orm_meta.view".hop_penultimate_release as
|
|
3
|
+
select * from (select major, minor, patch from half_orm_meta.hop_release order by major desc, minor desc, patch desc limit 2 ) as penultimate order by major, minor, patch limit 1;
|
half_orm_dev/patches/log
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
--
|
|
2
|
+
-- PostgreSQL database dump
|
|
3
|
+
--
|
|
4
|
+
|
|
5
|
+
-- Dumped from database version 13.11 (Debian 13.11-1.pgdg110+1)
|
|
6
|
+
-- Dumped by pg_dump version 13.11 (Debian 13.11-1.pgdg110+1)
|
|
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: half_orm_meta; Type: SCHEMA; Schema: -; Owner: -
|
|
21
|
+
--
|
|
22
|
+
|
|
23
|
+
CREATE SCHEMA half_orm_meta;
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
--
|
|
27
|
+
-- Name: half_orm_meta.view; Type: SCHEMA; Schema: -; Owner: -
|
|
28
|
+
--
|
|
29
|
+
|
|
30
|
+
CREATE SCHEMA "half_orm_meta.view";
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
--
|
|
34
|
+
-- Name: check_database(text); Type: FUNCTION; Schema: half_orm_meta; Owner: -
|
|
35
|
+
--
|
|
36
|
+
|
|
37
|
+
CREATE FUNCTION half_orm_meta.check_database(old_dbid text DEFAULT NULL::text) RETURNS text
|
|
38
|
+
LANGUAGE plpgsql
|
|
39
|
+
AS $$
|
|
40
|
+
DECLARE
|
|
41
|
+
dbname text;
|
|
42
|
+
dbid text;
|
|
43
|
+
BEGIN
|
|
44
|
+
select current_database() into dbname;
|
|
45
|
+
--XXX: use a materialized view.
|
|
46
|
+
BEGIN
|
|
47
|
+
select encode(hmac(dbname, pg_read_file('hop_key'), 'sha1'), 'hex') into dbid;
|
|
48
|
+
EXCEPTION
|
|
49
|
+
when undefined_file then
|
|
50
|
+
raise NOTICE 'No hop_key file for the cluster. Will use % for dbid', dbname;
|
|
51
|
+
dbid := dbname;
|
|
52
|
+
END;
|
|
53
|
+
if old_dbid is not null and old_dbid != dbid
|
|
54
|
+
then
|
|
55
|
+
raise Exception 'Not the same database!';
|
|
56
|
+
end if;
|
|
57
|
+
return dbid;
|
|
58
|
+
END;
|
|
59
|
+
$$;
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
SET default_tablespace = '';
|
|
63
|
+
|
|
64
|
+
SET default_table_access_method = heap;
|
|
65
|
+
|
|
66
|
+
--
|
|
67
|
+
-- Name: database; Type: TABLE; Schema: half_orm_meta; Owner: -
|
|
68
|
+
--
|
|
69
|
+
|
|
70
|
+
CREATE TABLE half_orm_meta.database (
|
|
71
|
+
id text NOT NULL,
|
|
72
|
+
name text NOT NULL,
|
|
73
|
+
description text
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
--
|
|
78
|
+
-- Name: TABLE database; Type: COMMENT; Schema: half_orm_meta; Owner: -
|
|
79
|
+
--
|
|
80
|
+
|
|
81
|
+
COMMENT ON TABLE half_orm_meta.database IS '
|
|
82
|
+
id identifies the database in the cluster. It uses the key
|
|
83
|
+
in hop_key.
|
|
84
|
+
';
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
--
|
|
88
|
+
-- Name: hop_release; Type: TABLE; Schema: half_orm_meta; Owner: -
|
|
89
|
+
--
|
|
90
|
+
|
|
91
|
+
CREATE TABLE half_orm_meta.hop_release (
|
|
92
|
+
major integer NOT NULL,
|
|
93
|
+
minor integer NOT NULL,
|
|
94
|
+
patch integer NOT NULL,
|
|
95
|
+
pre_release text DEFAULT ''::text NOT NULL,
|
|
96
|
+
pre_release_num text DEFAULT ''::text NOT NULL,
|
|
97
|
+
date date DEFAULT CURRENT_DATE,
|
|
98
|
+
"time" time(0) with time zone DEFAULT CURRENT_TIME,
|
|
99
|
+
changelog text,
|
|
100
|
+
commit text,
|
|
101
|
+
dbid text,
|
|
102
|
+
hop_release text,
|
|
103
|
+
CONSTRAINT hop_release_major_check CHECK ((major >= 0)),
|
|
104
|
+
CONSTRAINT hop_release_minor_check CHECK ((minor >= 0)),
|
|
105
|
+
CONSTRAINT hop_release_patch_check CHECK ((patch >= 0)),
|
|
106
|
+
CONSTRAINT hop_release_pre_release_check CHECK ((pre_release = ANY (ARRAY['alpha'::text, 'beta'::text, 'rc'::text, ''::text]))),
|
|
107
|
+
CONSTRAINT hop_release_pre_release_num_check CHECK (((pre_release_num = ''::text) OR (pre_release_num ~ '^\d+$'::text)))
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
--
|
|
112
|
+
-- Name: hop_release_issue; Type: TABLE; Schema: half_orm_meta; Owner: -
|
|
113
|
+
--
|
|
114
|
+
|
|
115
|
+
CREATE TABLE half_orm_meta.hop_release_issue (
|
|
116
|
+
num integer NOT NULL,
|
|
117
|
+
issue_release integer DEFAULT 0 NOT NULL,
|
|
118
|
+
release_major integer NOT NULL,
|
|
119
|
+
release_minor integer NOT NULL,
|
|
120
|
+
release_patch integer NOT NULL,
|
|
121
|
+
release_pre_release text NOT NULL,
|
|
122
|
+
release_pre_release_num text NOT NULL,
|
|
123
|
+
changelog text,
|
|
124
|
+
CONSTRAINT hop_release_issue_num_check CHECK ((num >= 0))
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
--
|
|
129
|
+
-- Name: hop_last_release; Type: VIEW; Schema: half_orm_meta.view; Owner: -
|
|
130
|
+
--
|
|
131
|
+
|
|
132
|
+
CREATE VIEW "half_orm_meta.view".hop_last_release AS
|
|
133
|
+
SELECT hop_release.major,
|
|
134
|
+
hop_release.minor,
|
|
135
|
+
hop_release.patch,
|
|
136
|
+
hop_release.pre_release,
|
|
137
|
+
hop_release.pre_release_num,
|
|
138
|
+
hop_release.date,
|
|
139
|
+
hop_release."time",
|
|
140
|
+
hop_release.changelog,
|
|
141
|
+
hop_release.commit
|
|
142
|
+
FROM half_orm_meta.hop_release
|
|
143
|
+
ORDER BY hop_release.major DESC, hop_release.minor DESC, hop_release.patch DESC, hop_release.pre_release DESC, hop_release.pre_release_num DESC
|
|
144
|
+
LIMIT 1;
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
--
|
|
148
|
+
-- Name: hop_penultimate_release; Type: VIEW; Schema: half_orm_meta.view; Owner: -
|
|
149
|
+
--
|
|
150
|
+
|
|
151
|
+
CREATE VIEW "half_orm_meta.view".hop_penultimate_release AS
|
|
152
|
+
SELECT penultimate.major,
|
|
153
|
+
penultimate.minor,
|
|
154
|
+
penultimate.patch
|
|
155
|
+
FROM ( SELECT hop_release.major,
|
|
156
|
+
hop_release.minor,
|
|
157
|
+
hop_release.patch
|
|
158
|
+
FROM half_orm_meta.hop_release
|
|
159
|
+
ORDER BY hop_release.major DESC, hop_release.minor DESC, hop_release.patch DESC
|
|
160
|
+
LIMIT 2) penultimate
|
|
161
|
+
ORDER BY penultimate.major, penultimate.minor, penultimate.patch
|
|
162
|
+
LIMIT 1;
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
--
|
|
166
|
+
-- Name: database database_pkey; Type: CONSTRAINT; Schema: half_orm_meta; Owner: -
|
|
167
|
+
--
|
|
168
|
+
|
|
169
|
+
ALTER TABLE ONLY half_orm_meta.database
|
|
170
|
+
ADD CONSTRAINT database_pkey PRIMARY KEY (id);
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
--
|
|
174
|
+
-- Name: hop_release_issue hop_release_issue_pkey; Type: CONSTRAINT; Schema: half_orm_meta; Owner: -
|
|
175
|
+
--
|
|
176
|
+
|
|
177
|
+
ALTER TABLE ONLY half_orm_meta.hop_release_issue
|
|
178
|
+
ADD CONSTRAINT hop_release_issue_pkey PRIMARY KEY (num, issue_release);
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
--
|
|
182
|
+
-- Name: hop_release hop_release_pkey; Type: CONSTRAINT; Schema: half_orm_meta; Owner: -
|
|
183
|
+
--
|
|
184
|
+
|
|
185
|
+
ALTER TABLE ONLY half_orm_meta.hop_release
|
|
186
|
+
ADD CONSTRAINT hop_release_pkey PRIMARY KEY (major, minor, patch, pre_release, pre_release_num);
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
--
|
|
190
|
+
-- Name: hop_release hop_release_dbid_fkey; Type: FK CONSTRAINT; Schema: half_orm_meta; Owner: -
|
|
191
|
+
--
|
|
192
|
+
|
|
193
|
+
ALTER TABLE ONLY half_orm_meta.hop_release
|
|
194
|
+
ADD CONSTRAINT hop_release_dbid_fkey FOREIGN KEY (dbid) REFERENCES half_orm_meta.database(id) ON UPDATE CASCADE;
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
--
|
|
198
|
+
-- Name: hop_release_issue hop_release_issue_release_major_release_minor_release_patc_fkey; Type: FK CONSTRAINT; Schema: half_orm_meta; Owner: -
|
|
199
|
+
--
|
|
200
|
+
|
|
201
|
+
ALTER TABLE ONLY half_orm_meta.hop_release_issue
|
|
202
|
+
ADD CONSTRAINT hop_release_issue_release_major_release_minor_release_patc_fkey FOREIGN KEY (release_major, release_minor, release_patch, release_pre_release, release_pre_release_num) REFERENCES half_orm_meta.hop_release(major, minor, patch, pre_release, pre_release_num);
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
--
|
|
206
|
+
-- PostgreSQL database dump complete
|
|
207
|
+
--
|
|
208
|
+
|