pg_conn 0.15.1 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ebb0df0000ecb59d313c906b608bdd267aaef84edcb4be633650601c453768b
4
- data.tar.gz: 96d5e1db6d3644fef73dbb69f50acd7deec6dc79404641615d034586d1fddcf0
3
+ metadata.gz: 25c23025d55818536e6d89bc5fa40b12ac296f0e61657db387f8a8732bdf3504
4
+ data.tar.gz: 7592d3a2b26d7f75f0ea88ad9b8f951422ced0cc913a387eb3f774b31bb1c4eb
5
5
  SHA512:
6
- metadata.gz: 1cba9680627ce4ba4c5b8aa3df5505ab407e6096c26560799680a340640cef3b6a107319c58c1230ee130f1811d569367f8819d81f1be957069703b03d0bbb8b
7
- data.tar.gz: 4c578bd594f740f9b07d288a1ae37de9d54cee7009da5f4d493a58ed324821cd4258ebce916ddfd68527e773c4396cd866da8054d4558d31c95962c6fd9ce9d1
6
+ metadata.gz: 0701ef07551bc1db06b654aa12e957ca4ad2286f1050c122af95ce78670ea426e6bf8f566831070200588e2c844eaf70ddab0cc5147941ccdcf6f4dc232c4102
7
+ data.tar.gz: 5310090682a9c93a648823b477081eebe31c214554ebe06a44d2f6702ec18c3fd5eb43e16bad0c15178d48a6194e109dbf15adb8bb3f75e645b90581ef3bc948
@@ -69,8 +69,8 @@ module PgConn
69
69
  end
70
70
 
71
71
  # Hollow-out a database by removing all schemas in the database. The public
72
- # schema is recreated afterwards unless if :public is false. Uses the
73
- # current database if @database is nil
72
+ # schema is recreated afterwards unless :public is false. Uses the current
73
+ # database if @database is nil
74
74
  #
75
75
  # Note that the database can have active users logged in while the database
76
76
  # is emptied. TODO Explain what happens if the users have active
@@ -103,7 +103,7 @@ module PgConn
103
103
  create(to_database, owner: owner, template: from_database)
104
104
  end
105
105
 
106
- # TODO: This code is replicated across many project. Should be moved to PgConn
106
+ # TODO: This code is replicated across many projects. Should be moved to PgConn
107
107
  def load(database, file, role: ENV['USER'], gzip: nil)
108
108
  command_opt = role ? "-c \"set role #{role}\";\n" : nil
109
109
  if gzip
@@ -118,7 +118,7 @@ module PgConn
118
118
  status == 0 or raise PsqlError.new(stderr)
119
119
  end
120
120
 
121
- # TODO: This code is replicated across many project. Should be moved to PgConn
121
+ # TODO: This code is replicated across many projects. Should be moved to PgConn
122
122
  def save(database, file, data: true, schema: true, gzip: nil)
123
123
  data_opt = data ? nil : "--schema-only"
124
124
  schema_opt = schema ? nil : "--data-only"
@@ -35,6 +35,26 @@ module PgConn
35
35
  true
36
36
  end
37
37
 
38
+ # Hollow out a schema by dropping all tables and views (but still not
39
+ # functions and procedures TODO)
40
+ def empty!(schema, exclude: [])
41
+ self.list_tables(schema, exclude: exclude).each { |table|
42
+ conn.exec "drop table if exists #{schema}.#{table} cascade"
43
+ }
44
+ self.list_views(schema, exclude: exclude).each { |view|
45
+ conn.exec "drop view if exists #{schema}.#{view} cascade"
46
+ }
47
+ end
48
+
49
+ # Empty all tables in the given schema
50
+ def clean!(schema, exclude: [])
51
+ conn.session.triggers(false) {
52
+ self.list_tables(schema, exclude: exclude).each { |table|
53
+ conn.exec "delete from #{schema}.#{table}"
54
+ }
55
+ }
56
+ end
57
+
38
58
  # List schemas. Built-in schemas are not listed unless the :all option is
39
59
  # true. The :exclude option can be used to exclude named schemas
40
60
  def list(all: false, exclude: [])
@@ -53,7 +73,7 @@ module PgConn
53
73
 
54
74
  # Return true if table exists
55
75
  def exist_table?(schema, table)
56
- conn.exist?(relation_exist_query(schema, table, kind: %w(r f)))
76
+ conn.exist? relation_exist_query(schema, table, kind: %w(r f))
57
77
  end
58
78
 
59
79
  # Return true if view exists
@@ -66,19 +86,22 @@ module PgConn
66
86
  conn.exist? column_exist_query(schema, relation, column)
67
87
  end
68
88
 
89
+ # TODO
90
+ # def exist_index?(schema, relation, FIXME)
91
+
69
92
  # Return list of relations in the schema
70
- def list_relations(schema)
71
- conn.values relation_list_query(schema)
93
+ def list_relations(schema, exclude: [])
94
+ conn.values relation_list_query(schema, exclude: exclude)
72
95
  end
73
96
 
74
97
  # Return list of tables in the schema
75
- def list_tables(schema)
76
- conn.values relation_list_query(schema, kind: %w(r f))
98
+ def list_tables(schema, exclude: [])
99
+ conn.values relation_list_query(schema, exclude: exclude, kind: %w(r f))
77
100
  end
78
101
 
79
102
  # Return list of view in the schema
80
- def list_views(schema)
81
- conn.values relation_list_query(schema, kind: %w(v m))
103
+ def list_views(schema, exclude: [])
104
+ conn.values relation_list_query(schema, exclude: exclude, kind: %w(v m))
82
105
  end
83
106
 
84
107
  # Return a list of columns. If +relation+ is defined, only columns from that
@@ -113,13 +136,18 @@ module PgConn
113
136
  )
114
137
  end
115
138
 
116
- def relation_list_query(schema, kind: nil)
117
- kind_sql_list = "'" + (kind.nil? ? %w(r f v m) : Array(kind).flatten).join("', '") + "'"
139
+ def relation_list_query(schema, exclude: nil, kind: nil)
140
+ kind_list = "'" + (kind.nil? ? %w(r f v m) : Array(kind).flatten).join("', '") + "'"
141
+ kind_expr = "relkind in (#{kind_list})"
142
+ exclude = Array(exclude || []).flatten
143
+ exclude_list = "'#{exclude.flatten.join("', '")}'" if !exclude.empty?
144
+ exclude_expr = exclude.empty? ? "true = true" : "not relname in (#{exclude_list})"
118
145
  %(
119
146
  select relname
120
147
  from pg_class
121
148
  where relnamespace::regnamespace::text = '#{schema}'
122
- and relkind in (#{kind_sql_list})
149
+ and #{kind_expr}
150
+ and #{exclude_expr}
123
151
  )
124
152
  end
125
153
 
@@ -8,7 +8,8 @@ module PgConn
8
8
  end
9
9
 
10
10
  # Returns a list of users connected to the given database. If database is
11
- # nil, it returns a list of database/username tuples for all connected users
11
+ # nil, it returns a list of database/username tuples for all connected
12
+ # users
12
13
  def list(database)
13
14
  if database
14
15
  conn.values "select usename from pg_stat_activity where datname = '#{database}'"
@@ -21,34 +22,52 @@ module PgConn
21
22
  end
22
23
  end
23
24
 
24
- # Terminate sessions in the database of the given users or of all users if
25
- # the users is nil. Note that 'terminate(database)' is a nop because the
26
- # absent users argument defaults to an empty list
27
- #
28
- # TODO: Make is possible to terminate a single session of a user with
29
- # multiple sessions (is this ever relevant?)
30
- def terminate(database, *users)
25
+ # Return true if the given database accepts connections
26
+ def enabled?(database)
31
27
  !database.nil? or raise ArgumentError
32
- users = Array(users).flatten
33
- case users
34
- when []; return
35
- when [nil]; users = list(database)
36
- else users = Array(users).flatten
37
- end
38
- pids = self.pids(database, users)
39
- return if pids.empty?
40
- pids_sql = pids.map { |pid| "(#{pid})" }.join(", ")
41
- conn.execute "select pg_terminate_backend(pid) from ( values #{pids_sql} ) as x(pid)"
28
+ conn.value "select datallowconn from pg_catalog.pg_database where datname = '#{database}'"
42
29
  end
43
30
 
31
+ # Ensure connections to the given database are enabled
32
+ def enable(database)
33
+ !database.nil? or raise ArgumentError
34
+ conn.execute "alter database #{database} allow_connections = true"
35
+ end
36
+
37
+ # Ensure connections to the given database are disabled
44
38
  def disable(database)
45
39
  !database.nil? or raise ArgumentError
46
40
  conn.execute "alter database #{database} allow_connections = false"
47
41
  end
48
42
 
49
- def enable(database)
43
+ # TODO: Why not let a nil database argument have the current database as default?
44
+
45
+ # Terminate sessions in the database of the given users or of all users if
46
+ # nil. Note that 'terminate(database)' is a nop because the absent users
47
+ # argument defaults to an empty list
48
+ #
49
+ # TODO: Make is possible to terminate a single session of a user with
50
+ # multiple sessions (is this ever relevant?)
51
+ #
52
+ def terminate(database, *users)
50
53
  !database.nil? or raise ArgumentError
51
- conn.execute "alter database #{database} allow_connections = true"
54
+ enabled = self.enabled?(database)
55
+
56
+ case users
57
+ when [];
58
+ return
59
+ when [nil]
60
+ self.disable(database) if enabled
61
+ users = self.list(database)
62
+ else
63
+ users = Array(users).flatten
64
+ end
65
+ pids = self.pids(database, users)
66
+ if !pids.empty?
67
+ pids_sql = pids.map { |pid| "(#{pid})" }.join(", ")
68
+ conn.execute "select pg_terminate_backend(pid) from ( values #{pids_sql} ) as x(pid)"
69
+ end
70
+ self.enable(database) if self.enabled?(database) != enabled
52
71
  end
53
72
 
54
73
  # Run block without any connected users. Existing sessions are terminated
@@ -56,14 +75,46 @@ module PgConn
56
75
  !database.nil? or raise ArgumentError
57
76
  begin
58
77
  disable(database)
59
- users = list(database)
60
- terminate(database, users)
78
+ terminate(database, nil)
61
79
  yield
62
80
  ensure
63
81
  enable(database)
64
82
  end
65
83
  end
66
84
 
85
+ # Return true if session triggers are enabled. Triggers are enabled by
86
+ # default by Postgres
87
+ def triggers?() conn.value "select current_setting('session_replication_role') = 'replica'" end
88
+
89
+ # Enable session triggers
90
+ def enable_triggers()
91
+ conn.execute "set session session_replication_role = replica"
92
+ end
93
+
94
+ # Disable session triggers
95
+ def disable_triggers()
96
+ conn.execute "set session session_replication_role = DEFAULT"
97
+ end
98
+
99
+ # Execute block with session triggers on or off
100
+ def triggers(on_off, &block)
101
+ begin
102
+ active = triggers?
103
+ if on_off && !active
104
+ enable_triggers
105
+ elsif !on_off && active
106
+ disable_triggers
107
+ end
108
+ yield
109
+ ensure
110
+ if on_off && !active
111
+ disable_triggers
112
+ elsif !on_off && active
113
+ enable_triggers
114
+ end
115
+ end
116
+ end
117
+
67
118
  private
68
119
  # Like #list but returns the PIDs of the users
69
120
  def pids(database, users)
@@ -1,3 +1,3 @@
1
1
  module PgConn
2
- VERSION = "0.15.1"
2
+ VERSION = "0.16.0"
3
3
  end
data/lib/pg_conn.rb CHANGED
@@ -73,6 +73,9 @@ module PgConn
73
73
  # transactions
74
74
  attr_reader :error
75
75
 
76
+ # True if the transaction is in a error state
77
+ def error?() !@error.nil? end
78
+
76
79
  # Tuple of error message, lineno, and charno of the error object where each
77
80
  # element defaults to nil if not found
78
81
  def err
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_conn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.1
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claus Rasmussen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-04-13 00:00:00.000000000 Z
11
+ date: 2024-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg