Skip to content

Commit 7e07e15

Browse files
committed
Merge branch 'master' into hash-partitioning
2 parents df4a7f6 + 694a7f2 commit 7e07e15

File tree

6 files changed

+72
-25
lines changed

6 files changed

+72
-25
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
## 0.7.1 (unreleased)
22

33
- Added experimental support for hash partitioning
4-
- Improved `analyze` and `unprep` for declarative partitioning
4+
- Fixed `analyze` analyzing partitions twice with declarative partitioning
5+
- Removed unnecessary query for `unprep` with declarative partitioning
56

67
## 0.7.0 (2025-05-26)
78

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,9 @@ You can also use pgslice to reduce the size of a table without partitioning by c
301301
```sh
302302
pgslice prep <table> --no-partition
303303
pgslice fill <table> --where "id > 1000" # use any conditions
304+
pgslice analyze <table>
304305
pgslice swap <table>
306+
pgslice fill <table> --where "id > 1000" --swapped
305307
```
306308

307309
## Hash Partitioning

lib/pgslice/cli.rb

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
module PgSlice
22
class CLI < Thor
33
class << self
4-
attr_accessor :instance
4+
attr_accessor :instance, :exit_on_failure
5+
alias_method :exit_on_failure?, :exit_on_failure
56
end
7+
self.exit_on_failure = true
68

79
include Helpers
810

@@ -13,10 +15,6 @@ class << self
1315

1416
map %w[--version -v] => :version
1517

16-
def self.exit_on_failure?
17-
ENV["PGSLICE_ENV"] != "test"
18-
end
19-
2018
def initialize(*args)
2119
PgSlice::CLI.instance = self
2220
$stdout.sync = true

lib/pgslice/table.rb

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,19 @@ def to_s
1212
end
1313

1414
def exists?
15-
execute("SELECT COUNT(*) FROM pg_catalog.pg_tables WHERE schemaname = $1 AND tablename = $2", [schema, name]).first["count"].to_i > 0
15+
query = <<~SQL
16+
SELECT COUNT(*) FROM pg_catalog.pg_tables
17+
WHERE schemaname = $1 AND tablename = $2
18+
SQL
19+
execute(query, [schema, name]).first["count"].to_i > 0
1620
end
1721

1822
def columns
19-
execute("SELECT column_name FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 AND is_generated = 'NEVER'", [schema, name]).map { |r| r["column_name"] }
23+
query = <<~SQL
24+
SELECT column_name FROM information_schema.columns
25+
WHERE table_schema = $1 AND table_name = $2 AND is_generated = 'NEVER'
26+
SQL
27+
execute(query, [schema, name]).map { |r| r["column_name"] }
2028
end
2129

2230
# http://www.dbforums.com/showthread.php?1667561-How-to-list-sequences-and-the-columns-by-SQL
@@ -41,7 +49,11 @@ def sequences
4149
end
4250

4351
def foreign_keys
44-
execute("SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = $1::regclass AND contype ='f'", [quote_table]).map { |r| r["pg_get_constraintdef"] }
52+
query = <<~SQL
53+
SELECT pg_get_constraintdef(oid) FROM pg_constraint
54+
WHERE conrelid = $1::regclass AND contype ='f'
55+
SQL
56+
execute(query, [quote_table]).map { |r| r["pg_get_constraintdef"] }
4557
end
4658

4759
# https://stackoverflow.com/a/20537829
@@ -69,7 +81,11 @@ def primary_key
6981
end
7082

7183
def index_defs
72-
execute("SELECT pg_get_indexdef(indexrelid) FROM pg_index WHERE indrelid = $1::regclass AND indisprimary = 'f'", [quote_table]).map { |r| r["pg_get_indexdef"] }
84+
query = <<~SQL
85+
SELECT pg_get_indexdef(indexrelid) FROM pg_index
86+
WHERE indrelid = $1::regclass AND indisprimary = 'f'
87+
SQL
88+
execute(query, [quote_table]).map { |r| r["pg_get_indexdef"] }
7389
end
7490

7591
def quote_table
@@ -89,7 +105,11 @@ def trigger_name
89105
end
90106

91107
def column_cast(column)
92-
data_type = execute("SELECT data_type FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 AND column_name = $3", [schema, name, column])[0]["data_type"]
108+
query = <<~SQL
109+
SELECT data_type FROM information_schema.columns
110+
WHERE table_schema = $1 AND table_name = $2 AND column_name = $3
111+
SQL
112+
data_type = execute(query, [schema, name, column])[0]["data_type"]
93113
data_type == "timestamp with time zone" ? "timestamptz" : "date"
94114
end
95115

@@ -139,7 +159,11 @@ def fetch_comment
139159
end
140160

141161
def fetch_trigger(trigger_name)
142-
execute("SELECT obj_description(oid, 'pg_trigger') AS comment FROM pg_trigger WHERE tgname = $1 AND tgrelid = $2::regclass", [trigger_name, quote_table])[0]
162+
query = <<~SQL
163+
SELECT obj_description(oid, 'pg_trigger') AS comment FROM pg_trigger
164+
WHERE tgname = $1 AND tgrelid = $2::regclass
165+
SQL
166+
execute(query, [trigger_name, quote_table])[0]
143167
end
144168

145169
# legacy

test/pgslice_test.rb

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,18 @@ def test_no_partition
3333
run_command "fill Posts"
3434
assert_equal 10000, count("Posts_intermediate")
3535

36+
assert_analyzed "Posts_intermediate" do
37+
run_command "analyze Posts"
38+
end
39+
3640
run_command "swap Posts"
3741
assert !table_exists?("Posts_intermediate")
3842
assert table_exists?("Posts_retired")
3943

44+
assert_analyzed "Posts" do
45+
run_command "analyze Posts --swapped"
46+
end
47+
4048
run_command "unswap Posts"
4149
assert table_exists?("Posts_intermediate")
4250
assert !table_exists?("Posts_retired")
@@ -158,11 +166,8 @@ def assert_period(period, column: "createdAt", trigger_based: false, tablespace:
158166
# insert into old table
159167
execute %!INSERT INTO "Posts" (#{quote_ident(column)}) VALUES ($1) RETURNING "Id"!, [now.iso8601]
160168

161-
run_command "analyze Posts"
162-
# https://github.com/postgres/postgres/commit/375aed36ad83f0e021e9bdd3a0034c0c992c66dc
163-
if server_version_num >= 150000
164-
last_analyzed = execute("SELECT relname, last_analyze FROM pg_stat_user_tables WHERE relname LIKE 'Posts_%'")
165-
assert_equal 4, last_analyzed.count { |v| v["last_analyze"] }
169+
assert_analyzed "Posts%", 4 do
170+
run_command "analyze Posts"
166171
end
167172

168173
# TODO check sequence ownership
@@ -219,7 +224,9 @@ def assert_period(period, column: "createdAt", trigger_based: false, tablespace:
219224
assert_column partition_name, "updatedAt"
220225
assert_column new_partition_name, "updatedAt"
221226

222-
run_command "analyze Posts --swapped"
227+
assert_analyzed "Posts%", 6 do
228+
run_command "analyze Posts --swapped"
229+
end
223230

224231
# pg_stats_ext view available with Postgres 12+
225232
assert_statistics "Posts" if !trigger_based
@@ -275,7 +282,7 @@ def run_command(command, error: nil)
275282
puts
276283
end
277284
stdout, stderr = capture_io do
278-
PgSlice::CLI.start("#{command} --url #{$url}".split(" "))
285+
PgSlice::CLI.start("#{command} --url #{url}".split(" "))
279286
end
280287
if verbose?
281288
puts stdout
@@ -360,6 +367,16 @@ def assert_foreign_key(table_name)
360367
assert !result.detect { |row| row["def"] =~ /\AFOREIGN KEY \(.*\) REFERENCES "Users"\("Id"\)\z/ }.nil?, "Missing foreign key on #{table_name}"
361368
end
362369

370+
def assert_analyzed(table_pattern, expected = 1)
371+
execute("SELECT pg_stat_reset()")
372+
yield
373+
last_analyzed = execute("SELECT relname, last_analyze FROM pg_stat_user_tables WHERE relname LIKE $1", [table_pattern])
374+
# https://github.com/postgres/postgres/commit/375aed36ad83f0e021e9bdd3a0034c0c992c66dc
375+
if server_version_num >= 150000
376+
assert_equal expected, last_analyzed.count { |v| v["last_analyze"] }
377+
end
378+
end
379+
363380
# extended statistics are built on partitioned tables
364381
# https://github.com/postgres/postgres/commit/20b9fa308ebf7d4a26ac53804fce1c30f781d60c
365382
# (backported to Postgres 10)
@@ -378,11 +395,19 @@ def server_version_num
378395
execute("SHOW server_version_num").first["server_version_num"].to_i
379396
end
380397

398+
def url
399+
@url ||= ENV["PGSLICE_URL"] || "postgres:///pgslice_test"
400+
end
401+
402+
def connection
403+
@connection ||= PG::Connection.new(url)
404+
end
405+
381406
def execute(query, params = [])
382407
if params.any?
383-
$conn.exec_params(query, params)
408+
connection.exec_params(query, params)
384409
else
385-
$conn.exec(query)
410+
connection.exec(query)
386411
end
387412
end
388413

test/test_helper.rb

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,4 @@
33
require "minitest/autorun"
44
require "minitest/pride"
55

6-
ENV["PGSLICE_ENV"] = "test"
7-
8-
$url = ENV["PGSLICE_URL"] || "postgres:///pgslice_test"
9-
$conn = PG::Connection.new($url)
6+
PgSlice::CLI.exit_on_failure = false

0 commit comments

Comments
 (0)