pgslice 0.4.0 → 0.4.1
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 +5 -5
- data/CHANGELOG.md +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -1
- data/lib/pgslice.rb +76 -51
- data/lib/pgslice/version.rb +1 -1
- data/pgslice.gemspec +1 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '00404860fae8f92d17c1be36fb44f6b8fea7413644bf3f184a2431589c750abc'
|
4
|
+
data.tar.gz: db1520df2c4bd9097927cf73900785c42bebeb67f747a12a857c48fdc6ca64a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: accd820d05e151745c26cf7138394ded42f0b50f3272f051b8a154cf6dbc51271a8f810f41602138ed003bfba72a3279129638d7eb312cff4a967d6ef05bfe2e
|
7
|
+
data.tar.gz: 1b52fc649ff7f7a6832ad69236c3de3343b00cf762bfe9d935dc2be96552dfcee69e55d2ece4926e1d3b0d8a9123c970db31f706250c3d19501796c0a9a38874
|
data/CHANGELOG.md
CHANGED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016-2017 Andrew Kane
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
CHANGED
@@ -92,6 +92,18 @@ pgslice prep visits created_at month
|
|
92
92
|
```
|
93
93
|
|
94
94
|
```sql
|
95
|
+
-- Postgres 10
|
96
|
+
|
97
|
+
BEGIN;
|
98
|
+
|
99
|
+
CREATE TABLE visits_intermediate (LIKE visits INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING STORAGE INCLUDING COMMENTS) PARTITION BY RANGE (created_at);
|
100
|
+
|
101
|
+
COMMENT ON TABLE visits_intermediate is 'column:created_at,period:day';
|
102
|
+
|
103
|
+
COMMIT;
|
104
|
+
|
105
|
+
-- Postgres 9.6 and below
|
106
|
+
|
95
107
|
BEGIN;
|
96
108
|
|
97
109
|
CREATE TABLE visits_intermediate (LIKE visits INCLUDING ALL);
|
@@ -117,6 +129,32 @@ pgslice add_partitions visits --intermediate --past 1 --future 1
|
|
117
129
|
```
|
118
130
|
|
119
131
|
```sql
|
132
|
+
-- Postgres 10
|
133
|
+
|
134
|
+
BEGIN;
|
135
|
+
|
136
|
+
CREATE TABLE visits_201608 PARTITION OF visits_intermediate FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
|
137
|
+
|
138
|
+
ALTER TABLE visits_201608 ADD PRIMARY KEY (id);
|
139
|
+
|
140
|
+
CREATE INDEX ON visits_201608 USING btree (user_id);
|
141
|
+
|
142
|
+
CREATE TABLE visits_201609 PARTITION OF visits_intermediate FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
|
143
|
+
|
144
|
+
ALTER TABLE visits_201609 ADD PRIMARY KEY (id);
|
145
|
+
|
146
|
+
CREATE INDEX ON visits_201609 USING btree (user_id);
|
147
|
+
|
148
|
+
CREATE TABLE visits_201610 PARTITION OF visits_intermediate FOR VALUES FROM ('2016-10-01') TO ('2016-11-01');
|
149
|
+
|
150
|
+
ALTER TABLE visits_201610 ADD PRIMARY KEY (id);
|
151
|
+
|
152
|
+
CREATE INDEX ON visits_201610 USING btree (user_id);
|
153
|
+
|
154
|
+
COMMIT;
|
155
|
+
|
156
|
+
-- Postgres 9.6 and below
|
157
|
+
|
120
158
|
BEGIN;
|
121
159
|
|
122
160
|
CREATE TABLE visits_201608
|
@@ -339,7 +377,7 @@ To use master, run:
|
|
339
377
|
|
340
378
|
```sh
|
341
379
|
gem install specific_install
|
342
|
-
gem specific_install ankane/pgslice
|
380
|
+
gem specific_install https://github.com/ankane/pgslice.git
|
343
381
|
```
|
344
382
|
|
345
383
|
## Docker
|
data/lib/pgslice.rb
CHANGED
@@ -54,7 +54,9 @@ module PgSlice
|
|
54
54
|
|
55
55
|
def prep
|
56
56
|
table, column, period = arguments
|
57
|
+
table = qualify_table(table)
|
57
58
|
intermediate_table = "#{table}_intermediate"
|
59
|
+
|
58
60
|
trigger_name = self.trigger_name(table)
|
59
61
|
|
60
62
|
if options[:no_partition]
|
@@ -77,21 +79,21 @@ module PgSlice
|
|
77
79
|
|
78
80
|
if declarative && !options[:no_partition]
|
79
81
|
queries << <<-SQL
|
80
|
-
CREATE TABLE #{
|
82
|
+
CREATE TABLE #{quote_table(intermediate_table)} (LIKE #{quote_table(table)} INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING STORAGE INCLUDING COMMENTS) PARTITION BY RANGE (#{quote_table(column)});
|
81
83
|
SQL
|
82
84
|
|
83
85
|
# add comment
|
84
86
|
cast = column_cast(table, column)
|
85
87
|
queries << <<-SQL
|
86
|
-
COMMENT ON TABLE #{
|
88
|
+
COMMENT ON TABLE #{quote_table(intermediate_table)} is 'column:#{column},period:#{period},cast:#{cast}';
|
87
89
|
SQL
|
88
90
|
else
|
89
91
|
queries << <<-SQL
|
90
|
-
CREATE TABLE #{
|
92
|
+
CREATE TABLE #{quote_table(intermediate_table)} (LIKE #{quote_table(table)} INCLUDING ALL);
|
91
93
|
SQL
|
92
94
|
|
93
95
|
foreign_keys(table).each do |fk_def|
|
94
|
-
queries << "ALTER TABLE #{
|
96
|
+
queries << "ALTER TABLE #{quote_table(intermediate_table)} ADD #{fk_def};"
|
95
97
|
end
|
96
98
|
end
|
97
99
|
|
@@ -108,13 +110,13 @@ CREATE FUNCTION #{quote_ident(trigger_name)}()
|
|
108
110
|
|
109
111
|
queries << <<-SQL
|
110
112
|
CREATE TRIGGER #{quote_ident(trigger_name)}
|
111
|
-
BEFORE INSERT ON #{
|
113
|
+
BEFORE INSERT ON #{quote_table(intermediate_table)}
|
112
114
|
FOR EACH ROW EXECUTE PROCEDURE #{quote_ident(trigger_name)}();
|
113
115
|
SQL
|
114
116
|
|
115
117
|
cast = column_cast(table, column)
|
116
118
|
queries << <<-SQL
|
117
|
-
COMMENT ON TRIGGER #{quote_ident(trigger_name)} ON #{
|
119
|
+
COMMENT ON TRIGGER #{quote_ident(trigger_name)} ON #{quote_table(intermediate_table)} is 'column:#{column},period:#{period},cast:#{cast}';
|
118
120
|
SQL
|
119
121
|
end
|
120
122
|
|
@@ -122,7 +124,7 @@ COMMENT ON TRIGGER #{quote_ident(trigger_name)} ON #{quote_ident(intermediate_ta
|
|
122
124
|
end
|
123
125
|
|
124
126
|
def unprep
|
125
|
-
table = arguments.first
|
127
|
+
table = qualify_table(arguments.first)
|
126
128
|
intermediate_table = "#{table}_intermediate"
|
127
129
|
trigger_name = self.trigger_name(table)
|
128
130
|
|
@@ -130,14 +132,14 @@ COMMENT ON TRIGGER #{quote_ident(trigger_name)} ON #{quote_ident(intermediate_ta
|
|
130
132
|
abort "Table not found: #{intermediate_table}" unless table_exists?(intermediate_table)
|
131
133
|
|
132
134
|
queries = [
|
133
|
-
"DROP TABLE #{
|
135
|
+
"DROP TABLE #{quote_table(intermediate_table)} CASCADE;",
|
134
136
|
"DROP FUNCTION IF EXISTS #{quote_ident(trigger_name)}();"
|
135
137
|
]
|
136
138
|
run_queries(queries)
|
137
139
|
end
|
138
140
|
|
139
141
|
def add_partitions
|
140
|
-
original_table = arguments.first
|
142
|
+
original_table = qualify_table(arguments.first)
|
141
143
|
table = options[:intermediate] ? "#{original_table}_intermediate" : original_table
|
142
144
|
trigger_name = self.trigger_name(original_table)
|
143
145
|
|
@@ -158,7 +160,7 @@ COMMENT ON TRIGGER #{quote_ident(trigger_name)} ON #{quote_ident(intermediate_ta
|
|
158
160
|
queries = []
|
159
161
|
|
160
162
|
if needs_comment
|
161
|
-
queries << "COMMENT ON TRIGGER #{quote_ident(trigger_name)} ON #{
|
163
|
+
queries << "COMMENT ON TRIGGER #{quote_ident(trigger_name)} ON #{quote_table(table)} is 'column:#{field},period:#{period},cast:#{cast}';"
|
162
164
|
end
|
163
165
|
|
164
166
|
# today = utc date
|
@@ -170,9 +172,10 @@ COMMENT ON TRIGGER #{quote_ident(trigger_name)} ON #{quote_ident(intermediate_ta
|
|
170
172
|
elsif options[:intermediate]
|
171
173
|
original_table
|
172
174
|
else
|
173
|
-
|
175
|
+
existing_partitions(original_table, period).last
|
174
176
|
end
|
175
|
-
|
177
|
+
|
178
|
+
index_defs = execute("SELECT pg_get_indexdef(indexrelid) FROM pg_index WHERE indrelid = #{regclass(schema_table)} AND indisprimary = 'f'").map { |r| r["pg_get_indexdef"] }
|
176
179
|
fk_defs = foreign_keys(schema_table)
|
177
180
|
primary_key = self.primary_key(schema_table)
|
178
181
|
|
@@ -186,24 +189,24 @@ COMMENT ON TRIGGER #{quote_ident(trigger_name)} ON #{quote_ident(intermediate_ta
|
|
186
189
|
|
187
190
|
if declarative
|
188
191
|
queries << <<-SQL
|
189
|
-
CREATE TABLE #{
|
192
|
+
CREATE TABLE #{quote_table(partition_name)} PARTITION OF #{quote_table(table)} FOR VALUES FROM (#{sql_date(day, cast, false)}) TO (#{sql_date(advance_date(day, period, 1), cast, false)});
|
190
193
|
SQL
|
191
194
|
else
|
192
195
|
queries << <<-SQL
|
193
|
-
CREATE TABLE #{
|
196
|
+
CREATE TABLE #{quote_table(partition_name)}
|
194
197
|
(CHECK (#{quote_ident(field)} >= #{sql_date(day, cast)} AND #{quote_ident(field)} < #{sql_date(advance_date(day, period, 1), cast)}))
|
195
|
-
INHERITS (#{
|
198
|
+
INHERITS (#{quote_table(table)});
|
196
199
|
SQL
|
197
200
|
end
|
198
201
|
|
199
|
-
queries << "ALTER TABLE #{
|
202
|
+
queries << "ALTER TABLE #{quote_table(partition_name)} ADD PRIMARY KEY (#{primary_key.map { |k| quote_ident(k) }.join(", ")});" if primary_key.any?
|
200
203
|
|
201
204
|
index_defs.each do |index_def|
|
202
|
-
queries << index_def.sub(/ ON \S+ USING /, " ON #{
|
205
|
+
queries << index_def.sub(/ ON \S+ USING /, " ON #{quote_table(partition_name)} USING ").sub(/ INDEX .+ ON /, " INDEX ON ") + ";"
|
203
206
|
end
|
204
207
|
|
205
208
|
fk_defs.each do |fk_def|
|
206
|
-
queries << "ALTER TABLE #{
|
209
|
+
queries << "ALTER TABLE #{quote_table(partition_name)} ADD #{fk_def};"
|
207
210
|
end
|
208
211
|
end
|
209
212
|
|
@@ -213,7 +216,7 @@ CREATE TABLE #{quote_ident(partition_name)}
|
|
213
216
|
future_defs = []
|
214
217
|
past_defs = []
|
215
218
|
name_format = self.name_format(period)
|
216
|
-
existing_tables = existing_partitions(original_table)
|
219
|
+
existing_tables = existing_partitions(original_table, period)
|
217
220
|
existing_tables = (existing_tables + added_partitions).uniq.sort
|
218
221
|
|
219
222
|
existing_tables.each do |table|
|
@@ -221,7 +224,7 @@ CREATE TABLE #{quote_ident(partition_name)}
|
|
221
224
|
partition_name = "#{original_table}_#{day.strftime(name_format(period))}"
|
222
225
|
|
223
226
|
sql = "(NEW.#{quote_ident(field)} >= #{sql_date(day, cast)} AND NEW.#{quote_ident(field)} < #{sql_date(advance_date(day, period, 1), cast)}) THEN
|
224
|
-
INSERT INTO #{
|
227
|
+
INSERT INTO #{quote_table(partition_name)} VALUES (NEW.*);"
|
225
228
|
|
226
229
|
if day.to_date < today
|
227
230
|
past_defs << sql
|
@@ -255,7 +258,7 @@ CREATE OR REPLACE FUNCTION #{quote_ident(trigger_name)}()
|
|
255
258
|
end
|
256
259
|
|
257
260
|
def fill
|
258
|
-
table = arguments.first
|
261
|
+
table = qualify_table(arguments.first)
|
259
262
|
|
260
263
|
abort "Usage: pgslice fill <table>" if arguments.length != 1
|
261
264
|
|
@@ -278,7 +281,7 @@ CREATE OR REPLACE FUNCTION #{quote_ident(trigger_name)}()
|
|
278
281
|
if period
|
279
282
|
name_format = self.name_format(period)
|
280
283
|
|
281
|
-
existing_tables = existing_partitions(table)
|
284
|
+
existing_tables = existing_partitions(table, period)
|
282
285
|
if existing_tables.any?
|
283
286
|
starting_time = DateTime.strptime(existing_tables.first.split("_").last, name_format)
|
284
287
|
ending_time = advance_date(DateTime.strptime(existing_tables.last.split("_").last, name_format), period, 1)
|
@@ -286,8 +289,10 @@ CREATE OR REPLACE FUNCTION #{quote_ident(trigger_name)}()
|
|
286
289
|
end
|
287
290
|
|
288
291
|
schema_table = period && declarative ? existing_tables.last : table
|
289
|
-
|
292
|
+
|
293
|
+
primary_key = self.primary_key(schema_table)[0]
|
290
294
|
abort "No primary key" unless primary_key
|
295
|
+
|
291
296
|
max_source_id = max_id(source_table, primary_key)
|
292
297
|
|
293
298
|
max_dest_id =
|
@@ -326,8 +331,8 @@ CREATE OR REPLACE FUNCTION #{quote_ident(trigger_name)}()
|
|
326
331
|
|
327
332
|
query = <<-SQL
|
328
333
|
/* #{i} of #{batch_count} */
|
329
|
-
INSERT INTO #{
|
330
|
-
SELECT #{fields} FROM #{
|
334
|
+
INSERT INTO #{quote_table(dest_table)} (#{fields})
|
335
|
+
SELECT #{fields} FROM #{quote_table(source_table)}
|
331
336
|
WHERE #{where}
|
332
337
|
SQL
|
333
338
|
|
@@ -343,7 +348,7 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
343
348
|
end
|
344
349
|
|
345
350
|
def swap
|
346
|
-
table = arguments.first
|
351
|
+
table = qualify_table(arguments.first)
|
347
352
|
intermediate_table = intermediate_name(table)
|
348
353
|
retired_table = retired_name(table)
|
349
354
|
|
@@ -353,8 +358,8 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
353
358
|
abort "Table already exists: #{retired_table}" if table_exists?(retired_table)
|
354
359
|
|
355
360
|
queries = [
|
356
|
-
"ALTER TABLE #{
|
357
|
-
"ALTER TABLE #{
|
361
|
+
"ALTER TABLE #{quote_table(table)} RENAME TO #{quote_no_schema(retired_table)};",
|
362
|
+
"ALTER TABLE #{quote_table(intermediate_table)} RENAME TO #{quote_no_schema(table)};"
|
358
363
|
]
|
359
364
|
|
360
365
|
self.sequences(table).each do |sequence|
|
@@ -367,7 +372,7 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
367
372
|
end
|
368
373
|
|
369
374
|
def unswap
|
370
|
-
table = arguments.first
|
375
|
+
table = qualify_table(arguments.first)
|
371
376
|
intermediate_table = intermediate_name(table)
|
372
377
|
retired_table = retired_name(table)
|
373
378
|
|
@@ -377,8 +382,8 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
377
382
|
abort "Table already exists: #{intermediate_table}" if table_exists?(intermediate_table)
|
378
383
|
|
379
384
|
queries = [
|
380
|
-
"ALTER TABLE #{
|
381
|
-
"ALTER TABLE #{
|
385
|
+
"ALTER TABLE #{quote_table(table)} RENAME TO #{quote_no_schema(intermediate_table)};",
|
386
|
+
"ALTER TABLE #{quote_table(retired_table)} RENAME TO #{quote_no_schema(table)};"
|
382
387
|
]
|
383
388
|
|
384
389
|
self.sequences(table).each do |sequence|
|
@@ -389,14 +394,14 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
389
394
|
end
|
390
395
|
|
391
396
|
def analyze
|
392
|
-
table = arguments.first
|
397
|
+
table = qualify_table(arguments.first)
|
393
398
|
parent_table = options[:swapped] ? table : intermediate_name(table)
|
394
399
|
|
395
400
|
abort "Usage: pgslice analyze <table>" if arguments.length != 1
|
396
401
|
|
397
402
|
existing_tables = existing_partitions(table)
|
398
403
|
analyze_list = existing_tables + [parent_table]
|
399
|
-
run_queries_without_transaction analyze_list.map { |t| "ANALYZE VERBOSE #{
|
404
|
+
run_queries_without_transaction analyze_list.map { |t| "ANALYZE VERBOSE #{quote_table(t)};" }
|
400
405
|
end
|
401
406
|
|
402
407
|
# arguments
|
@@ -508,13 +513,23 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
508
513
|
execute("SHOW server_version_num")[0]["server_version_num"].to_i
|
509
514
|
end
|
510
515
|
|
511
|
-
def existing_partitions(table)
|
512
|
-
|
516
|
+
def existing_partitions(table, period = nil)
|
517
|
+
count =
|
518
|
+
case period
|
519
|
+
when "day"
|
520
|
+
8
|
521
|
+
when "month"
|
522
|
+
6
|
523
|
+
else
|
524
|
+
"6,8"
|
525
|
+
end
|
526
|
+
|
527
|
+
existing_tables(like: "#{table}_%").select { |t| /\A#{Regexp.escape("#{table}_")}\d{#{count}}\z/.match(t) }
|
513
528
|
end
|
514
529
|
|
515
530
|
def existing_tables(like:)
|
516
|
-
query = "SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = $1 AND tablename LIKE $2"
|
517
|
-
execute(query,
|
531
|
+
query = "SELECT schemaname, tablename FROM pg_catalog.pg_tables WHERE schemaname = $1 AND tablename LIKE $2"
|
532
|
+
execute(query, like.split(".", 2)).map { |r| "#{r["schemaname"]}.#{r["tablename"]}" }.sort
|
518
533
|
end
|
519
534
|
|
520
535
|
def table_exists?(table)
|
@@ -522,7 +537,7 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
522
537
|
end
|
523
538
|
|
524
539
|
def columns(table)
|
525
|
-
execute("SELECT column_name FROM information_schema.columns WHERE table_schema
|
540
|
+
execute("SELECT column_name FROM information_schema.columns WHERE table_schema || '.' || table_name = $1", [table]).map{ |r| r["column_name"] }
|
526
541
|
end
|
527
542
|
|
528
543
|
# http://stackoverflow.com/a/20537829
|
@@ -534,20 +549,18 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
534
549
|
FROM
|
535
550
|
pg_index, pg_class, pg_attribute, pg_namespace
|
536
551
|
WHERE
|
537
|
-
relname = $
|
552
|
+
nspname || '.' || relname = $1 AND
|
538
553
|
indrelid = pg_class.oid AND
|
539
|
-
nspname = $1 AND
|
540
554
|
pg_class.relnamespace = pg_namespace.oid AND
|
541
555
|
pg_attribute.attrelid = pg_class.oid AND
|
542
556
|
pg_attribute.attnum = any(pg_index.indkey) AND
|
543
557
|
indisprimary
|
544
558
|
SQL
|
545
|
-
|
546
|
-
row && row["attname"]
|
559
|
+
execute(query, [table]).map { |r| r["attname"] }
|
547
560
|
end
|
548
561
|
|
549
562
|
def max_id(table, primary_key, below: nil, where: nil)
|
550
|
-
query = "SELECT MAX(#{quote_ident(primary_key)}) FROM #{
|
563
|
+
query = "SELECT MAX(#{quote_ident(primary_key)}) FROM #{quote_table(table)}"
|
551
564
|
conditions = []
|
552
565
|
conditions << "#{quote_ident(primary_key)} <= #{below}" if below
|
553
566
|
conditions << where if where
|
@@ -556,7 +569,7 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
556
569
|
end
|
557
570
|
|
558
571
|
def min_id(table, primary_key, column, cast, starting_time, where)
|
559
|
-
query = "SELECT MIN(#{quote_ident(primary_key)}) FROM #{
|
572
|
+
query = "SELECT MIN(#{quote_ident(primary_key)}) FROM #{quote_table(table)}"
|
560
573
|
conditions = []
|
561
574
|
conditions << "#{quote_ident(column)} >= #{sql_date(starting_time, cast)}" if starting_time
|
562
575
|
conditions << where if where
|
@@ -569,7 +582,7 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
569
582
|
end
|
570
583
|
|
571
584
|
def fetch_comment(table)
|
572
|
-
execute("SELECT obj_description(#{regclass(
|
585
|
+
execute("SELECT obj_description(#{regclass(table)}) AS comment")[0]
|
573
586
|
end
|
574
587
|
|
575
588
|
# http://www.dbforums.com/showthread.php?1667561-How-to-list-sequences-and-the-columns-by-SQL
|
@@ -593,7 +606,7 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
593
606
|
# helpers
|
594
607
|
|
595
608
|
def trigger_name(table)
|
596
|
-
"#{table}_insert_trigger"
|
609
|
+
"#{table.split(".")[-1]}_insert_trigger"
|
597
610
|
end
|
598
611
|
|
599
612
|
def intermediate_name(table)
|
@@ -605,7 +618,7 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
605
618
|
end
|
606
619
|
|
607
620
|
def column_cast(table, column)
|
608
|
-
data_type = execute("SELECT data_type FROM information_schema.columns WHERE table_schema
|
621
|
+
data_type = execute("SELECT data_type FROM information_schema.columns WHERE table_schema || '.' || table_name = $1 AND column_name = $2", [table, column])[0]["data_type"]
|
609
622
|
data_type == "timestamp with time zone" ? "timestamptz" : "date"
|
610
623
|
end
|
611
624
|
|
@@ -652,12 +665,24 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
652
665
|
PG::Connection.quote_ident(value)
|
653
666
|
end
|
654
667
|
|
655
|
-
def
|
656
|
-
"
|
668
|
+
def quote_table(table)
|
669
|
+
table.split(".", 2).map { |v| quote_ident(v) }.join(".")
|
670
|
+
end
|
671
|
+
|
672
|
+
def quote_no_schema(table)
|
673
|
+
quote_ident(table.split(".", 2)[-1])
|
674
|
+
end
|
675
|
+
|
676
|
+
def regclass(table)
|
677
|
+
"'#{quote_table(table)}'::regclass"
|
657
678
|
end
|
658
679
|
|
659
680
|
def fetch_trigger(trigger_name, table)
|
660
|
-
execute("SELECT obj_description(oid, 'pg_trigger') AS comment FROM pg_trigger WHERE tgname = $1 AND tgrelid = #{regclass(
|
681
|
+
execute("SELECT obj_description(oid, 'pg_trigger') AS comment FROM pg_trigger WHERE tgname = $1 AND tgrelid = #{regclass(table)}", [trigger_name])[0]
|
682
|
+
end
|
683
|
+
|
684
|
+
def qualify_table(table)
|
685
|
+
table.to_s.include?(".") ? table : [schema, table].join(".")
|
661
686
|
end
|
662
687
|
|
663
688
|
def settings_from_trigger(original_table, table)
|
@@ -692,7 +717,7 @@ INSERT INTO #{quote_ident(dest_table)} (#{fields})
|
|
692
717
|
end
|
693
718
|
|
694
719
|
def foreign_keys(table)
|
695
|
-
execute("SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = #{regclass(
|
720
|
+
execute("SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = #{regclass(table)} AND contype ='f'").map { |r| r["pg_get_constraintdef"] }
|
696
721
|
end
|
697
722
|
|
698
723
|
def server_version_num
|
data/lib/pgslice/version.rb
CHANGED
data/pgslice.gemspec
CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
|
12
12
|
spec.summary = "Postgres partitioning as easy as pie"
|
13
13
|
spec.homepage = "https://github.com/ankane/pgslice"
|
14
|
+
spec.license = "MIT"
|
14
15
|
|
15
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
17
|
spec.bindir = "exe"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pgslice
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-05-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: slop
|
@@ -93,6 +93,7 @@ files:
|
|
93
93
|
- CHANGELOG.md
|
94
94
|
- Dockerfile
|
95
95
|
- Gemfile
|
96
|
+
- LICENSE.txt
|
96
97
|
- README.md
|
97
98
|
- Rakefile
|
98
99
|
- exe/pgslice
|
@@ -100,7 +101,8 @@ files:
|
|
100
101
|
- lib/pgslice/version.rb
|
101
102
|
- pgslice.gemspec
|
102
103
|
homepage: https://github.com/ankane/pgslice
|
103
|
-
licenses:
|
104
|
+
licenses:
|
105
|
+
- MIT
|
104
106
|
metadata: {}
|
105
107
|
post_install_message:
|
106
108
|
rdoc_options: []
|
@@ -118,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
120
|
version: '0'
|
119
121
|
requirements: []
|
120
122
|
rubyforge_project:
|
121
|
-
rubygems_version: 2.6
|
123
|
+
rubygems_version: 2.7.6
|
122
124
|
signing_key:
|
123
125
|
specification_version: 4
|
124
126
|
summary: Postgres partitioning as easy as pie
|