prick 0.18.0 → 0.20.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +10 -4
  3. data/README.md +7 -7
  4. data/Rakefile +3 -1
  5. data/TODO +13 -11
  6. data/bin/console +2 -1
  7. data/doc/build-yml.txt +14 -0
  8. data/exe/prick +264 -28
  9. data/lib/builder/batch.rb +147 -0
  10. data/lib/builder/builder.rb +122 -0
  11. data/lib/builder/node.rb +189 -0
  12. data/lib/builder/node_pool.rb +105 -0
  13. data/lib/builder/parser.rb +120 -0
  14. data/lib/local/command.rb +193 -0
  15. data/lib/{prick → local}/git.rb +148 -22
  16. data/lib/local/timer.rb +98 -0
  17. data/lib/prick/constants.rb +54 -66
  18. data/lib/prick/diff.rb +28 -18
  19. data/lib/prick/prick_version.rb +161 -0
  20. data/lib/prick/state.rb +80 -165
  21. data/lib/prick/version.rb +2 -163
  22. data/lib/prick.rb +43 -27
  23. data/lib/share/init/.gitignore +10 -0
  24. data/lib/share/init/.prick-context +2 -0
  25. data/lib/share/init/.rspec +3 -0
  26. data/{share/schema/schema/public → lib/share/init/migration}/.keep +0 -0
  27. data/lib/share/init/prick.yml +6 -0
  28. data/lib/share/init/schema/.keep +0 -0
  29. data/lib/share/init/schema/build.yml +2 -0
  30. data/lib/share/init/schema/prick/.keep +0 -0
  31. data/lib/share/init/schema/prick/build.yml +5 -0
  32. data/lib/share/init/schema/prick/data.sql +6 -0
  33. data/{share/schema → lib/share/init}/schema/prick/tables.sql +2 -3
  34. data/lib/share/init/schema/public/.keep +0 -0
  35. data/lib/share/init/spec/prick_helper.rb +1 -0
  36. data/lib/share/init/spec/prick_spec.rb +6 -0
  37. data/lib/share/init/spec/spec_helper.rb +50 -0
  38. data/lib/share/migrate/migration/build.yml +4 -0
  39. data/lib/share/migrate/migration/diff.after-tables.sql +0 -0
  40. data/lib/share/migrate/migration/diff.before-tables.sql +0 -0
  41. data/lib/share/migrate/migration/diff.tables.sql +0 -0
  42. data/lib/subcommand/prick-build.rb +55 -0
  43. data/lib/subcommand/prick-create.rb +78 -0
  44. data/lib/subcommand/prick-drop.rb +25 -0
  45. data/lib/subcommand/prick-fox.rb +62 -0
  46. data/lib/subcommand/prick-init.rb +46 -0
  47. data/lib/subcommand/prick-make.rb +202 -0
  48. data/lib/subcommand/prick-migrate.rb +37 -0
  49. data/lib/subcommand/prick-release.rb +23 -0
  50. data/lib/subcommand/prick-setup.rb +20 -0
  51. data/lib/subcommand/prick-teardown.rb +18 -0
  52. data/prick.gemspec +43 -16
  53. metadata +161 -72
  54. data/.gitignore +0 -29
  55. data/.travis.yml +0 -7
  56. data/doc/create_release.txt +0 -17
  57. data/doc/flow.txt +0 -98
  58. data/doc/migra +0 -1
  59. data/doc/migrations.txt +0 -172
  60. data/doc/notes.txt +0 -116
  61. data/doc/prick.txt +0 -114
  62. data/doc/sh.prick +0 -316
  63. data/lib/ext/algorithm.rb +0 -14
  64. data/lib/ext/fileutils.rb +0 -26
  65. data/lib/ext/forward_method.rb +0 -18
  66. data/lib/ext/pg.rb +0 -18
  67. data/lib/ext/shortest_path.rb +0 -44
  68. data/lib/prick/archive.rb +0 -124
  69. data/lib/prick/branch.rb +0 -254
  70. data/lib/prick/builder.rb +0 -205
  71. data/lib/prick/cache.rb +0 -34
  72. data/lib/prick/command.rb +0 -102
  73. data/lib/prick/database.rb +0 -82
  74. data/lib/prick/dsort.rb +0 -151
  75. data/lib/prick/ensure.rb +0 -119
  76. data/lib/prick/exceptions.rb +0 -25
  77. data/lib/prick/head.rb +0 -183
  78. data/lib/prick/migration.rb +0 -70
  79. data/lib/prick/program.rb +0 -506
  80. data/lib/prick/project.rb +0 -626
  81. data/lib/prick/rdbms.rb +0 -137
  82. data/lib/prick/schema.rb +0 -27
  83. data/lib/prick/share.rb +0 -64
  84. data/libexec/strip-comments +0 -33
  85. data/make_releases +0 -72
  86. data/make_schema +0 -10
  87. data/share/diff/diff.after-tables.sql +0 -4
  88. data/share/diff/diff.before-tables.sql +0 -4
  89. data/share/diff/diff.tables.sql +0 -8
  90. data/share/features/diff.sql +0 -2
  91. data/share/features/feature/diff.sql +0 -2
  92. data/share/features/feature/migrate.sql +0 -2
  93. data/share/features/features.sql +0 -2
  94. data/share/features/features.yml +0 -2
  95. data/share/features/migrations.sql +0 -4
  96. data/share/gitignore +0 -2
  97. data/share/migration/diff.tables.sql +0 -8
  98. data/share/migration/features.yml +0 -6
  99. data/share/migration/migrate.sql +0 -3
  100. data/share/migration/migrate.yml +0 -8
  101. data/share/migration/tables.sql +0 -3
  102. data/share/schema/build.yml +0 -14
  103. data/share/schema/schema/build.yml +0 -3
  104. data/share/schema/schema/prick/build.yml +0 -14
  105. data/share/schema/schema/prick/data.sql +0 -7
  106. data/share/schema/schema/prick/schema.sql +0 -3
  107. data/share/schema/schema/public/build.yml +0 -13
  108. data/share/schema/schema.sql +0 -3
  109. data/test_assorted +0 -192
  110. data/test_feature +0 -112
  111. data/test_refactor +0 -34
  112. data/test_single_dev +0 -83
data/lib/prick/branch.rb DELETED
@@ -1,254 +0,0 @@
1
- require "prick/state.rb"
2
-
3
- module Prick
4
- class Branch
5
- # Branch name. It is usually equal to the version but migrations use a
6
- # <base_version>_<version> format instead
7
- attr_reader :name
8
-
9
- # Version of this branch. Note that version and base_version are the same
10
- # for feature branches
11
- attr_reader :version
12
-
13
- # Base version
14
- attr_reader :base_version
15
-
16
- # The release directory. It contains the release's .prick-migration file
17
- attr_reader :directory
18
-
19
- # The Schema object. This is shared by many all branches
20
- attr_reader :schema
21
-
22
- # Migration object. Running the migration object on the base release will
23
- # migrate it to this release
24
- attr_reader :migration
25
-
26
- # Database name
27
- def database() name end
28
-
29
- # Classifiers
30
- def release?() self.is_a?(Release) && !prerelease? end
31
- def prerelease?() self.is_a?(PreRelease) end
32
- def feature?() self.is_a?(Feature) end
33
- def migration?() self.is_a?(MigrationRelease) end
34
-
35
- # Note that `name` can be nil. It defaults to `version.to_s`
36
- def initialize(name, version, base_version, directory, migration)
37
- @name = name || migration.version.to_s
38
- @version = version
39
- @base_version = base_version
40
- @directory = directory
41
- @schema = Schema.new
42
- @migration = migration
43
- end
44
-
45
- def dump
46
- puts "#{self.class}"
47
- indent {
48
- puts "name: #{name}"
49
- puts "version: #{version}"
50
- puts "base_version: #{base_version}"
51
- puts "directory: #{directory}"
52
- print "migration: "
53
- migration.dump
54
- puts "database: #{database}"
55
- }
56
- end
57
-
58
- def self.load(name) raise NotThis end
59
-
60
- # True if the branch exists in git
61
- def exist?() self.class.exist?(name) end
62
-
63
- def create()
64
- !exist? or raise Error, "Can't create branch #{name}, exists already"
65
- Git.create_branch(name)
66
- Git.checkout_branch(name)
67
- prepare if !prepared?
68
- self
69
- end
70
-
71
- # True if the branch exists on disk
72
- def present?() self.class.present?(name) end
73
-
74
- def prepared?() @migration.exist? end
75
- def prepare() @migration.create end
76
-
77
- def include(feature_version) @migration.append_feature(feature_version) end
78
-
79
- def build(database) schema.build(database) end
80
-
81
- # Used to checkout migrations. MigrationReleases checks out the
82
- # corresponding branch while Release and PreRelease checks out a tag
83
- def checkout_release() Git.checkout_branch(version) end
84
-
85
- def migrate(database)
86
- @migration.migrate(database)
87
- end
88
-
89
- def migrate_features(database)
90
- @migration.migrate_features(database)
91
- end
92
-
93
- def self.directory(name) raise NotThis end
94
- def self.exist?(name) Git.branch?(name) || Git.tag?(name) end
95
- def self.present?(name) File.exist?(directory(name)) end
96
-
97
- def <=>(other)
98
- if !self.is_a?(MigrationRelease) && other.is_a?(MigrationRelease)
99
- compare(other.base_version, other.version)
100
- else
101
- compare(other.version, other.base_version)
102
- end
103
- end
104
-
105
- private
106
- def compare(other_version, other_base_version)
107
- r = version <=> other_version
108
- return r if r != 0
109
- if base_version.nil?
110
- other_base_version.nil? ? 0 : -1
111
- elsif other_base_version.nil?
112
- 1
113
- else
114
- base_version <=> other_base_version
115
- end
116
- end
117
- end
118
-
119
- class AbstractRelease < Branch
120
- def create(schema_version = self.version)
121
- super()
122
- schema.version = schema_version
123
- Git.add schema.version_file
124
- self
125
- end
126
-
127
- def checkout_release() Git.checkout_tag(version) end
128
-
129
- def tag!() Git.create_tag(version) end
130
- end
131
-
132
- class Release < AbstractRelease
133
- def initialize(version, base_version)
134
- !version.zero? || base_version.nil? or raise Internal, "Version 0.0.0 has no base release"
135
- directory = self.class.directory(version.to_s)
136
- migration = ReleaseMigration.new(version, base_version)
137
- super(nil, version, base_version, directory, migration)
138
- end
139
-
140
- def self.load(name)
141
- migration = ReleaseMigration.load(directory(name))
142
- self.new(migration.version, migration.base_version)
143
- end
144
-
145
- def self.directory(name)
146
- File.join(RELEASES_DIR, name)
147
- end
148
- end
149
-
150
- class PreRelease < AbstractRelease
151
- attr_reader :prerelease_version
152
-
153
- def database() version.to_s end
154
-
155
- def initialize(prerelease_version, base_version)
156
- @prerelease_version = prerelease_version
157
- version = prerelease_version.truncate(:pre)
158
- directory = Release.directory(version.to_s)
159
- migration = ReleaseMigration.new(version, base_version)
160
- super(prerelease_version.to_s, version, base_version, directory, migration)
161
- end
162
-
163
- def self.load(name, prerelease_version)
164
- migration = ReleaseMigration.load(directory(name))
165
- self.new(prerelease_version, migration.base_version)
166
- end
167
-
168
- def create()
169
- super(prerelease_version)
170
- end
171
-
172
- def increment
173
- PreRelease.new(prerelease_version.increment(:pre), base_version)
174
- end
175
-
176
- def self.directory(name)
177
- version = Version.new(name)
178
- Release.directory(version.truncate(:pre).to_s)
179
- end
180
- end
181
-
182
- class MigrationRelease < Branch
183
- def database() version.to_s end
184
-
185
- def initialize(version, base_version)
186
- migration = MigrationMigration.new(version, base_version)
187
- super(migration.name, version, base_version, migration.dir, migration)
188
- end
189
-
190
- def self.load(name)
191
- directory = self.directory(name)
192
- migration_state = MigrationState.new(directory).read
193
- self.new(migration_state.version, migration_state.base_version)
194
- end
195
-
196
- def self.directory(name)
197
- File.join(MIGRATIONS_DIR, name)
198
- end
199
- end
200
-
201
- # Feature maintains a migration in the parent release but it is ignored when
202
- # including the feature
203
- class Feature < Branch
204
- attr_reader :feature_name
205
-
206
- def database() base_version.to_s end
207
-
208
- def initialize(feature_name, base_version)
209
- version = Version.new(base_version, feature: feature_name)
210
- migration = FeatureMigration.new(feature_name, base_version)
211
- super(version.to_s, version, base_version, migration.features_dir, migration)
212
- @feature_name = feature_name
213
- end
214
-
215
- def create
216
- super()
217
- schema.version = version
218
- Git.add schema.version_file
219
- self
220
- end
221
-
222
- def include(feature_version) @migration.insert_feature(feature_version) end
223
-
224
- def rebase(base_version)
225
- new_version = Version.new(new_base_version, feature: feature_name)
226
- name = new_version.to_s
227
-
228
- Git.create_branch(name)
229
- Git.checkout_branch(name)
230
-
231
- symlink = self.directory(name)
232
- FileUtils.ln_sr(directory, symlink, force: true)
233
- Git.add(symlink)
234
-
235
- migration.base_version = new_base_version
236
- migration.save
237
-
238
- schema.version = new_version
239
- Git.add ect.schema.version_file
240
- end
241
-
242
- def self.load(name)
243
- directory = self.directory(name)
244
- migration_state = MigrationState.new(directory).read
245
- self.new(migration_state.version.feature, migration_state.base_version)
246
- end
247
-
248
- def self.directory(name)
249
- version = Version.new(name)
250
- File.join(Release.directory(version.truncate(:feature).to_s), version.feature.to_s)
251
- end
252
- end
253
- end
254
-
data/lib/prick/builder.rb DELETED
@@ -1,205 +0,0 @@
1
- require "prick/state.rb"
2
-
3
- module Prick
4
- # Builder is a procedural object for building schemas and executing
5
- # migrations. Builder use resources that can be executables, SQL files, YAML
6
- # files, or directories. A resource is identified by a name (eg. 'my-tables')
7
- # and is used to match a build file. Build files are looked up in the
8
- # following order:
9
- #
10
- # #{name} executable
11
- # #{name}.* executable
12
- # #{name}.yml
13
- # #{name}.sql
14
- # #{name}/
15
- #
16
- # The output from executable objects is expected to be SQL statements that
17
- # are fed into postgres
18
- #
19
- # When a resource match a directory, the directory can contain a special
20
- # default resource ('build' or 'migrate') that takes over the rest of the
21
- # build process for that directory. Typically, you'll use the 'build.yml' or
22
- # 'migrate.yml' to control the build order of the other resources. If a
23
- # directory doesn't contain a build resource, the resources in the directory
24
- # are built in alphabetic order
25
- #
26
- # Build (but not migration) SQL scripts and executables are executed with
27
- # search path set to containing schema
28
- #
29
- class Builder
30
- attr_reader :database
31
- attr_reader :directory
32
- attr_reader :default # either "build" or "migrate"
33
- attr_reader :lines
34
-
35
- def initialize(database, directory, default)
36
- @database = database
37
- @directory = directory
38
- @default = default
39
- @execute = true
40
- @lines = []
41
- @cwd = Dir.getwd # used in error messages
42
- end
43
-
44
- def build(subject = default, execute: true)
45
- @execute = execute
46
- @lines = []
47
- # puts "Building #{subject.inspect}"
48
- Dir.chdir(directory) {
49
- if subject
50
- build_subject(subject)
51
- else
52
- build_directory(".")
53
- end
54
- }
55
- @lines.reject! { |l| l =~ /^\s*--/ || l =~ /^\s*$/ }
56
- self
57
- end
58
-
59
- def self.yml_file(directory) raise NotThis end
60
-
61
- protected
62
- def do_dir(path, &block)
63
- # puts "do_dir(#{path.inspect})"
64
- dir, file = File.split(path)
65
- Dir.chdir(dir) { yield(file) }
66
- end
67
-
68
- def do_sql(path, schema: nil)
69
- # puts "do_sql(#{path})"
70
- do_dir(path) { |file|
71
- if @execute
72
- begin
73
- Rdbms.exec_file(database.name, file, user: database.user, schema: schema)
74
- rescue Command::Error => ex
75
- $stderr.puts ex.stderr
76
- $stderr.puts "in #{reldir}/#{file}"
77
- exit 1
78
- end
79
- else
80
- @lines += IO.readlines(file).map(&:chomp)
81
- end
82
- }
83
- true
84
- end
85
-
86
- def do_exe(path, args = [], schema: nil)
87
- # puts "do_exe(#{path.inspect}, #{args.inspect})"
88
- do_dir(path) { |file|
89
- cmd = (["./#{file}"] + args).join(" ")
90
- lines = Command.command cmd, stdin: [database.name, database.user]
91
- if @execute
92
- begin
93
- Rdbms.exec_sql(database.name, lines.join("\n"), user: database.user, schema: schema)
94
- rescue Command::Error => ex
95
- $stderr.puts ex.stderr
96
- $stderr.puts "from #{reldir}/#{file}"
97
- exit 1
98
- end
99
- else
100
- @lines += lines
101
- end
102
- }
103
- true
104
- end
105
-
106
- def do_yml(path)
107
- # puts "do_yml(#{path})"
108
- dir, file = File.split(path)
109
- YAML.load(File.read(path))&.each { |subject| build_subject(File.join(dir, subject)) }
110
- true
111
- end
112
-
113
- # A subject can be both an abstract subject or a concrete file (*.yml, *.sql)
114
- def build_subject(subject)
115
- cmd, *args = subject.split(/\s+/)
116
- if File.file?(cmd) && File.executable?(cmd)
117
- do_exe(cmd, args)
118
- elsif File.file?(subject) && subject.end_with?(".yml")
119
- do_yml(subject)
120
- elsif File.file?(subject) && subject.end_with?(".sql")
121
- do_sql(subject)
122
- elsif File.exist?(yml_file = "#{subject}.yml")
123
- do_yml(yml_file)
124
- elsif File.exist?(sql_file = "#{subject}.sql")
125
- do_sql(sql_file)
126
- elsif File.directory?(subject)
127
- build_directory(subject)
128
- else
129
- false
130
- end
131
- end
132
-
133
- def build_directory(path)
134
- Dir.chdir(path) {
135
- build_subject(File.join(path, @default)) || begin
136
- if Dir["#{path}/#{default}", "#{path}/#{default}.yml", "#{path}/#{default}.sql"]
137
- subjects = [default]
138
- else
139
- exes = candidates.select { |file| File.file?(file) && File.executable?(file) }
140
- ymls = candidates.select { |file| file.end_with?(".yml") }.map { |f| f.sub(/\.yml$/, "") }
141
- sqls = candidates.select { |file| file.end_with?(".sql") }.map { |f| f.sub(/\.sql$/, "") }
142
- dirs = candidates.select { |file| File.directory?(file) }
143
- subjects = (exes + ymls + sqls + dirs).uniq.sort.reject { |f| f != "diff" }
144
- end
145
- subjects.inject(false) { |a, s| build_subject(s) || a }
146
- end
147
- }
148
- end
149
-
150
- private
151
- # Return the relative path to the current directory from the directory of
152
- # the time of the instantiation of the Builder object. Used in error
153
- # messages
154
- def reldir
155
- Dir.getwd.sub(/^#{@cwd}\//, "")
156
- end
157
- end
158
-
159
- class MigrationBuilder < Builder
160
- def initialize(database, directory)
161
- super(database, directory, "migrate")
162
- end
163
-
164
- def self.yml_file(directory) File.join(directory, "migrate") + ".yml" end
165
- end
166
-
167
- class SchemaBuilder < Builder
168
- def initialize(database, directory)
169
- super(database, directory, "build")
170
- end
171
-
172
- def build(subject = nil, execute: true)
173
- if subject
174
- @execute = execute
175
- @lines = []
176
- Dir.chdir(directory) {
177
- if File.executable?(subject)
178
- do_exe(subject)
179
- elsif subject.end_with?(".sql")
180
- do_sql(subject)
181
- else
182
- build_subject(subject)
183
- end
184
- }
185
- else
186
- super(execute: execute)
187
- end
188
- self
189
- end
190
-
191
- def self.yml_file(directory) File.join(directory, "build") + ".yml" end
192
-
193
- protected
194
- def do_sql(path)
195
- schema = Dir.getwd.sub(/^.*schema\/([^\/]+)(?:\/.*)?$/, '\1')
196
- super(path, schema: schema)
197
- end
198
-
199
- def do_exe(path, args = [])
200
- schema = Dir.getwd.sub(/^.*schema\/([^\/]+)(?:\/.*)?$/, '\1')
201
- super(path, args, schema: schema)
202
- end
203
- end
204
- end
205
-
data/lib/prick/cache.rb DELETED
@@ -1,34 +0,0 @@
1
-
2
- module Prick
3
- class Cache
4
- def initialize
5
- end
6
-
7
- def exist?(version)
8
- File.exist?(file(version))
9
- end
10
-
11
- def list
12
- Dir.glob(File.join(CACHE_DIR, "*.sql.gz"))
13
- end
14
-
15
- def load(database, version)
16
- Rdbms.load(database, file(version))
17
- end
18
-
19
- def save(database, version = database.version)
20
- Rdbms.save(database, file(version))
21
- end
22
-
23
- # Returns list of removed files
24
- def clean
25
- l = list
26
- FileUtils.rm(l)
27
- l
28
- end
29
-
30
- def file(version)
31
- File.join(CACHE_DIR, "#{version}.sql.gz")
32
- end
33
- end
34
- end
data/lib/prick/command.rb DELETED
@@ -1,102 +0,0 @@
1
- require 'fcntl'
2
-
3
- module Command
4
- class Error < RuntimeError
5
- attr_reader :status
6
- attr_reader :stdout
7
- attr_reader :stderr
8
-
9
- def initialize(message, status, stdout, stderr)
10
- super(message)
11
- @status = status
12
- @stdout = stdout
13
- @stderr = stderr
14
- end
15
- end
16
-
17
- # Execute the shell command 'cmd' and return standard output as an array of
18
- # strings. while stderr is passed through unless stderr: is false. If stderr:
19
- # is true, it returns a tuple of [stdout, stderr] instead. The shell command
20
- # is executed with the `errexit` and `pipefail` bash options
21
- #
22
- # The :stdin option is a line or an array of lines that'll be fed to the
23
- # standard input of the command. Default is nil
24
- #
25
- # It raises a Command::Error exception if the command fails unless :fail is
26
- # true. The exit status of the last command is stored in ::status
27
- #
28
- def command(cmd, stdin: nil, stderr: false, fail: true)
29
- cmd = "set -o errexit\nset -o pipefail\n#{cmd}"
30
-
31
- pw = IO::pipe # pipe[0] for read, pipe[1] for write
32
- pr = IO::pipe
33
- pe = IO::pipe
34
-
35
- STDOUT.flush
36
-
37
- pid = fork {
38
- pw[1].close
39
- pr[0].close
40
- pe[0].close
41
-
42
- STDIN.reopen(pw[0])
43
- pw[0].close
44
-
45
- STDOUT.reopen(pr[1])
46
- pr[1].close
47
-
48
- STDERR.reopen(pe[1])
49
- pe[1].close
50
-
51
- exec(cmd)
52
- }
53
-
54
- pw[0].close
55
- pr[1].close
56
- pe[1].close
57
-
58
- if stdin
59
- pw[1].puts(stdin)
60
- pw[1].flush
61
- pw[1].close
62
- end
63
-
64
- @status = Process.waitpid2(pid)[1].exitstatus
65
-
66
- out = pr[0].readlines.collect { |line| line.chop }
67
- err = pe[0].readlines.collect { |line| line.chop }.grep_v(/^NOTICE:/)
68
-
69
- pw[1].close if !stdin
70
- pr[0].close
71
- pe[0].close
72
-
73
- result =
74
- case stderr
75
- when true; [out, err]
76
- when false; out
77
- when NilClass; $stderr.puts err
78
- else
79
- raise Internal, "Unexpected value for :stderr - #{stderr.inspect}"
80
- end
81
-
82
- if @status == 0 || fail == false
83
- result
84
- elsif fail
85
- raise Command::Error.new("\n" + cmd + "\n" + (out + err).join("\n") + "\n", status, out, err)
86
- end
87
- end
88
-
89
- # Exit status of the last command
90
- def status() @status end
91
-
92
- # Like command but returns true if the command exited with the expected status
93
- def command?(cmd, expect: 0)
94
- command(cmd, fail: false)
95
- @status == expect
96
- end
97
-
98
- module_function :command
99
- module_function :status
100
- module_function :command?
101
- end
102
-
@@ -1,82 +0,0 @@
1
-
2
- module Prick
3
- class Database
4
- attr_reader :name
5
- attr_reader :user
6
-
7
- def initialize(name, user)
8
- name != "" or raise "Illegal database name"
9
- @name = name
10
- @user = user
11
- @version = nil
12
- end
13
-
14
- def version(cache: true)
15
- if cache && @version
16
- @version
17
- else
18
- @version = begin
19
- version =
20
- if exist? && Rdbms.exist_table?(name, "prick", "versions")
21
- Rdbms.select(name, "select version from prick.versions")&.first&.first
22
- else
23
- nil
24
- end
25
- version && Version.new(version)
26
- end
27
- end
28
- end
29
-
30
- def version=(version)
31
- @version = version && Version.new(version)
32
- Rdbms.exec_sql(name, %(
33
- update prick.versions
34
- set major = '#{@version.major}',
35
- minor = '#{@version.minor}',
36
- patch = '#{@version.patch}',
37
- pre = '#{@version.pre}',
38
- version = '#{@version.to_s}'
39
- )) if @version
40
- @version
41
- end
42
-
43
- def exist?() Rdbms.exist_database?(name) end
44
- def create() Rdbms.create_database(name, owner: user) end
45
- def recreate() drop if exist?; create; @version = nil end
46
- def drop() Rdbms.drop_database(name, fail: false); @version = nil end
47
-
48
- def loaded?() !version.nil? end
49
-
50
- def load(file)
51
- !loaded? or raise Internal, "Database #{name} is already loaded"
52
- Rdbms.load(name, file, user: user)
53
- version # Provoke load of version
54
- end
55
-
56
- def save(file)
57
- loaded? or raise Internal, "Database #{name} is not loaded"
58
- Rdbms.save(name, file)
59
- @version = nil
60
- end
61
-
62
- # Hollow-out a database without dropping it. This is useful compared to a
63
- # simple drop database since it wont block on other sessions wont block
64
- def clean()
65
- if exist?
66
- schemas = Rdbms.select_values(
67
- "postgres",
68
- "select nspname from pg_namespace where nspowner != 10")
69
- for schema in schemas
70
- Rdbms.exec_sql(name, "drop schema \"#{schema}\" cascade")
71
- end
72
- Rdbms.exec_sql(name, "drop schema if exists public cascade")
73
- Rdbms.exec_sql(name, "create schema public authorization postgres")
74
- Rdbms.exec_sql(name, "grant usage, create on schema public to public")
75
- else
76
- create
77
- end
78
- end
79
-
80
- def to_s() name end
81
- end
82
- end