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.
Files changed (38) hide show
  1. half_orm_dev/__init__.py +0 -0
  2. half_orm_dev/changelog.py +117 -0
  3. half_orm_dev/cli_extension.py +171 -0
  4. half_orm_dev/database.py +127 -0
  5. half_orm_dev/db_conn.py +134 -0
  6. half_orm_dev/hgit.py +202 -0
  7. half_orm_dev/hop.py +167 -0
  8. half_orm_dev/manifest.py +43 -0
  9. half_orm_dev/modules.py +357 -0
  10. half_orm_dev/patch.py +348 -0
  11. half_orm_dev/patches/0/1/0/00_half_orm_meta.database.sql +34 -0
  12. half_orm_dev/patches/0/1/0/01_alter_half_orm_meta.hop_release.sql +2 -0
  13. half_orm_dev/patches/0/1/0/02_half_orm_meta.view.hop_penultimate_release.sql +3 -0
  14. half_orm_dev/patches/log +2 -0
  15. half_orm_dev/patches/sql/half_orm_meta.sql +208 -0
  16. half_orm_dev/repo.py +266 -0
  17. half_orm_dev/templates/.gitignore +14 -0
  18. half_orm_dev/templates/MANIFEST.in +1 -0
  19. half_orm_dev/templates/Pipfile +13 -0
  20. half_orm_dev/templates/README +25 -0
  21. half_orm_dev/templates/base_test +26 -0
  22. half_orm_dev/templates/init_module_template +6 -0
  23. half_orm_dev/templates/module_template_1 +12 -0
  24. half_orm_dev/templates/module_template_2 +5 -0
  25. half_orm_dev/templates/module_template_3 +3 -0
  26. half_orm_dev/templates/relation_test +19 -0
  27. half_orm_dev/templates/setup.py +81 -0
  28. half_orm_dev/templates/sql_adapter +9 -0
  29. half_orm_dev/templates/warning +12 -0
  30. half_orm_dev/utils.py +12 -0
  31. half_orm_dev/version.txt +1 -0
  32. half_orm_dev-0.16.0a1.dist-info/METADATA +314 -0
  33. half_orm_dev-0.16.0a1.dist-info/RECORD +38 -0
  34. half_orm_dev-0.16.0a1.dist-info/WHEEL +5 -0
  35. half_orm_dev-0.16.0a1.dist-info/entry_points.txt +2 -0
  36. half_orm_dev-0.16.0a1.dist-info/licenses/AUTHORS +3 -0
  37. half_orm_dev-0.16.0a1.dist-info/licenses/LICENSE +14 -0
  38. 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,2 @@
1
+ alter table half_orm_meta.hop_release add column dbid text references half_orm_meta.database(id) on update cascade;
2
+ alter table half_orm_meta.hop_release add column hop_release text;
@@ -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;
@@ -0,0 +1,2 @@
1
+ 0.0.26 t581fd33
2
+ 0.1.0
@@ -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
+