pgslice 0.4.2 → 0.4.3
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 +5 -0
- data/README.md +14 -0
- data/exe/pgslice +2 -4
- data/lib/pgslice.rb +8 -742
- data/lib/pgslice/client.rb +577 -0
- data/lib/pgslice/generic_table.rb +89 -0
- data/lib/pgslice/table.rb +72 -0
- data/lib/pgslice/version.rb +1 -1
- metadata +10 -7
@@ -0,0 +1,89 @@
|
|
1
|
+
module PgSlice
|
2
|
+
class GenericTable
|
3
|
+
attr_reader :table
|
4
|
+
|
5
|
+
def initialize(table)
|
6
|
+
@table = table.to_s
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
table
|
11
|
+
end
|
12
|
+
|
13
|
+
def exists?
|
14
|
+
existing_tables(like: table).any?
|
15
|
+
end
|
16
|
+
|
17
|
+
def columns
|
18
|
+
execute("SELECT column_name FROM information_schema.columns WHERE table_schema || '.' || table_name = $1", [table]).map{ |r| r["column_name"] }
|
19
|
+
end
|
20
|
+
|
21
|
+
# http://www.dbforums.com/showthread.php?1667561-How-to-list-sequences-and-the-columns-by-SQL
|
22
|
+
def sequences
|
23
|
+
query = <<-SQL
|
24
|
+
SELECT
|
25
|
+
a.attname as related_column,
|
26
|
+
s.relname as sequence_name
|
27
|
+
FROM pg_class s
|
28
|
+
JOIN pg_depend d ON d.objid = s.oid
|
29
|
+
JOIN pg_class t ON d.objid = s.oid AND d.refobjid = t.oid
|
30
|
+
JOIN pg_attribute a ON (d.refobjid, d.refobjsubid) = (a.attrelid, a.attnum)
|
31
|
+
JOIN pg_namespace n ON n.oid = s.relnamespace
|
32
|
+
WHERE s.relkind = 'S'
|
33
|
+
AND n.nspname = $1
|
34
|
+
AND t.relname = $2
|
35
|
+
SQL
|
36
|
+
execute(query, table.split(".", 2))
|
37
|
+
end
|
38
|
+
|
39
|
+
def foreign_keys
|
40
|
+
execute("SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = #{regclass(table)} AND contype ='f'").map { |r| r["pg_get_constraintdef"] }
|
41
|
+
end
|
42
|
+
|
43
|
+
# http://stackoverflow.com/a/20537829
|
44
|
+
def primary_key
|
45
|
+
query = <<-SQL
|
46
|
+
SELECT
|
47
|
+
pg_attribute.attname,
|
48
|
+
format_type(pg_attribute.atttypid, pg_attribute.atttypmod)
|
49
|
+
FROM
|
50
|
+
pg_index, pg_class, pg_attribute, pg_namespace
|
51
|
+
WHERE
|
52
|
+
nspname || '.' || relname = $1 AND
|
53
|
+
indrelid = pg_class.oid AND
|
54
|
+
pg_class.relnamespace = pg_namespace.oid AND
|
55
|
+
pg_attribute.attrelid = pg_class.oid AND
|
56
|
+
pg_attribute.attnum = any(pg_index.indkey) AND
|
57
|
+
indisprimary
|
58
|
+
SQL
|
59
|
+
execute(query, [table]).map { |r| r["attname"] }
|
60
|
+
end
|
61
|
+
|
62
|
+
def index_defs
|
63
|
+
execute("SELECT pg_get_indexdef(indexrelid) FROM pg_index WHERE indrelid = #{regclass(table)} AND indisprimary = 'f'").map { |r| r["pg_get_indexdef"] }
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
def existing_tables(like:)
|
69
|
+
query = "SELECT schemaname, tablename FROM pg_catalog.pg_tables WHERE schemaname = $1 AND tablename LIKE $2"
|
70
|
+
execute(query, like.split(".", 2)).map { |r| "#{r["schemaname"]}.#{r["tablename"]}" }.sort
|
71
|
+
end
|
72
|
+
|
73
|
+
def execute(*args)
|
74
|
+
$client.send(:execute, *args)
|
75
|
+
end
|
76
|
+
|
77
|
+
def quote_ident(value)
|
78
|
+
PG::Connection.quote_ident(value)
|
79
|
+
end
|
80
|
+
|
81
|
+
def quote_table(table)
|
82
|
+
table.to_s.split(".", 2).map { |v| quote_ident(v) }.join(".")
|
83
|
+
end
|
84
|
+
|
85
|
+
def regclass(table)
|
86
|
+
"'#{quote_table(table)}'::regclass"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module PgSlice
|
2
|
+
class Table < GenericTable
|
3
|
+
def intermediate_table
|
4
|
+
self.class.new("#{table}_intermediate")
|
5
|
+
end
|
6
|
+
|
7
|
+
def retired_table
|
8
|
+
self.class.new("#{table}_retired")
|
9
|
+
end
|
10
|
+
|
11
|
+
def trigger_name
|
12
|
+
"#{table.split(".")[-1]}_insert_trigger"
|
13
|
+
end
|
14
|
+
|
15
|
+
def column_cast(column)
|
16
|
+
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"]
|
17
|
+
data_type == "timestamp with time zone" ? "timestamptz" : "date"
|
18
|
+
end
|
19
|
+
|
20
|
+
def max_id(primary_key, below: nil, where: nil)
|
21
|
+
query = "SELECT MAX(#{quote_ident(primary_key)}) FROM #{quote_table(table)}"
|
22
|
+
conditions = []
|
23
|
+
conditions << "#{quote_ident(primary_key)} <= #{below}" if below
|
24
|
+
conditions << where if where
|
25
|
+
query << " WHERE #{conditions.join(" AND ")}" if conditions.any?
|
26
|
+
execute(query)[0]["max"].to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
def min_id(primary_key, column, cast, starting_time, where)
|
30
|
+
query = "SELECT MIN(#{quote_ident(primary_key)}) FROM #{quote_table(table)}"
|
31
|
+
conditions = []
|
32
|
+
conditions << "#{quote_ident(column)} >= #{sql_date(starting_time, cast)}" if starting_time
|
33
|
+
conditions << where if where
|
34
|
+
query << " WHERE #{conditions.join(" AND ")}" if conditions.any?
|
35
|
+
(execute(query)[0]["min"] || 1).to_i
|
36
|
+
end
|
37
|
+
|
38
|
+
def existing_partitions(period = nil)
|
39
|
+
count =
|
40
|
+
case period
|
41
|
+
when "day"
|
42
|
+
8
|
43
|
+
when "month"
|
44
|
+
6
|
45
|
+
else
|
46
|
+
"6,8"
|
47
|
+
end
|
48
|
+
|
49
|
+
existing_tables(like: "#{table}_%").select { |t| /\A#{Regexp.escape("#{table}_")}\d{#{count}}\z/.match(t) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def fetch_comment
|
53
|
+
execute("SELECT obj_description(#{regclass(table)}) AS comment")[0]
|
54
|
+
end
|
55
|
+
|
56
|
+
def fetch_trigger(trigger_name)
|
57
|
+
execute("SELECT obj_description(oid, 'pg_trigger') AS comment FROM pg_trigger WHERE tgname = $1 AND tgrelid = #{regclass(table)}", [trigger_name])[0]
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
def sql_date(time, cast, add_cast = true)
|
63
|
+
if cast == "timestamptz"
|
64
|
+
fmt = "%Y-%m-%d %H:%M:%S UTC"
|
65
|
+
else
|
66
|
+
fmt = "%Y-%m-%d"
|
67
|
+
end
|
68
|
+
str = "'#{time.strftime(fmt)}'"
|
69
|
+
add_cast ? "#{str}::#{cast}" : str
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/pgslice/version.rb
CHANGED
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
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.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: thor
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pg
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 0.18.2
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 0.18.2
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -92,6 +92,9 @@ files:
|
|
92
92
|
- README.md
|
93
93
|
- exe/pgslice
|
94
94
|
- lib/pgslice.rb
|
95
|
+
- lib/pgslice/client.rb
|
96
|
+
- lib/pgslice/generic_table.rb
|
97
|
+
- lib/pgslice/table.rb
|
95
98
|
- lib/pgslice/version.rb
|
96
99
|
homepage: https://github.com/ankane/pgslice
|
97
100
|
licenses:
|