logidze 0.11.0 → 1.2.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +79 -4
- data/LICENSE.txt +1 -1
- data/README.md +305 -102
- data/lib/generators/logidze/fx_helper.rb +17 -0
- data/lib/generators/logidze/inject_sql.rb +18 -0
- data/lib/generators/logidze/install/USAGE +6 -1
- data/lib/generators/logidze/install/functions/logidze_capture_exception.sql +23 -0
- data/lib/generators/logidze/install/functions/logidze_compact_history.sql +38 -0
- data/lib/generators/logidze/install/functions/logidze_filter_keys.sql +27 -0
- data/lib/generators/logidze/install/functions/logidze_logger.sql +203 -0
- data/lib/generators/logidze/install/functions/logidze_snapshot.sql +33 -0
- data/lib/generators/logidze/install/functions/logidze_version.sql +21 -0
- data/lib/generators/logidze/install/install_generator.rb +43 -1
- data/lib/generators/logidze/install/templates/hstore.rb.erb +1 -1
- data/lib/generators/logidze/install/templates/migration.rb.erb +19 -232
- data/lib/generators/logidze/install/templates/migration_fx.rb.erb +41 -0
- data/lib/generators/logidze/model/model_generator.rb +53 -13
- data/lib/generators/logidze/model/templates/migration.rb.erb +57 -36
- data/lib/generators/logidze/model/triggers/logidze.sql +6 -0
- data/lib/logidze.rb +37 -14
- data/lib/logidze/engine.rb +9 -0
- data/lib/logidze/has_logidze.rb +1 -1
- data/lib/logidze/history.rb +2 -11
- data/lib/logidze/ignore_log_data.rb +1 -3
- data/lib/logidze/meta.rb +43 -16
- data/lib/logidze/model.rb +51 -44
- data/lib/logidze/utils/check_pending.rb +57 -0
- data/lib/logidze/utils/function_definitions.rb +49 -0
- data/lib/logidze/utils/pending_migration_error.rb +25 -0
- data/lib/logidze/version.rb +1 -1
- metadata +69 -77
- data/.gitattributes +0 -3
- data/.github/ISSUE_TEMPLATE.md +0 -20
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -29
- data/.gitignore +0 -40
- data/.rubocop.yml +0 -55
- data/.travis.yml +0 -42
- data/Gemfile +0 -15
- data/Rakefile +0 -28
- data/assets/pg_log_data_chart.png +0 -0
- data/bench/performance/README.md +0 -109
- data/bench/performance/diff_bench.rb +0 -38
- data/bench/performance/insert_bench.rb +0 -22
- data/bench/performance/memory_profile.rb +0 -56
- data/bench/performance/setup.rb +0 -315
- data/bench/performance/update_bench.rb +0 -38
- data/bench/triggers/Makefile +0 -56
- data/bench/triggers/Readme.md +0 -58
- data/bench/triggers/bench.sql +0 -6
- data/bench/triggers/hstore_trigger_setup.sql +0 -38
- data/bench/triggers/jsonb_minus_2_setup.sql +0 -47
- data/bench/triggers/jsonb_minus_setup.sql +0 -49
- data/bench/triggers/keys2_trigger_setup.sql +0 -44
- data/bench/triggers/keys_trigger_setup.sql +0 -50
- data/bin/console +0 -8
- data/bin/setup +0 -9
- data/gemfiles/rails42.gemfile +0 -6
- data/gemfiles/rails5.gemfile +0 -6
- data/gemfiles/rails52.gemfile +0 -6
- data/gemfiles/rails6.gemfile +0 -6
- data/gemfiles/railsmaster.gemfile +0 -7
- data/lib/logidze/ignore_log_data/association.rb +0 -11
- data/lib/logidze/ignore_log_data/ignored_columns.rb +0 -46
- data/lib/logidze/migration.rb +0 -20
- data/logidze.gemspec +0 -41
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Logidze
|
4
|
+
module Generators
|
5
|
+
# Adds --fx option and provide #fx? method
|
6
|
+
module FxHelper
|
7
|
+
def self.included(base)
|
8
|
+
base.class_option :fx, type: :boolean, optional: true,
|
9
|
+
desc: "Define whether to use fx gem functionality"
|
10
|
+
end
|
11
|
+
|
12
|
+
def fx?
|
13
|
+
options[:fx] || (options[:fx] != false && defined?(::Fx::SchemaDumper))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Logidze
|
4
|
+
module Generators
|
5
|
+
module InjectSql
|
6
|
+
def inject_sql(source, indent: 4)
|
7
|
+
source = ::File.expand_path(find_in_source_paths(source.to_s))
|
8
|
+
|
9
|
+
indent(
|
10
|
+
ERB.new(::File.binread(source)).tap do |erb|
|
11
|
+
erb.filename = source
|
12
|
+
end.result(instance_eval("binding")), # rubocop:disable Style/EvalWithLocation
|
13
|
+
indent
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,7 +1,12 @@
|
|
1
1
|
Description:
|
2
2
|
Generates the necessary files to get you up and running with Logidze gem
|
3
|
-
|
3
|
+
|
4
4
|
Examples:
|
5
5
|
rails generate logidze:install
|
6
6
|
|
7
7
|
This will generate the core migration file with trigger function defined.
|
8
|
+
|
9
|
+
rails generate logidze:install --fx
|
10
|
+
|
11
|
+
This will generate schema.rb compatible migration with `create_function` definitions and separate SQL files.
|
12
|
+
The fx gem must be installed.
|
@@ -0,0 +1,23 @@
|
|
1
|
+
CREATE OR REPLACE FUNCTION logidze_capture_exception(error_data jsonb) RETURNS boolean AS $body$
|
2
|
+
-- version: 1
|
3
|
+
BEGIN
|
4
|
+
-- Feel free to change this function to change Logidze behavior on exception.
|
5
|
+
--
|
6
|
+
-- Return `false` to raise exception or `true` to commit record changes.
|
7
|
+
--
|
8
|
+
-- `error_data` contains:
|
9
|
+
-- - returned_sqlstate
|
10
|
+
-- - message_text
|
11
|
+
-- - pg_exception_detail
|
12
|
+
-- - pg_exception_hint
|
13
|
+
-- - pg_exception_context
|
14
|
+
-- - schema_name
|
15
|
+
-- - table_name
|
16
|
+
-- Learn more about available keys:
|
17
|
+
-- https://www.postgresql.org/docs/9.6/plpgsql-control-structures.html#PLPGSQL-EXCEPTION-DIAGNOSTICS-VALUES
|
18
|
+
--
|
19
|
+
|
20
|
+
return false;
|
21
|
+
END;
|
22
|
+
$body$
|
23
|
+
LANGUAGE plpgsql;
|
@@ -0,0 +1,38 @@
|
|
1
|
+
CREATE OR REPLACE FUNCTION logidze_compact_history(log_data jsonb, cutoff integer DEFAULT 1) RETURNS jsonb AS $body$
|
2
|
+
-- version: 1
|
3
|
+
DECLARE
|
4
|
+
merged jsonb;
|
5
|
+
BEGIN
|
6
|
+
LOOP
|
7
|
+
merged := jsonb_build_object(
|
8
|
+
'ts',
|
9
|
+
log_data#>'{h,1,ts}',
|
10
|
+
'v',
|
11
|
+
log_data#>'{h,1,v}',
|
12
|
+
'c',
|
13
|
+
(log_data#>'{h,0,c}') || (log_data#>'{h,1,c}')
|
14
|
+
);
|
15
|
+
|
16
|
+
IF (log_data#>'{h,1}' ? 'm') THEN
|
17
|
+
merged := jsonb_set(merged, ARRAY['m'], log_data#>'{h,1,m}');
|
18
|
+
END IF;
|
19
|
+
|
20
|
+
log_data := jsonb_set(
|
21
|
+
log_data,
|
22
|
+
'{h}',
|
23
|
+
jsonb_set(
|
24
|
+
log_data->'h',
|
25
|
+
'{1}',
|
26
|
+
merged
|
27
|
+
) - 0
|
28
|
+
);
|
29
|
+
|
30
|
+
cutoff := cutoff - 1;
|
31
|
+
|
32
|
+
EXIT WHEN cutoff <= 0;
|
33
|
+
END LOOP;
|
34
|
+
|
35
|
+
return log_data;
|
36
|
+
END;
|
37
|
+
$body$
|
38
|
+
LANGUAGE plpgsql;
|
@@ -0,0 +1,27 @@
|
|
1
|
+
CREATE OR REPLACE FUNCTION logidze_filter_keys(obj jsonb, keys text[], include_columns boolean DEFAULT false) RETURNS jsonb AS $body$
|
2
|
+
-- version: 1
|
3
|
+
DECLARE
|
4
|
+
res jsonb;
|
5
|
+
key text;
|
6
|
+
BEGIN
|
7
|
+
res := '{}';
|
8
|
+
|
9
|
+
IF include_columns THEN
|
10
|
+
FOREACH key IN ARRAY keys
|
11
|
+
LOOP
|
12
|
+
IF obj ? key THEN
|
13
|
+
res = jsonb_insert(res, ARRAY[key], obj->key);
|
14
|
+
END IF;
|
15
|
+
END LOOP;
|
16
|
+
ELSE
|
17
|
+
res = obj;
|
18
|
+
FOREACH key IN ARRAY keys
|
19
|
+
LOOP
|
20
|
+
res = res - key;
|
21
|
+
END LOOP;
|
22
|
+
END IF;
|
23
|
+
|
24
|
+
RETURN res;
|
25
|
+
END;
|
26
|
+
$body$
|
27
|
+
LANGUAGE plpgsql;
|
@@ -0,0 +1,203 @@
|
|
1
|
+
CREATE OR REPLACE FUNCTION logidze_logger() RETURNS TRIGGER AS $body$
|
2
|
+
-- version: 2
|
3
|
+
DECLARE
|
4
|
+
changes jsonb;
|
5
|
+
version jsonb;
|
6
|
+
snapshot jsonb;
|
7
|
+
new_v integer;
|
8
|
+
size integer;
|
9
|
+
history_limit integer;
|
10
|
+
debounce_time integer;
|
11
|
+
current_version integer;
|
12
|
+
k text;
|
13
|
+
iterator integer;
|
14
|
+
item record;
|
15
|
+
columns text[];
|
16
|
+
include_columns boolean;
|
17
|
+
ts timestamp with time zone;
|
18
|
+
ts_column text;
|
19
|
+
err_sqlstate text;
|
20
|
+
err_message text;
|
21
|
+
err_detail text;
|
22
|
+
err_hint text;
|
23
|
+
err_context text;
|
24
|
+
err_table_name text;
|
25
|
+
err_schema_name text;
|
26
|
+
err_jsonb jsonb;
|
27
|
+
err_captured boolean;
|
28
|
+
BEGIN
|
29
|
+
ts_column := NULLIF(TG_ARGV[1], 'null');
|
30
|
+
columns := NULLIF(TG_ARGV[2], 'null');
|
31
|
+
include_columns := NULLIF(TG_ARGV[3], 'null');
|
32
|
+
|
33
|
+
IF TG_OP = 'INSERT' THEN
|
34
|
+
IF columns IS NOT NULL THEN
|
35
|
+
snapshot = logidze_snapshot(to_jsonb(NEW.*), ts_column, columns, include_columns);
|
36
|
+
ELSE
|
37
|
+
snapshot = logidze_snapshot(to_jsonb(NEW.*), ts_column);
|
38
|
+
END IF;
|
39
|
+
|
40
|
+
IF snapshot#>>'{h, -1, c}' != '{}' THEN
|
41
|
+
NEW.log_data := snapshot;
|
42
|
+
END IF;
|
43
|
+
|
44
|
+
ELSIF TG_OP = 'UPDATE' THEN
|
45
|
+
|
46
|
+
IF OLD.log_data is NULL OR OLD.log_data = '{}'::jsonb THEN
|
47
|
+
IF columns IS NOT NULL THEN
|
48
|
+
snapshot = logidze_snapshot(to_jsonb(NEW.*), ts_column, columns, include_columns);
|
49
|
+
ELSE
|
50
|
+
snapshot = logidze_snapshot(to_jsonb(NEW.*), ts_column);
|
51
|
+
END IF;
|
52
|
+
|
53
|
+
IF snapshot#>>'{h, -1, c}' != '{}' THEN
|
54
|
+
NEW.log_data := snapshot;
|
55
|
+
END IF;
|
56
|
+
RETURN NEW;
|
57
|
+
END IF;
|
58
|
+
|
59
|
+
history_limit := NULLIF(TG_ARGV[0], 'null');
|
60
|
+
debounce_time := NULLIF(TG_ARGV[4], 'null');
|
61
|
+
|
62
|
+
current_version := (NEW.log_data->>'v')::int;
|
63
|
+
|
64
|
+
IF ts_column IS NULL THEN
|
65
|
+
ts := statement_timestamp();
|
66
|
+
ELSE
|
67
|
+
ts := (to_jsonb(NEW.*)->>ts_column)::timestamp with time zone;
|
68
|
+
IF ts IS NULL OR ts = (to_jsonb(OLD.*)->>ts_column)::timestamp with time zone THEN
|
69
|
+
ts := statement_timestamp();
|
70
|
+
END IF;
|
71
|
+
END IF;
|
72
|
+
|
73
|
+
IF NEW = OLD THEN
|
74
|
+
RETURN NEW;
|
75
|
+
END IF;
|
76
|
+
|
77
|
+
IF current_version < (NEW.log_data#>>'{h,-1,v}')::int THEN
|
78
|
+
iterator := 0;
|
79
|
+
FOR item in SELECT * FROM jsonb_array_elements(NEW.log_data->'h')
|
80
|
+
LOOP
|
81
|
+
IF (item.value->>'v')::int > current_version THEN
|
82
|
+
NEW.log_data := jsonb_set(
|
83
|
+
NEW.log_data,
|
84
|
+
'{h}',
|
85
|
+
(NEW.log_data->'h') - iterator
|
86
|
+
);
|
87
|
+
END IF;
|
88
|
+
iterator := iterator + 1;
|
89
|
+
END LOOP;
|
90
|
+
END IF;
|
91
|
+
|
92
|
+
changes := '{}';
|
93
|
+
|
94
|
+
IF (coalesce(current_setting('logidze.full_snapshot', true), '') = 'on') THEN
|
95
|
+
BEGIN
|
96
|
+
changes = hstore_to_jsonb_loose(hstore(NEW.*));
|
97
|
+
EXCEPTION
|
98
|
+
WHEN NUMERIC_VALUE_OUT_OF_RANGE THEN
|
99
|
+
changes = row_to_json(NEW.*)::jsonb;
|
100
|
+
FOR k IN (SELECT key FROM jsonb_each(changes))
|
101
|
+
LOOP
|
102
|
+
IF jsonb_typeof(changes->k) = 'object' THEN
|
103
|
+
changes = jsonb_set(changes, ARRAY[k], to_jsonb(changes->>k));
|
104
|
+
END IF;
|
105
|
+
END LOOP;
|
106
|
+
END;
|
107
|
+
ELSE
|
108
|
+
BEGIN
|
109
|
+
changes = hstore_to_jsonb_loose(
|
110
|
+
hstore(NEW.*) - hstore(OLD.*)
|
111
|
+
);
|
112
|
+
EXCEPTION
|
113
|
+
WHEN NUMERIC_VALUE_OUT_OF_RANGE THEN
|
114
|
+
changes = (SELECT
|
115
|
+
COALESCE(json_object_agg(key, value), '{}')::jsonb
|
116
|
+
FROM
|
117
|
+
jsonb_each(row_to_json(NEW.*)::jsonb)
|
118
|
+
WHERE NOT jsonb_build_object(key, value) <@ row_to_json(OLD.*)::jsonb);
|
119
|
+
FOR k IN (SELECT key FROM jsonb_each(changes))
|
120
|
+
LOOP
|
121
|
+
IF jsonb_typeof(changes->k) = 'object' THEN
|
122
|
+
changes = jsonb_set(changes, ARRAY[k], to_jsonb(changes->>k));
|
123
|
+
END IF;
|
124
|
+
END LOOP;
|
125
|
+
END;
|
126
|
+
END IF;
|
127
|
+
|
128
|
+
changes = changes - 'log_data';
|
129
|
+
|
130
|
+
IF columns IS NOT NULL THEN
|
131
|
+
changes = logidze_filter_keys(changes, columns, include_columns);
|
132
|
+
END IF;
|
133
|
+
|
134
|
+
IF changes = '{}' THEN
|
135
|
+
RETURN NEW;
|
136
|
+
END IF;
|
137
|
+
|
138
|
+
new_v := (NEW.log_data#>>'{h,-1,v}')::int + 1;
|
139
|
+
|
140
|
+
size := jsonb_array_length(NEW.log_data->'h');
|
141
|
+
version := logidze_version(new_v, changes, ts);
|
142
|
+
|
143
|
+
IF (
|
144
|
+
debounce_time IS NOT NULL AND
|
145
|
+
(version->>'ts')::bigint - (NEW.log_data#>'{h,-1,ts}')::text::bigint <= debounce_time
|
146
|
+
) THEN
|
147
|
+
-- merge new version with the previous one
|
148
|
+
new_v := (NEW.log_data#>>'{h,-1,v}')::int;
|
149
|
+
version := logidze_version(new_v, (NEW.log_data#>'{h,-1,c}')::jsonb || changes, ts);
|
150
|
+
-- remove the previous version from log
|
151
|
+
NEW.log_data := jsonb_set(
|
152
|
+
NEW.log_data,
|
153
|
+
'{h}',
|
154
|
+
(NEW.log_data->'h') - (size - 1)
|
155
|
+
);
|
156
|
+
END IF;
|
157
|
+
|
158
|
+
NEW.log_data := jsonb_set(
|
159
|
+
NEW.log_data,
|
160
|
+
ARRAY['h', size::text],
|
161
|
+
version,
|
162
|
+
true
|
163
|
+
);
|
164
|
+
|
165
|
+
NEW.log_data := jsonb_set(
|
166
|
+
NEW.log_data,
|
167
|
+
'{v}',
|
168
|
+
to_jsonb(new_v)
|
169
|
+
);
|
170
|
+
|
171
|
+
IF history_limit IS NOT NULL AND history_limit <= size THEN
|
172
|
+
NEW.log_data := logidze_compact_history(NEW.log_data, size - history_limit + 1);
|
173
|
+
END IF;
|
174
|
+
END IF;
|
175
|
+
|
176
|
+
return NEW;
|
177
|
+
EXCEPTION
|
178
|
+
WHEN OTHERS THEN
|
179
|
+
GET STACKED DIAGNOSTICS err_sqlstate = RETURNED_SQLSTATE,
|
180
|
+
err_message = MESSAGE_TEXT,
|
181
|
+
err_detail = PG_EXCEPTION_DETAIL,
|
182
|
+
err_hint = PG_EXCEPTION_HINT,
|
183
|
+
err_context = PG_EXCEPTION_CONTEXT,
|
184
|
+
err_schema_name = SCHEMA_NAME,
|
185
|
+
err_table_name = TABLE_NAME;
|
186
|
+
err_jsonb := jsonb_build_object(
|
187
|
+
'returned_sqlstate', err_sqlstate,
|
188
|
+
'message_text', err_message,
|
189
|
+
'pg_exception_detail', err_detail,
|
190
|
+
'pg_exception_hint', err_hint,
|
191
|
+
'pg_exception_context', err_context,
|
192
|
+
'schema_name', err_schema_name,
|
193
|
+
'table_name', err_table_name
|
194
|
+
);
|
195
|
+
err_captured = logidze_capture_exception(err_jsonb);
|
196
|
+
IF err_captured THEN
|
197
|
+
return NEW;
|
198
|
+
ELSE
|
199
|
+
RAISE;
|
200
|
+
END IF;
|
201
|
+
END;
|
202
|
+
$body$
|
203
|
+
LANGUAGE plpgsql;
|
@@ -0,0 +1,33 @@
|
|
1
|
+
CREATE OR REPLACE FUNCTION logidze_snapshot(item jsonb, ts_column text DEFAULT NULL, columns text[] DEFAULT NULL, include_columns boolean DEFAULT false) RETURNS jsonb AS $body$
|
2
|
+
-- version: 3
|
3
|
+
DECLARE
|
4
|
+
ts timestamp with time zone;
|
5
|
+
k text;
|
6
|
+
BEGIN
|
7
|
+
item = item - 'log_data';
|
8
|
+
IF ts_column IS NULL THEN
|
9
|
+
ts := statement_timestamp();
|
10
|
+
ELSE
|
11
|
+
ts := coalesce((item->>ts_column)::timestamp with time zone, statement_timestamp());
|
12
|
+
END IF;
|
13
|
+
|
14
|
+
IF columns IS NOT NULL THEN
|
15
|
+
item := logidze_filter_keys(item, columns, include_columns);
|
16
|
+
END IF;
|
17
|
+
|
18
|
+
FOR k IN (SELECT key FROM jsonb_each(item))
|
19
|
+
LOOP
|
20
|
+
IF jsonb_typeof(item->k) = 'object' THEN
|
21
|
+
item := jsonb_set(item, ARRAY[k], to_jsonb(item->>k));
|
22
|
+
END IF;
|
23
|
+
END LOOP;
|
24
|
+
|
25
|
+
return json_build_object(
|
26
|
+
'v', 1,
|
27
|
+
'h', jsonb_build_array(
|
28
|
+
logidze_version(1, item, ts)
|
29
|
+
)
|
30
|
+
);
|
31
|
+
END;
|
32
|
+
$body$
|
33
|
+
LANGUAGE plpgsql;
|
@@ -0,0 +1,21 @@
|
|
1
|
+
CREATE OR REPLACE FUNCTION logidze_version(v bigint, data jsonb, ts timestamp with time zone) RETURNS jsonb AS $body$
|
2
|
+
-- version: 2
|
3
|
+
DECLARE
|
4
|
+
buf jsonb;
|
5
|
+
BEGIN
|
6
|
+
data = data - 'log_data';
|
7
|
+
buf := jsonb_build_object(
|
8
|
+
'ts',
|
9
|
+
(extract(epoch from ts) * 1000)::bigint,
|
10
|
+
'v',
|
11
|
+
v,
|
12
|
+
'c',
|
13
|
+
data
|
14
|
+
);
|
15
|
+
IF coalesce(current_setting('logidze.meta', true), '') <> '' THEN
|
16
|
+
buf := jsonb_insert(buf, '{m}', current_setting('logidze.meta')::jsonb);
|
17
|
+
END IF;
|
18
|
+
RETURN buf;
|
19
|
+
END;
|
20
|
+
$body$
|
21
|
+
LANGUAGE plpgsql;
|
@@ -2,19 +2,28 @@
|
|
2
2
|
|
3
3
|
require "rails/generators"
|
4
4
|
require "rails/generators/active_record"
|
5
|
+
require "logidze/utils/function_definitions"
|
6
|
+
require_relative "../inject_sql"
|
7
|
+
require_relative "../fx_helper"
|
8
|
+
|
9
|
+
using RubyNext
|
5
10
|
|
6
11
|
module Logidze
|
7
12
|
module Generators
|
8
13
|
class InstallGenerator < ::Rails::Generators::Base # :nodoc:
|
9
14
|
include Rails::Generators::Migration
|
15
|
+
include InjectSql
|
16
|
+
include FxHelper
|
10
17
|
|
11
18
|
source_root File.expand_path("templates", __dir__)
|
19
|
+
source_paths << File.expand_path("functions", __dir__)
|
12
20
|
|
13
21
|
class_option :update, type: :boolean, optional: true,
|
14
22
|
desc: "Define whether this is an update migration"
|
15
23
|
|
16
24
|
def generate_migration
|
17
|
-
migration_template "
|
25
|
+
migration_template = fx? ? "migration_fx.rb.erb" : "migration.rb.erb"
|
26
|
+
migration_template migration_template, "db/migrate/#{migration_name}.rb"
|
18
27
|
end
|
19
28
|
|
20
29
|
def generate_hstore_migration
|
@@ -23,6 +32,16 @@ module Logidze
|
|
23
32
|
migration_template "hstore.rb.erb", "db/migrate/enable_hstore.rb"
|
24
33
|
end
|
25
34
|
|
35
|
+
def generate_fx_functions
|
36
|
+
return unless fx?
|
37
|
+
|
38
|
+
function_definitions.each do |fdef|
|
39
|
+
next if fdef.version == previous_version_for(fdef.name)
|
40
|
+
|
41
|
+
template "#{fdef.name}.sql", "db/functions/#{fdef.name}_v#{fdef.version.to_s.rjust(2, "0")}.sql"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
26
45
|
no_tasks do
|
27
46
|
def migration_name
|
28
47
|
if update?
|
@@ -39,6 +58,29 @@ module Logidze
|
|
39
58
|
def update?
|
40
59
|
options[:update]
|
41
60
|
end
|
61
|
+
|
62
|
+
def previous_version_for(name)
|
63
|
+
all_functions.filter_map { |path| Regexp.last_match[1].to_i if path =~ %r{#{name}_v(\d+).sql} }.max
|
64
|
+
end
|
65
|
+
|
66
|
+
def all_functions
|
67
|
+
@all_functions ||=
|
68
|
+
begin
|
69
|
+
res = nil
|
70
|
+
in_root do
|
71
|
+
res = if File.directory?("db/functions")
|
72
|
+
Dir.entries("db/functions")
|
73
|
+
else
|
74
|
+
[]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
res
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def function_definitions
|
82
|
+
@function_definitions ||= Logidze::Utils::FunctionDefinitions.from_fs
|
83
|
+
end
|
42
84
|
end
|
43
85
|
|
44
86
|
def self.next_migration_number(dir)
|