pgslice 0.6.1 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: df319dee9da69596907cb6c50714a3c49bcfd86eae5a230f2e26c460266c5b66
4
- data.tar.gz: 81149e6d3ec75fd57988c534932b226850617ce78a2b66d75cd101de8c117478
3
+ metadata.gz: b8084c5bbabb6b85b6e90f03d1895d95c4fa9130dcf68fbb0c0929b43132bf25
4
+ data.tar.gz: 6313d7ff50a64695fac17e31be5e3939f7982db3d0662629ba0841f4c5a71518
5
5
  SHA512:
6
- metadata.gz: 8f7e3242e5d5ad1476be31537a3abfdbec0323fa797eaee514e5f3fc8da047402647c1b3b2940bccc7202215933f41c12b4aa7d1471eb65ea844d895673abcb1
7
- data.tar.gz: a198cd6f0efa32403cdd498749b5f886ebc41bb209535cbd85ae88ceb07ece537706a25828280a0a6a87d04865a218c8f330305fba434e374a1ef68c30d4eadb
6
+ metadata.gz: 82751357b5799a2ee7f3e36cff221a9d0a8a41c08bfc3cae1309e50a1af9d6fbd5392ee9ecd657051f03fc59440276d6d481266cf560da034a3012d2a79dee62
7
+ data.tar.gz: '06320641649d6a8caa68c2fdba355f0bc76755b13f37ae0e1afba53115f0a0e7df1c073e718308e591a7c91938283505730eaf638fb4959bacfdbbb8851bf956'
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 0.7.1 (2025-07-27)
2
+
3
+ - Fixed `analyze` analyzing partitions twice with declarative partitioning
4
+ - Removed unnecessary query for `unprep` with declarative partitioning
5
+
6
+ ## 0.7.0 (2025-05-26)
7
+
8
+ - Dropped support for Ruby < 3
9
+ - Dropped support for Postgres < 13
10
+
1
11
  ## 0.6.1 (2023-04-26)
2
12
 
3
13
  - Fixed `uninitialized constant` error
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016-2023 Andrew Kane
3
+ Copyright (c) 2016-2025 Andrew Kane
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -4,7 +4,7 @@ Postgres partitioning as easy as pie. Works great for both new and existing tabl
4
4
 
5
5
  :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
6
6
 
7
- [![Build Status](https://github.com/ankane/pgslice/workflows/build/badge.svg?branch=master)](https://github.com/ankane/pgslice/actions)
7
+ [![Build Status](https://github.com/ankane/pgslice/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/pgslice/actions)
8
8
 
9
9
  ## Install
10
10
 
@@ -14,7 +14,9 @@ pgslice is a command line tool. To install, run:
14
14
  gem install pgslice
15
15
  ```
16
16
 
17
- This will give you the `pgslice` command. You can also install it with [Homebrew](#homebrew) or [Docker](#docker). If installation fails, you may need to install [dependencies](#dependencies).
17
+ This will give you the `pgslice` command. If installation fails, you may need to install [dependencies](#dependencies).
18
+
19
+ You can also install it with [Homebrew](#homebrew) or [Docker](#docker).
18
20
 
19
21
  ## Steps
20
22
 
@@ -92,11 +94,11 @@ pgslice prep visits created_at month
92
94
  ```sql
93
95
  BEGIN;
94
96
 
95
- CREATE TABLE "public"."visits_intermediate" (LIKE "public"."visits" INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING STORAGE INCLUDING COMMENTS) PARTITION BY RANGE ("created_at");
97
+ CREATE TABLE "public"."visits_intermediate" (LIKE "public"."visits" INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING STORAGE INCLUDING COMMENTS INCLUDING STATISTICS INCLUDING GENERATED INCLUDING COMPRESSION) PARTITION BY RANGE ("created_at");
96
98
 
97
99
  CREATE INDEX ON "public"."visits_intermediate" USING btree ("created_at");
98
100
 
99
- COMMENT ON TABLE "public"."visits_intermediate" is 'column:createdAt,period:day,cast:date,version:3';
101
+ COMMENT ON TABLE "public"."visits_intermediate" is 'column:created_at,period:month,cast:date,version:3';
100
102
 
101
103
  COMMIT;
102
104
  ```
@@ -108,17 +110,17 @@ pgslice add_partitions visits --intermediate --past 1 --future 1
108
110
  ```sql
109
111
  BEGIN;
110
112
 
111
- CREATE TABLE "public"."visits_202208" PARTITION OF "public"."visits_intermediate" FOR VALUES FROM ('2022-08-01') TO ('2022-09-01');
113
+ CREATE TABLE "public"."visits_202408" PARTITION OF "public"."visits_intermediate" FOR VALUES FROM ('2024-08-01') TO ('2024-09-01');
112
114
 
113
- ALTER TABLE "public"."visits_202208" ADD PRIMARY KEY ("id");
115
+ ALTER TABLE "public"."visits_202408" ADD PRIMARY KEY ("id");
114
116
 
115
- CREATE TABLE "public"."visits_202209" PARTITION OF "public"."visits_intermediate" FOR VALUES FROM ('2022-09-01') TO ('2022-10-01');
117
+ CREATE TABLE "public"."visits_202409" PARTITION OF "public"."visits_intermediate" FOR VALUES FROM ('2024-09-01') TO ('2024-10-01');
116
118
 
117
- ALTER TABLE "public"."visits_202209" ADD PRIMARY KEY ("id");
119
+ ALTER TABLE "public"."visits_202409" ADD PRIMARY KEY ("id");
118
120
 
119
- CREATE TABLE "public"."visits_202210" PARTITION OF "public"."visits_intermediate" FOR VALUES FROM ('2022-10-01') TO ('2022-11-01');
121
+ CREATE TABLE "public"."visits_202410" PARTITION OF "public"."visits_intermediate" FOR VALUES FROM ('2024-10-01') TO ('2024-11-01');
120
122
 
121
- ALTER TABLE "public"."visits_202210" ADD PRIMARY KEY ("id");
123
+ ALTER TABLE "public"."visits_202410" ADD PRIMARY KEY ("id");
122
124
 
123
125
  COMMIT;
124
126
  ```
@@ -131,17 +133,17 @@ pgslice fill visits
131
133
  /* 1 of 3 */
132
134
  INSERT INTO "public"."visits_intermediate" ("id", "user_id", "ip", "created_at")
133
135
  SELECT "id", "user_id", "ip", "created_at" FROM "public"."visits"
134
- WHERE "id" > 0 AND "id" <= 10000 AND "created_at" >= '2022-08-01'::date AND "created_at" < '2022-11-01'::date
136
+ WHERE "id" > 0 AND "id" <= 10000 AND "created_at" >= '2024-08-01'::date AND "created_at" < '2024-11-01'::date
135
137
 
136
138
  /* 2 of 3 */
137
139
  INSERT INTO "public"."visits_intermediate" ("id", "user_id", "ip", "created_at")
138
140
  SELECT "id", "user_id", "ip", "created_at" FROM "public"."visits"
139
- WHERE "id" > 10000 AND "id" <= 20000 AND "created_at" >= '2022-08-01'::date AND "created_at" < '2022-11-01'::date
141
+ WHERE "id" > 10000 AND "id" <= 20000 AND "created_at" >= '2024-08-01'::date AND "created_at" < '2024-11-01'::date
140
142
 
141
143
  /* 3 of 3 */
142
144
  INSERT INTO "public"."visits_intermediate" ("id", "user_id", "ip", "created_at")
143
145
  SELECT "id", "user_id", "ip", "created_at" FROM "public"."visits"
144
- WHERE "id" > 20000 AND "id" <= 30000 AND "created_at" >= '2022-08-01'::date AND "created_at" < '2022-11-01'::date
146
+ WHERE "id" > 20000 AND "id" <= 30000 AND "created_at" >= '2024-08-01'::date AND "created_at" < '2024-11-01'::date
145
147
  ```
146
148
 
147
149
  ```sh
@@ -149,11 +151,11 @@ pgslice analyze visits
149
151
  ```
150
152
 
151
153
  ```sql
152
- ANALYZE VERBOSE "public"."visits_202208";
154
+ ANALYZE VERBOSE "public"."visits_202408";
153
155
 
154
- ANALYZE VERBOSE "public"."visits_202209";
156
+ ANALYZE VERBOSE "public"."visits_202409";
155
157
 
156
- ANALYZE VERBOSE "public"."visits_202210";
158
+ ANALYZE VERBOSE "public"."visits_202410";
157
159
 
158
160
  ANALYZE VERBOSE "public"."visits_intermediate";
159
161
  ```
@@ -217,14 +219,14 @@ WHERE
217
219
  Back up and drop older partitions each day, month, or year.
218
220
 
219
221
  ```sh
220
- pg_dump -c -Fc -t <table>_202209 $PGSLICE_URL > <table>_202209.dump
221
- psql -c "DROP TABLE <table>_202209" $PGSLICE_URL
222
+ pg_dump -c -Fc -t <table>_202409 $PGSLICE_URL > <table>_202409.dump
223
+ psql -c "DROP TABLE <table>_202409" $PGSLICE_URL
222
224
  ```
223
225
 
224
226
  If you use [Amazon S3](https://aws.amazon.com/s3/) for backups, [s3cmd](https://github.com/s3tools/s3cmd) is a nice tool.
225
227
 
226
228
  ```sh
227
- s3cmd put <table>_202209.dump s3://<s3-bucket>/<table>_202209.dump
229
+ s3cmd put <table>_202409.dump s3://<s3-bucket>/<table>_202409.dump
228
230
  ```
229
231
 
230
232
  ## Schema Updates
@@ -267,7 +269,7 @@ SELECT * FROM
267
269
  WHERE
268
270
  user_id = 123 AND
269
271
  -- for performance
270
- created_at >= '2022-09-01' AND created_at < '2022-09-02'
272
+ created_at >= '2024-09-01' AND created_at < '2024-09-02'
271
273
  ```
272
274
 
273
275
  For this to be effective, ensure `constraint_exclusion` is set to `partition` (the default value) or `on`.
@@ -299,12 +301,14 @@ You can also use pgslice to reduce the size of a table without partitioning by c
299
301
  ```sh
300
302
  pgslice prep <table> --no-partition
301
303
  pgslice fill <table> --where "id > 1000" # use any conditions
304
+ pgslice analyze <table>
302
305
  pgslice swap <table>
306
+ pgslice fill <table> --where "id > 1000" --swapped
303
307
  ```
304
308
 
305
309
  ## Triggers
306
310
 
307
- Triggers aren’t copied from the original table. You can set up triggers on the intermediate table if needed. Note that Postgres doesn’t support `BEFORE / FOR EACH ROW` triggers on partitioned tables.
311
+ Triggers aren’t copied from the original table. You can set up triggers on the intermediate table if needed.
308
312
 
309
313
  ## Data Protection
310
314
 
@@ -317,7 +321,7 @@ Always make sure your [connection is secure](https://ankane.org/postgres-sslmode
317
321
  With Homebrew, you can use:
318
322
 
319
323
  ```sh
320
- brew install ankane/brew/pgslice
324
+ brew install pgslice
321
325
  ```
322
326
 
323
327
  ### Docker
@@ -374,6 +378,10 @@ Also check out:
374
378
  - [PgHero](https://github.com/ankane/pghero) - A performance dashboard for Postgres
375
379
  - [pgsync](https://github.com/ankane/pgsync) - Sync Postgres data to your local machine
376
380
 
381
+ ## History
382
+
383
+ View the [changelog](https://github.com/ankane/pgslice/blob/master/CHANGELOG.md)
384
+
377
385
  ## Contributing
378
386
 
379
387
  Everyone is encouraged to help improve this project. Here are a few ways you can help:
@@ -396,7 +404,7 @@ bundle exec rake test
396
404
  To test against different versions of Postgres with Docker, use:
397
405
 
398
406
  ```sh
399
- docker run -p=8000:5432 postgres:14
407
+ docker run -p=8000:5432 postgres:16
400
408
  TZ=Etc/UTC PGSLICE_URL=postgres://postgres@localhost:8000/postgres bundle exec rake
401
409
  ```
402
410
 
@@ -63,14 +63,14 @@ module PgSlice
63
63
  added_partitions << partition
64
64
 
65
65
  if declarative
66
- queries << <<-SQL
67
- CREATE TABLE #{quote_table(partition)} PARTITION OF #{quote_table(table)} FOR VALUES FROM (#{sql_date(day, cast, false)}) TO (#{sql_date(advance_date(day, period, 1), cast, false)})#{tablespace_str};
66
+ queries << <<~SQL
67
+ CREATE TABLE #{quote_table(partition)} PARTITION OF #{quote_table(table)} FOR VALUES FROM (#{sql_date(day, cast, false)}) TO (#{sql_date(advance_date(day, period, 1), cast, false)})#{tablespace_str};
68
68
  SQL
69
69
  else
70
- queries << <<-SQL
71
- CREATE TABLE #{quote_table(partition)}
72
- (CHECK (#{quote_ident(field)} >= #{sql_date(day, cast)} AND #{quote_ident(field)} < #{sql_date(advance_date(day, period, 1), cast)}))
73
- INHERITS (#{quote_table(table)})#{tablespace_str};
70
+ queries << <<~SQL
71
+ CREATE TABLE #{quote_table(partition)}
72
+ (CHECK (#{quote_ident(field)} >= #{sql_date(day, cast)} AND #{quote_ident(field)} < #{sql_date(advance_date(day, period, 1), cast)}))
73
+ INHERITS (#{quote_table(table)})#{tablespace_str};
74
74
  SQL
75
75
  end
76
76
 
@@ -115,17 +115,17 @@ CREATE TABLE #{quote_table(partition)}
115
115
  trigger_defs = current_defs + future_defs + past_defs.reverse
116
116
 
117
117
  if trigger_defs.any?
118
- queries << <<-SQL
119
- CREATE OR REPLACE FUNCTION #{quote_ident(trigger_name)}()
120
- RETURNS trigger AS $$
121
- BEGIN
122
- IF #{trigger_defs.join("\n ELSIF ")}
123
- ELSE
124
- RAISE EXCEPTION 'Date out of range. Ensure partitions are created.';
125
- END IF;
126
- RETURN NULL;
127
- END;
128
- $$ LANGUAGE plpgsql;
118
+ queries << <<~SQL
119
+ CREATE OR REPLACE FUNCTION #{quote_ident(trigger_name)}()
120
+ RETURNS trigger AS $$
121
+ BEGIN
122
+ IF #{trigger_defs.join("\n ELSIF ")}
123
+ ELSE
124
+ RAISE EXCEPTION 'Date out of range. Ensure partitions are created.';
125
+ END IF;
126
+ RETURN NULL;
127
+ END;
128
+ $$ LANGUAGE plpgsql;
129
129
  SQL
130
130
  end
131
131
  end
@@ -5,8 +5,10 @@ module PgSlice
5
5
  def analyze(table)
6
6
  table = create_table(table)
7
7
  parent_table = options[:swapped] ? table : table.intermediate_table
8
+ assert_table(parent_table)
8
9
 
9
- analyze_list = parent_table.partitions + [parent_table]
10
+ _, _, _, _, declarative, _ = parent_table.fetch_settings(table.trigger_name)
11
+ analyze_list = declarative ? [parent_table] : (parent_table.partitions + [parent_table])
10
12
  run_queries_without_transaction(analyze_list.map { |t| "ANALYZE VERBOSE #{quote_table(t)};" })
11
13
  end
12
14
  end
@@ -82,11 +82,11 @@ module PgSlice
82
82
  where << " AND #{options[:where]}"
83
83
  end
84
84
 
85
- query = <<-SQL
86
- /* #{i} of #{batch_count} */
87
- INSERT INTO #{quote_table(dest_table)} (#{fields})
88
- SELECT #{fields} FROM #{quote_table(source_table)}
89
- WHERE #{where}
85
+ query = <<~SQL
86
+ /* #{i} of #{batch_count} */
87
+ INSERT INTO #{quote_table(dest_table)} (#{fields})
88
+ SELECT #{fields} FROM #{quote_table(source_table)}
89
+ WHERE #{where}
90
90
  SQL
91
91
 
92
92
  run_query(query)
@@ -4,7 +4,7 @@ module PgSlice
4
4
  option :partition, type: :boolean, default: true, desc: "Partition the table"
5
5
  option :trigger_based, type: :boolean, default: false, desc: "Use trigger-based partitioning"
6
6
  option :test_version, type: :numeric, hide: true
7
- def prep(table, column=nil, period=nil)
7
+ def prep(table, column = nil, period = nil)
8
8
  table = create_table(table)
9
9
  intermediate_table = table.intermediate_table
10
10
  trigger_name = table.trigger_name
@@ -33,15 +33,12 @@ module PgSlice
33
33
  declarative = version > 1
34
34
 
35
35
  if declarative && options[:partition]
36
- including = ["DEFAULTS", "CONSTRAINTS", "STORAGE", "COMMENTS", "STATISTICS"]
37
- if server_version_num >= 120000
38
- including << "GENERATED"
39
- end
36
+ including = ["DEFAULTS", "CONSTRAINTS", "STORAGE", "COMMENTS", "STATISTICS", "GENERATED"]
40
37
  if server_version_num >= 140000
41
38
  including << "COMPRESSION"
42
39
  end
43
- queries << <<-SQL
44
- CREATE TABLE #{quote_table(intermediate_table)} (LIKE #{quote_table(table)} #{including.map { |v| "INCLUDING #{v}" }.join(" ")}) PARTITION BY RANGE (#{quote_ident(column)});
40
+ queries << <<~SQL
41
+ CREATE TABLE #{quote_table(intermediate_table)} (LIKE #{quote_table(table)} #{including.map { |v| "INCLUDING #{v}" }.join(" ")}) PARTITION BY RANGE (#{quote_ident(column)});
45
42
  SQL
46
43
 
47
44
  if version == 3
@@ -57,12 +54,12 @@ CREATE TABLE #{quote_table(intermediate_table)} (LIKE #{quote_table(table)} #{in
57
54
 
58
55
  # add comment
59
56
  cast = table.column_cast(column)
60
- queries << <<-SQL
61
- COMMENT ON TABLE #{quote_table(intermediate_table)} IS 'column:#{column},period:#{period},cast:#{cast},version:#{version}';
57
+ queries << <<~SQL
58
+ COMMENT ON TABLE #{quote_table(intermediate_table)} IS 'column:#{column},period:#{period},cast:#{cast},version:#{version}';
62
59
  SQL
63
60
  else
64
- queries << <<-SQL
65
- CREATE TABLE #{quote_table(intermediate_table)} (LIKE #{quote_table(table)} INCLUDING ALL);
61
+ queries << <<~SQL
62
+ CREATE TABLE #{quote_table(intermediate_table)} (LIKE #{quote_table(table)} INCLUDING ALL);
66
63
  SQL
67
64
 
68
65
  table.foreign_keys.each do |fk_def|
@@ -71,24 +68,24 @@ CREATE TABLE #{quote_table(intermediate_table)} (LIKE #{quote_table(table)} INCL
71
68
  end
72
69
 
73
70
  if options[:partition] && !declarative
74
- queries << <<-SQL
75
- CREATE FUNCTION #{quote_ident(trigger_name)}()
76
- RETURNS trigger AS $$
77
- BEGIN
78
- RAISE EXCEPTION 'Create partitions first.';
79
- END;
80
- $$ LANGUAGE plpgsql;
71
+ queries << <<~SQL
72
+ CREATE FUNCTION #{quote_ident(trigger_name)}()
73
+ RETURNS trigger AS $$
74
+ BEGIN
75
+ RAISE EXCEPTION 'Create partitions first.';
76
+ END;
77
+ $$ LANGUAGE plpgsql;
81
78
  SQL
82
79
 
83
- queries << <<-SQL
84
- CREATE TRIGGER #{quote_ident(trigger_name)}
85
- BEFORE INSERT ON #{quote_table(intermediate_table)}
86
- FOR EACH ROW EXECUTE PROCEDURE #{quote_ident(trigger_name)}();
80
+ queries << <<~SQL
81
+ CREATE TRIGGER #{quote_ident(trigger_name)}
82
+ BEFORE INSERT ON #{quote_table(intermediate_table)}
83
+ FOR EACH ROW EXECUTE PROCEDURE #{quote_ident(trigger_name)}();
87
84
  SQL
88
85
 
89
86
  cast = table.column_cast(column)
90
- queries << <<-SQL
91
- COMMENT ON TRIGGER #{quote_ident(trigger_name)} ON #{quote_table(intermediate_table)} IS 'column:#{column},period:#{period},cast:#{cast}';
87
+ queries << <<~SQL
88
+ COMMENT ON TRIGGER #{quote_ident(trigger_name)} ON #{quote_table(intermediate_table)} IS 'column:#{column},period:#{period},cast:#{cast}';
92
89
  SQL
93
90
  end
94
91
 
@@ -9,9 +9,14 @@ module PgSlice
9
9
  assert_table(intermediate_table)
10
10
 
11
11
  queries = [
12
- "DROP TABLE #{quote_table(intermediate_table)} CASCADE;",
13
- "DROP FUNCTION IF EXISTS #{quote_ident(trigger_name)}();"
12
+ "DROP TABLE #{quote_table(intermediate_table)} CASCADE;"
14
13
  ]
14
+
15
+ _, _, _, _, declarative, _ = intermediate_table.fetch_settings(table.trigger_name)
16
+ unless declarative
17
+ queries << "DROP FUNCTION IF EXISTS #{quote_ident(trigger_name)}();"
18
+ end
19
+
15
20
  run_queries(queries)
16
21
  end
17
22
  end
data/lib/pgslice/cli.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  module PgSlice
2
2
  class CLI < Thor
3
3
  class << self
4
- attr_accessor :instance
4
+ attr_accessor :instance, :exit_on_failure
5
+ alias_method :exit_on_failure?, :exit_on_failure
5
6
  end
7
+ self.exit_on_failure = true
6
8
 
7
9
  include Helpers
8
10
 
@@ -13,10 +15,6 @@ module PgSlice
13
15
 
14
16
  map %w[--version -v] => :version
15
17
 
16
- def self.exit_on_failure?
17
- ENV["PGSLICE_ENV"] != "test"
18
- end
19
-
20
18
  def initialize(*args)
21
19
  PgSlice::CLI.instance = self
22
20
  $stdout.sync = true
@@ -41,8 +41,8 @@ module PgSlice
41
41
  say message
42
42
  end
43
43
  @server_version_num = conn.exec("SHOW server_version_num")[0]["server_version_num"].to_i
44
- if @server_version_num < 110000
45
- abort "This version of pgslice requires Postgres 11+"
44
+ if @server_version_num < 130000
45
+ abort "This version of pgslice requires Postgres 13+"
46
46
  end
47
47
  conn
48
48
  end
data/lib/pgslice/table.rb CHANGED
@@ -12,16 +12,24 @@ module PgSlice
12
12
  end
13
13
 
14
14
  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
16
20
  end
17
21
 
18
22
  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"] }
20
28
  end
21
29
 
22
30
  # http://www.dbforums.com/showthread.php?1667561-How-to-list-sequences-and-the-columns-by-SQL
23
31
  def sequences
24
- query = <<-SQL
32
+ query = <<~SQL
25
33
  SELECT
26
34
  a.attname AS related_column,
27
35
  n.nspname AS sequence_schema,
@@ -41,13 +49,17 @@ module PgSlice
41
49
  end
42
50
 
43
51
  def foreign_keys
44
- execute("SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = #{regclass} AND contype ='f'").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"] }
45
57
  end
46
58
 
47
59
  # https://stackoverflow.com/a/20537829
48
60
  # TODO can simplify with array_position in Postgres 9.5+
49
61
  def primary_key
50
- query = <<-SQL
62
+ query = <<~SQL
51
63
  SELECT
52
64
  pg_attribute.attname,
53
65
  format_type(pg_attribute.atttypid, pg_attribute.atttypmod),
@@ -69,7 +81,11 @@ module PgSlice
69
81
  end
70
82
 
71
83
  def index_defs
72
- execute("SELECT pg_get_indexdef(indexrelid) FROM pg_index WHERE indrelid = #{regclass} AND indisprimary = 'f'").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"] }
73
89
  end
74
90
 
75
91
  def quote_table
@@ -89,17 +105,25 @@ module PgSlice
89
105
  end
90
106
 
91
107
  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"]
93
113
  data_type == "timestamp with time zone" ? "timestamptz" : "date"
94
114
  end
95
115
 
96
116
  def max_id(primary_key, below: nil, where: nil)
97
117
  query = "SELECT MAX(#{quote_ident(primary_key)}) FROM #{quote_table}"
98
118
  conditions = []
99
- conditions << "#{quote_ident(primary_key)} <= #{below}" if below
119
+ params = []
120
+ if below
121
+ conditions << "#{quote_ident(primary_key)} <= $1"
122
+ params << below
123
+ end
100
124
  conditions << where if where
101
125
  query << " WHERE #{conditions.join(" AND ")}" if conditions.any?
102
- execute(query)[0]["max"].to_i
126
+ execute(query, params)[0]["max"].to_i
103
127
  end
104
128
 
105
129
  def min_id(primary_key, column, cast, starting_time, where)
@@ -113,15 +137,15 @@ module PgSlice
113
137
 
114
138
  # ensure this returns partitions in the correct order
115
139
  def partitions
116
- query = <<-SQL
140
+ query = <<~SQL
117
141
  SELECT
118
- nmsp_child.nspname AS schema,
119
- child.relname AS name
142
+ nmsp_child.nspname AS schema,
143
+ child.relname AS name
120
144
  FROM pg_inherits
121
- JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
122
- JOIN pg_class child ON pg_inherits.inhrelid = child.oid
123
- JOIN pg_namespace nmsp_parent ON nmsp_parent.oid = parent.relnamespace
124
- JOIN pg_namespace nmsp_child ON nmsp_child.oid = child.relnamespace
145
+ JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
146
+ JOIN pg_class child ON pg_inherits.inhrelid = child.oid
147
+ JOIN pg_namespace nmsp_parent ON nmsp_parent.oid = parent.relnamespace
148
+ JOIN pg_namespace nmsp_child ON nmsp_child.oid = child.relnamespace
125
149
  WHERE
126
150
  nmsp_parent.nspname = $1 AND
127
151
  parent.relname = $2
@@ -131,11 +155,15 @@ module PgSlice
131
155
  end
132
156
 
133
157
  def fetch_comment
134
- execute("SELECT obj_description(#{regclass}) AS comment")[0]
158
+ execute("SELECT obj_description($1::regclass) AS comment", [quote_table])[0]
135
159
  end
136
160
 
137
161
  def fetch_trigger(trigger_name)
138
- execute("SELECT obj_description(oid, 'pg_trigger') AS comment FROM pg_trigger WHERE tgname = $1 AND tgrelid = #{regclass}", [trigger_name])[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]
139
167
  end
140
168
 
141
169
  # legacy
@@ -186,10 +214,6 @@ module PgSlice
186
214
  PG::Connection.quote_ident(value)
187
215
  end
188
216
 
189
- def regclass
190
- "#{escape_literal(quote_table)}::regclass"
191
- end
192
-
193
217
  def sql_date(time, cast, add_cast = true)
194
218
  if cast == "timestamptz"
195
219
  fmt = "%Y-%m-%d %H:%M:%S UTC"
@@ -1,3 +1,3 @@
1
1
  module PgSlice
2
- VERSION = "0.6.1"
2
+ VERSION = "0.7.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgslice
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2023-04-26 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: pg
@@ -38,7 +37,6 @@ dependencies:
38
37
  - - ">="
39
38
  - !ruby/object:Gem::Version
40
39
  version: '0'
41
- description:
42
40
  email: andrew@ankane.org
43
41
  executables:
44
42
  - pgslice
@@ -65,7 +63,6 @@ homepage: https://github.com/ankane/pgslice
65
63
  licenses:
66
64
  - MIT
67
65
  metadata: {}
68
- post_install_message:
69
66
  rdoc_options: []
70
67
  require_paths:
71
68
  - lib
@@ -73,15 +70,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
73
70
  requirements:
74
71
  - - ">="
75
72
  - !ruby/object:Gem::Version
76
- version: '2.7'
73
+ version: '3'
77
74
  required_rubygems_version: !ruby/object:Gem::Requirement
78
75
  requirements:
79
76
  - - ">="
80
77
  - !ruby/object:Gem::Version
81
78
  version: '0'
82
79
  requirements: []
83
- rubygems_version: 3.4.10
84
- signing_key:
80
+ rubygems_version: 3.6.9
85
81
  specification_version: 4
86
82
  summary: Postgres partitioning as easy as pie
87
83
  test_files: []