half-orm-dev 0.16.0a1__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 +0 -0
- half_orm_dev/changelog.py +117 -0
- half_orm_dev/cli_extension.py +171 -0
- half_orm_dev/database.py +127 -0
- half_orm_dev/db_conn.py +134 -0
- half_orm_dev/hgit.py +202 -0
- half_orm_dev/hop.py +167 -0
- half_orm_dev/manifest.py +43 -0
- half_orm_dev/modules.py +357 -0
- half_orm_dev/patch.py +348 -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/repo.py +266 -0
- half_orm_dev/templates/.gitignore +14 -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/base_test +26 -0
- half_orm_dev/templates/init_module_template +6 -0
- half_orm_dev/templates/module_template_1 +12 -0
- half_orm_dev/templates/module_template_2 +5 -0
- half_orm_dev/templates/module_template_3 +3 -0
- half_orm_dev/templates/relation_test +19 -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 +12 -0
- half_orm_dev/version.txt +1 -0
- half_orm_dev-0.16.0a1.dist-info/METADATA +314 -0
- half_orm_dev-0.16.0a1.dist-info/RECORD +38 -0
- half_orm_dev-0.16.0a1.dist-info/WHEEL +5 -0
- half_orm_dev-0.16.0a1.dist-info/entry_points.txt +2 -0
- half_orm_dev-0.16.0a1.dist-info/licenses/AUTHORS +3 -0
- half_orm_dev-0.16.0a1.dist-info/licenses/LICENSE +14 -0
- half_orm_dev-0.16.0a1.dist-info/top_level.txt +1 -0
half_orm_dev/patch.py
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
"The patch module"
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
import psycopg2
|
|
9
|
+
|
|
10
|
+
from half_orm import utils
|
|
11
|
+
from half_orm_dev import modules
|
|
12
|
+
from half_orm_dev.changelog import Changelog
|
|
13
|
+
|
|
14
|
+
from .utils import hop_version
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
PYTEST_OK = True
|
|
18
|
+
import pytest
|
|
19
|
+
except ImportError:
|
|
20
|
+
PYTEST_OK = False
|
|
21
|
+
|
|
22
|
+
class Patch:
|
|
23
|
+
"The Patch class..."
|
|
24
|
+
__levels = ['patch', 'minor', 'major']
|
|
25
|
+
|
|
26
|
+
def __init__(self, repo):
|
|
27
|
+
self.__repo = repo
|
|
28
|
+
self.__patches_base_dir = os.path.join(repo.base_dir, 'Patches')
|
|
29
|
+
if self.__repo.devel:
|
|
30
|
+
self.__changelog = Changelog(repo)
|
|
31
|
+
if not os.path.exists(self.__patches_base_dir):
|
|
32
|
+
os.makedirs(self.__patches_base_dir)
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
@property
|
|
36
|
+
def levels(cls):
|
|
37
|
+
"Returns the levels"
|
|
38
|
+
return cls.__levels
|
|
39
|
+
|
|
40
|
+
def previous(self, release, index=0):
|
|
41
|
+
"Return .hop/CHANGELOG second to last line."
|
|
42
|
+
return self.__changelog.previous(release, index)
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def __next_releases(self):
|
|
46
|
+
db_last_release = self.__repo.database.last_release_s
|
|
47
|
+
ch_last_release = self.__changelog.last_release
|
|
48
|
+
if db_last_release != ch_last_release:
|
|
49
|
+
utils.error(
|
|
50
|
+
f'Last release mismatch between database {db_last_release}'
|
|
51
|
+
f' and CHANGELOG {ch_last_release}!\n', 1)
|
|
52
|
+
current = dict(self.__repo.database.last_release)
|
|
53
|
+
releases_in_dev = self.__changelog.releases_in_dev
|
|
54
|
+
n_rels = {}
|
|
55
|
+
for level in self.__levels:
|
|
56
|
+
n_rel = dict(current)
|
|
57
|
+
n_rel[level] = current[level] + 1
|
|
58
|
+
if level == 'major':
|
|
59
|
+
n_rel['minor'] = n_rel['patch'] = 0
|
|
60
|
+
if level == 'minor':
|
|
61
|
+
n_rel['patch'] = 0
|
|
62
|
+
next_release_s = f"{n_rel['major']}.{n_rel['minor']}.{n_rel['patch']}"
|
|
63
|
+
n_rel['in_dev'] = ''
|
|
64
|
+
if next_release_s in releases_in_dev:
|
|
65
|
+
n_rel['in_dev'] = '(IN DEV)'
|
|
66
|
+
n_rels[level] = n_rel
|
|
67
|
+
return n_rels
|
|
68
|
+
|
|
69
|
+
def __assert_main_branch(self):
|
|
70
|
+
if str(self.__repo.hgit.branch) != 'hop_main':
|
|
71
|
+
utils.error(
|
|
72
|
+
'ERROR! Wrong branch. Please, switch to the hop_main branch before.\n', exit_code=1)
|
|
73
|
+
|
|
74
|
+
def prep_release(self, release_level, message=None):
|
|
75
|
+
"""Returns the next (major, minor, patch) tuple according to the release_level
|
|
76
|
+
|
|
77
|
+
The repo must be clean.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
release_level (str): one of ['patch', 'minor', 'major']
|
|
81
|
+
"""
|
|
82
|
+
if not self.__repo.hgit.repos_is_clean():
|
|
83
|
+
utils.error('There are uncommited changes. Please commit your changes before using `hop prepare`\n', exit_code=1)
|
|
84
|
+
if self.__repo.database.last_release_s != self.__changelog.last_release:
|
|
85
|
+
try:
|
|
86
|
+
self.__restore_db(self.__changelog.last_release)
|
|
87
|
+
except FileNotFoundError as exc:
|
|
88
|
+
utils.error(f'No backup file for release {self.__changelog.last_release}\n{exc}\n', exit_code=1)
|
|
89
|
+
self.__repo.hgit.checkout_to_hop_main()
|
|
90
|
+
next_releases = self.__next_releases
|
|
91
|
+
if release_level is None:
|
|
92
|
+
next_levels = '\n'.join(
|
|
93
|
+
[f"""- {level}: {'{major}.{minor}.{patch} {in_dev}'.format(**next_releases[level])}"""
|
|
94
|
+
for level in self.__levels])
|
|
95
|
+
print(f'Next releases:\n{next_levels}')
|
|
96
|
+
next_possible_releases = [elt for elt in self.__levels if not next_releases[elt]['in_dev']]
|
|
97
|
+
release_level = input(f"Release level {next_possible_releases}? ")
|
|
98
|
+
if release_level not in next_possible_releases:
|
|
99
|
+
utils.error(f"Wrong release level ({release_level}).\n", exit_code=1)
|
|
100
|
+
elif next_releases[release_level]['in_dev']:
|
|
101
|
+
utils.error(f'{release_level} is alredy in development!\n', 1)
|
|
102
|
+
next_release = dict(self.__repo.database.last_release)
|
|
103
|
+
next_release[release_level] = next_release[release_level] + 1
|
|
104
|
+
if release_level == 'major':
|
|
105
|
+
next_release['minor'] = next_release['patch'] = 0
|
|
106
|
+
if release_level == 'minor':
|
|
107
|
+
next_release['patch'] = 0
|
|
108
|
+
new_release_s = '{major}.{minor}.{patch}'.format(**next_release)
|
|
109
|
+
rel_branch = f'hop_{new_release_s}'
|
|
110
|
+
if self.__repo.hgit.branch_exists(rel_branch):
|
|
111
|
+
utils.error(f'{rel_branch} already exists!\n', 1)
|
|
112
|
+
print(f'PREPARING: {new_release_s}')
|
|
113
|
+
patch_path = os.path.join(
|
|
114
|
+
'Patches',
|
|
115
|
+
str(next_release['major']),
|
|
116
|
+
str(next_release['minor']),
|
|
117
|
+
str(next_release['patch']))
|
|
118
|
+
if not os.path.exists(patch_path):
|
|
119
|
+
changelog_msg = message or input('Message - (leave empty to abort): ')
|
|
120
|
+
if not changelog_msg:
|
|
121
|
+
print('Aborting')
|
|
122
|
+
return
|
|
123
|
+
os.makedirs(patch_path)
|
|
124
|
+
with open(os.path.join(patch_path, 'MANIFEST.json'), 'w', encoding='utf-8') as manifest:
|
|
125
|
+
manifest.write(json.dumps({
|
|
126
|
+
'hop_version': hop_version(),
|
|
127
|
+
'changelog_msg': changelog_msg,
|
|
128
|
+
}))
|
|
129
|
+
self.__changelog.new_release(new_release_s)
|
|
130
|
+
self.__repo.hgit.set_branch(new_release_s)
|
|
131
|
+
print('You can now add your patch scripts (*.py, *.sql)'
|
|
132
|
+
f'in {patch_path}. See Patches/README.')
|
|
133
|
+
modules.generate(self.__repo)
|
|
134
|
+
|
|
135
|
+
def __check_apply_or_re_apply(self):
|
|
136
|
+
"""Return True if it's the first time.
|
|
137
|
+
False otherwise.
|
|
138
|
+
"""
|
|
139
|
+
if self.__repo.database.last_release_s == self.__repo.hgit.current_release:
|
|
140
|
+
return 're-apply'
|
|
141
|
+
return 'apply'
|
|
142
|
+
|
|
143
|
+
def __backup_file(self, directory, release, commit=None):
|
|
144
|
+
backup_dir = os.path.join(self.__repo.base_dir, directory)
|
|
145
|
+
if not os.path.isdir(backup_dir):
|
|
146
|
+
os.mkdir(backup_dir)
|
|
147
|
+
file_name = f'{self.__repo.name}-{release}'
|
|
148
|
+
if commit:
|
|
149
|
+
file_name = f'{file_name}-{commit}'
|
|
150
|
+
return os.path.join(backup_dir, f'{file_name}.sql')
|
|
151
|
+
|
|
152
|
+
def __save_db(self, release):
|
|
153
|
+
"""Save the database
|
|
154
|
+
"""
|
|
155
|
+
commit = None
|
|
156
|
+
if self.__repo.production:
|
|
157
|
+
commit = self.__repo.hgit.last_commit()
|
|
158
|
+
svg_file = self.__backup_file('Backups', release, commit)
|
|
159
|
+
print(f'Saving the database into {svg_file}')
|
|
160
|
+
if os.path.isfile(svg_file):
|
|
161
|
+
utils.error(
|
|
162
|
+
f"Oops! there is already a dump for the {release} release.\n")
|
|
163
|
+
utils.warning("Please remove it if you really want to proceed.\n")
|
|
164
|
+
sys.exit(1)
|
|
165
|
+
|
|
166
|
+
self.__repo.database.execute_pg_command(
|
|
167
|
+
'pg_dump', '-f', svg_file, stderr=subprocess.PIPE)
|
|
168
|
+
|
|
169
|
+
def __restore_db(self, release):
|
|
170
|
+
"""Restore the database to the release_s version.
|
|
171
|
+
"""
|
|
172
|
+
backup_file = self.__backup_file('Backups', release)
|
|
173
|
+
if not os.path.exists(backup_file):
|
|
174
|
+
raise FileNotFoundError(backup_file)
|
|
175
|
+
print(f'{utils.Color.green("Restoring the database to")} {utils.Color.bold(release)}')
|
|
176
|
+
self.__repo.model.disconnect()
|
|
177
|
+
self.__repo.database.execute_pg_command('dropdb')
|
|
178
|
+
self.__repo.database.execute_pg_command('createdb')
|
|
179
|
+
self.__repo.database.execute_pg_command(
|
|
180
|
+
'psql', '-f', backup_file, stdout=subprocess.DEVNULL)
|
|
181
|
+
self.__repo.model.ping()
|
|
182
|
+
|
|
183
|
+
def __restore_previous_release(self):
|
|
184
|
+
db_release = self.__repo.database.last_release_s
|
|
185
|
+
self.__restore_db(db_release)
|
|
186
|
+
os.remove(self.__backup_file('Backups', db_release))
|
|
187
|
+
sys.exit(1)
|
|
188
|
+
|
|
189
|
+
def __execute_sql(self, file_):
|
|
190
|
+
"Execute sql query contained in sql file_"
|
|
191
|
+
query = utils.read(file_.path).replace('%', '%%')
|
|
192
|
+
if len(query) == 0:
|
|
193
|
+
return
|
|
194
|
+
try:
|
|
195
|
+
self.__repo.model.execute_query(query)
|
|
196
|
+
except psycopg2.Error as err:
|
|
197
|
+
utils.error(f'Problem with query in {file_.name}\n{err}\n')
|
|
198
|
+
self.__restore_previous_release()
|
|
199
|
+
|
|
200
|
+
def __execute_script(self, file_):
|
|
201
|
+
try:
|
|
202
|
+
python_path = os.environ.get('PYTHONPATH')
|
|
203
|
+
if python_path:
|
|
204
|
+
python_path = python_path.split(':')
|
|
205
|
+
else:
|
|
206
|
+
python_path = []
|
|
207
|
+
if self.__repo.base_dir:
|
|
208
|
+
os.environ.update({'PYTHONPATH': ':'.join([self.__repo.base_dir] + python_path)})
|
|
209
|
+
subprocess.run(
|
|
210
|
+
['python', file_.path],
|
|
211
|
+
cwd=self.__repo.base_dir,
|
|
212
|
+
env=os.environ,
|
|
213
|
+
shell=False, check=True)
|
|
214
|
+
except subprocess.CalledProcessError as err:
|
|
215
|
+
utils.error(f'Problem with script {file_}\n{err}\n')
|
|
216
|
+
self.__restore_previous_release()
|
|
217
|
+
|
|
218
|
+
def __apply(self, path):
|
|
219
|
+
if not os.path.exists(path):
|
|
220
|
+
utils.warning(f"{path} does not exist. Skipping.\n")
|
|
221
|
+
sys.stderr.flush()
|
|
222
|
+
return
|
|
223
|
+
files_d = {elt.name: elt for elt in os.scandir(path)}
|
|
224
|
+
file_names = list(files_d.keys())
|
|
225
|
+
file_names.sort()
|
|
226
|
+
for file_name in file_names:
|
|
227
|
+
file_ = files_d[file_name]
|
|
228
|
+
extension = file_.name.split('.').pop()
|
|
229
|
+
if not (file_.is_file() and extension in ['sql', 'py']):
|
|
230
|
+
continue
|
|
231
|
+
print(f'+ {file_.name}')
|
|
232
|
+
|
|
233
|
+
if extension == 'sql':
|
|
234
|
+
self.__execute_sql(file_)
|
|
235
|
+
elif extension == 'py':
|
|
236
|
+
self.__execute_script(file_)
|
|
237
|
+
|
|
238
|
+
def apply(self, release, force=False, save_db=True):
|
|
239
|
+
"""Apply the release in 'path'
|
|
240
|
+
|
|
241
|
+
The history is first rebased on hop_main
|
|
242
|
+
"""
|
|
243
|
+
if self.__repo.hgit.repos_is_clean():
|
|
244
|
+
self.__repo.hgit.rebase('hop_main')
|
|
245
|
+
db_release = self.__repo.database.last_release_s
|
|
246
|
+
changelog_msg = ''
|
|
247
|
+
if self.__check_apply_or_re_apply() == 'apply' and save_db:
|
|
248
|
+
self.__save_db(db_release)
|
|
249
|
+
elif not self.__repo.production:
|
|
250
|
+
if not force:
|
|
251
|
+
okay = input(f'Do you want to re-apply the release {release} [y/N]?') or 'y'
|
|
252
|
+
if okay.upper() != 'Y':
|
|
253
|
+
sys.exit()
|
|
254
|
+
self.__restore_db(self.previous(db_release, 1))
|
|
255
|
+
app_upg = utils.Color.green('Upgrading to') if self.__repo.production else utils.Color.bold('Applying')
|
|
256
|
+
major, minor, patch = release.split('.')
|
|
257
|
+
print(utils.Color.green("Pre patch:"))
|
|
258
|
+
self.__apply(os.path.join(self.__patches_base_dir, 'pre'))
|
|
259
|
+
print(f'{app_upg} {utils.Color.green(release)}:')
|
|
260
|
+
self.__apply(os.path.join(self.__patches_base_dir, major, minor, patch))
|
|
261
|
+
if not self.__repo.production:
|
|
262
|
+
modules.generate(self.__repo)
|
|
263
|
+
print(utils.Color.green("Post patch:"))
|
|
264
|
+
self.__apply(os.path.join(self.__patches_base_dir, 'post'))
|
|
265
|
+
self.__repo.database.register_release(major, minor, patch, changelog_msg)
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def state(self):
|
|
269
|
+
"The state of a patch"
|
|
270
|
+
if not self.__repo.devel:
|
|
271
|
+
return 'This repo is not in developement mode.'
|
|
272
|
+
if not self.__repo.production:
|
|
273
|
+
resp = ['[Releases in development]']
|
|
274
|
+
if len(self.__changelog.releases_in_dev) == 0:
|
|
275
|
+
resp.append("No release in development.\nUse `hop prepare`.")
|
|
276
|
+
for release in self.__changelog.releases_in_dev:
|
|
277
|
+
resp.append(f'- {release} (branch hop_{release})')
|
|
278
|
+
else:
|
|
279
|
+
resp = ['[Releases to apply]']
|
|
280
|
+
if len(self.__changelog.releases_to_apply_in_prod) == 0:
|
|
281
|
+
resp.append("No new release to apply.")
|
|
282
|
+
for release in self.__changelog.releases_to_apply_in_prod:
|
|
283
|
+
resp.append(f'- {release}')
|
|
284
|
+
return '\n'.join(resp)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def undo(self, database_only=False):
|
|
288
|
+
"Undo a patch."
|
|
289
|
+
db_release = self.__repo.database.last_release_s
|
|
290
|
+
previous_release = self.previous(db_release, 1)
|
|
291
|
+
self.__restore_db(previous_release)
|
|
292
|
+
if not database_only:
|
|
293
|
+
modules.generate(self.__repo)
|
|
294
|
+
os.remove(self.__backup_file('Backups', previous_release))
|
|
295
|
+
|
|
296
|
+
def sync_package(self):
|
|
297
|
+
"Synchronise the package with the current database model"
|
|
298
|
+
modules.generate(self.__repo)
|
|
299
|
+
|
|
300
|
+
def release(self, push):
|
|
301
|
+
"Release a patch"
|
|
302
|
+
# We must be on the first branch in devel (see CHANGELOG)
|
|
303
|
+
next_release = self.__repo.changelog.releases_in_dev[0]
|
|
304
|
+
next_branch = f'hop_{next_release}'
|
|
305
|
+
if next_branch != self.__repo.hgit.branch:
|
|
306
|
+
utils.error(f'Next release is {next_release} Please switch to the branch {next_branch}!\n', 1)
|
|
307
|
+
# Git repo must be clean
|
|
308
|
+
if not self.__repo.hgit.repos_is_clean():
|
|
309
|
+
utils.error(
|
|
310
|
+
f'Please `git commit` your changes before releasing {next_release}.\n', exit_code=1)
|
|
311
|
+
# The patch must be applied and the last to apply
|
|
312
|
+
if self.__repo.database.last_release_s != next_release:
|
|
313
|
+
utils.error(f'Please `hop test` before releasing {next_release}.\n', exit_code=1)
|
|
314
|
+
# If we undo the patch (db only) and re-apply it the repo must still be clear.
|
|
315
|
+
self.undo(database_only=True)
|
|
316
|
+
self.apply(next_release, force=True)
|
|
317
|
+
if not self.__repo.hgit.repos_is_clean():
|
|
318
|
+
utils.error(
|
|
319
|
+
'Something has changed when re-applying the release. This should not happen.\n',
|
|
320
|
+
exit_code=1)
|
|
321
|
+
# do we have pytest
|
|
322
|
+
if PYTEST_OK:
|
|
323
|
+
try:
|
|
324
|
+
subprocess.run(['pytest', self.__repo.name], check=True)
|
|
325
|
+
except subprocess.CalledProcessError:
|
|
326
|
+
utils.error('Tests must pass in order to release.\n', exit_code=1)
|
|
327
|
+
# So far, so good
|
|
328
|
+
svg_file = self.__backup_file('Releases', next_release)
|
|
329
|
+
print(f'Saving the database into {svg_file}')
|
|
330
|
+
self.__repo.database.execute_pg_command(
|
|
331
|
+
'pg_dump', '-xO', '-f', svg_file, stderr=subprocess.PIPE)
|
|
332
|
+
self.__repo.hgit.add(svg_file)
|
|
333
|
+
self.__repo.hgit.commit("-m", f"Add sql for release {next_release}")
|
|
334
|
+
self.__repo.hgit.rebase_to_hop_main(push)
|
|
335
|
+
else:
|
|
336
|
+
utils.error('pytest is not installed!\n', 1)
|
|
337
|
+
|
|
338
|
+
def upgrade_prod(self):
|
|
339
|
+
"Upgrade the production"
|
|
340
|
+
self.__assert_main_branch()
|
|
341
|
+
self.__save_db(self.__repo.database.last_release_s)
|
|
342
|
+
for release in self.__repo.changelog.releases_to_apply_in_prod:
|
|
343
|
+
self.apply(release, save_db=False)
|
|
344
|
+
|
|
345
|
+
def restore(self, release):
|
|
346
|
+
"Restore the database and package to a release (in production)"
|
|
347
|
+
self.__restore_db(release)
|
|
348
|
+
# Do we have the backup
|
|
@@ -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
|
+
|