pg_conn 0.35.1 → 0.37.0

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: 7c834402fcec956ffcb288fa201d7a6bade6b435002ae4758e1d86ea54f5a656
4
- data.tar.gz: 20fafcbbe4c9f93ab426c6012e91f073e5224ab84d23948da894f4a64b5ef7c9
3
+ metadata.gz: 1edd50ec4e7e4f072a695cd044ab6d930dd14cdf763dd335ee0fbb65865e83c4
4
+ data.tar.gz: 3fe0cf2fe952999dceea4b6776155b7252d4e3f4aeeab7089348fc10ec9cfc5e
5
5
  SHA512:
6
- metadata.gz: 1da57a0c7f8daffdaac879899d3bd7d9723a6512e038afda6996329717d9b0246f7b646161ee1f0a82e00d828a12b9328f212ef98b47132914e4c66dc9f1e888
7
- data.tar.gz: 4fd13dd8b4130ebb417379dd3ec62a3476f7d721a7617d14ae2f5b88a0c1f13812dbd440931bc14c02c3db702f9ff181a8b4ab3116a08527293ae6b632c9cd0b
6
+ metadata.gz: a84b206b8c7b19813a3acbc63350289e6b072fc1ef8edb65901c99fe6f76fffe3c9ad9ab54d44105652902a8486e5904071e8f6712e73b7b9752019646a8bb8b
7
+ data.tar.gz: 6725ac8c9ef737db41770df819fefa88cb716ff189d714b139079475ee3255a59ec996e9d9c1e138b971b2b4908d6a91f08b9d7bc3731a7e46c32bd684f28000
data/TODO CHANGED
@@ -1,11 +1,12 @@
1
1
  TODO
2
+ o Drop existing SQL output interface and instead implement a SQL logger
3
+
2
4
  o Add support for hstore (a hash)
3
5
 
4
6
  o Add a <fetch>! method. Require v2
5
7
  value? 0 or 1
6
8
  value 1
7
9
  values 0 or n
8
- values? 0 or n
9
10
  values! 1 or more
10
11
 
11
12
  o Instrumentation of connection object
@@ -43,10 +44,6 @@ TODO
43
44
 
44
45
  server.call :sp_nic_update_comtext, str_id, str_comtext, site.current_user.id
45
46
 
46
- o Have a 'with' method that combines multiple brachet-methods:
47
-
48
- conn.with(schema: public, transation: true) { ... }
49
-
50
47
  o Create aliases
51
48
  tuple -> array
52
49
  tuples arrays
@@ -120,6 +117,9 @@ TODO
120
117
  composition of anonymous record types
121
118
 
122
119
  + Quote methods (value, identier, ... -> Postgres string)
120
+ + Have a 'with' method that combines multiple brachet-methods:
121
+ conn.with(schema: public, transation: true) { ... }
122
+
123
123
 
124
124
  REFACTOR
125
125
  #!/usr/bin/env ruby
@@ -48,7 +48,7 @@ module PgConn
48
48
 
49
49
  # Empty all tables in the given schema
50
50
  def clean!(schema, exclude: [])
51
- conn.session.triggers(false) {
51
+ conn.without_triggers {
52
52
  self.list_tables(schema, exclude: exclude).each { |table|
53
53
  conn.exec "delete from #{schema}.#{table}"
54
54
  }
@@ -84,38 +84,6 @@ module PgConn
84
84
  end
85
85
  end
86
86
 
87
- # Return true if session triggers are enabled. Triggers are enabled by
88
- # default by Postgres
89
- def triggers?() conn.value "select current_setting('session_replication_role') <> 'replica'" end
90
-
91
- # Enable session triggers
92
- def enable_triggers()
93
- conn.execute "set session session_replication_role = DEFAULT"
94
- end
95
-
96
- # Disable session triggers
97
- def disable_triggers()
98
- conn.execute "set session session_replication_role = replica"
99
- end
100
-
101
- # Execute block with session triggers on or off
102
- def triggers(on_off, &block)
103
- begin
104
- active = triggers?
105
- if on_off && !active
106
- enable_triggers
107
- elsif !on_off && active
108
- disable_triggers
109
- end
110
- yield
111
- ensure
112
- case active
113
- when true; enable_triggers if !triggers?
114
- when false; disable_triggers if triggers?
115
- end
116
- end
117
- end
118
-
119
87
  private
120
88
  # Like #list but returns the PIDs of the users
121
89
  def pids(database, users)
@@ -1,3 +1,3 @@
1
1
  module PgConn
2
- VERSION = "0.35.1"
2
+ VERSION = "0.37.0"
3
3
  end
data/lib/pg_conn.rb CHANGED
@@ -40,13 +40,13 @@ module PgConn
40
40
  #
41
41
  def self.quote_identifier(s)
42
42
  s = s.to_s if s.is_a?(Symbol)
43
- escape_identifier(s).gsub(/\./, '"."').sub(/"\*"/, "*")
43
+ Literal.new escape_identifier(s).gsub(/\./, '"."').sub(/"\*"/, "*")
44
44
  end
45
45
 
46
46
  # Quote identifiers and concatenate them using ',' as separator
47
- def self.quote_identifiers(idents) = idents.map { |ident| quote_identifier(ident) }.join(", ")
47
+ def self.quote_identifiers(idents) = Literal.new idents.map { |ident| quote_identifier(ident) }.join(", ")
48
48
 
49
- # Quote the value as a string. Emit 'null' if value is nil
49
+ # Quote the value as a string. Returns a Literal object
50
50
  #
51
51
  # The value can be of any type but is converted to a string using #to_s
52
52
  # before quoting. This works by default for the regular types Integer,
@@ -55,7 +55,7 @@ module PgConn
55
55
  #
56
56
  # Hashes are quoted as a literal JSON expression converted into the given
57
57
  # :json_type. If :json_type is nil, it is the application's responsibility to
58
- # cast them to either 'json' or 'jsonb'
58
+ # cast the value to either 'json' or 'jsonb'
59
59
  #
60
60
  # Note that a tuple value (an array) must be quoted using #quote_tuple
61
61
  # because #quote_value would quote the tuple as an array value instead of a
@@ -67,30 +67,31 @@ module PgConn
67
67
  # is guaranteed to be non-empty. Nested arrays are not supported
68
68
  #
69
69
  def self.quote_value(value, elem_type: nil, json_type: nil)
70
- case value
71
- when Literal; value
72
- when String; escape_literal(value)
73
- when Integer, Float; value.to_s
74
- when true, false; value.to_s
75
- when nil; 'null'
76
- when Date, DateTime; "'#{value}'"
77
- when Time; "'#{value.strftime("%FT%T%:z")}'"
78
- when Array
79
- if value.empty?
80
- elem_type or raise Error, "Empty array without elem_type"
81
- "array[]::#{elem_type}[]"
70
+ Literal.new \
71
+ case value
72
+ when Literal; value
73
+ when String; escape_literal(value)
74
+ when Integer, Float; value.to_s
75
+ when true, false; value.to_s
76
+ when nil; 'null'
77
+ when Date, DateTime; "'#{value}'"
78
+ when Time; "'#{value.strftime("%FT%T%:z")}'"
79
+ when Array
80
+ if value.empty?
81
+ elem_type or raise Error, "Empty array without elem_type"
82
+ "array[]::#{elem_type}[]"
83
+ else
84
+ "array[#{value.map { |elem| quote_value(elem) }.join(', ')}]"
85
+ end
86
+ when Hash; ["'#{value.to_json}'", json_type].compact.join('::')
82
87
  else
83
- "array[#{value.map { |elem| quote_value(elem) }.join(', ')}]"
88
+ escape_literal(value.to_s)
84
89
  end
85
- when Hash; ["'#{value.to_json}'", json_type].compact.join('::')
86
- else
87
- escape_literal(value.to_s)
88
- end
89
90
  end
90
91
 
91
92
  # Quote values and concatenate them using ',' as separator
92
93
  def self.quote_values(values, **opts)
93
- values.map { |value| quote_value(value, **opts) }.join(", ")
94
+ Literal.new values.map { |value| quote_value(value, **opts) }.join(", ")
94
95
  end
95
96
 
96
97
  # Quote an array of values as a tuple. The element types should be in the
@@ -101,7 +102,7 @@ module PgConn
101
102
  # Note that it is :elem_types (plural) and not :elem_type
102
103
  def self.quote_tuple(tuple, elem_types: nil, **opts)
103
104
  elem_types = Array(elem_types)
104
- tuple.map { |value|
105
+ Literal.new tuple.map { |value|
105
106
  elem_type = value.is_a?(Array) ? elem_types&.shift : nil
106
107
  quote_value(value, **opts, elem_type: elem_type)
107
108
  }.join(", ")
@@ -109,7 +110,7 @@ module PgConn
109
110
 
110
111
  # Quote an array of tuples
111
112
  def self.quote_tuples(tuples, **opts)
112
- tuples.map { |tuple| "(#{quote_tuple(tuple, **opts)})" }.join(", ")
113
+ Literal.new tuples.map { |tuple| "(#{quote_tuple(tuple, **opts)})" }.join(", ")
113
114
  end
114
115
 
115
116
  # Used to mark strings as literals that should not be quoted. This is the
@@ -132,7 +133,7 @@ module PgConn
132
133
 
133
134
  # Name of user
134
135
  def user() @pg_connection.user end
135
- alias_method :username, :user # Obsolete FIXME Is it?
136
+ alias_method :username, :user # Obsolete FIXME Is it? No it is not!
136
137
 
137
138
  # Name of database
138
139
  def name() @pg_connection.db end
@@ -262,12 +263,13 @@ module PgConn
262
263
  # The possible keys of the connection hash are :host, :port, :dbname, :user,
263
264
  # and :password. The connection string can either be a space-separated list
264
265
  # of <key>=<value> pairs with the same keys as the hash, or a URI with the
265
- # format 'postgres[ql]://[user[:password]@][host][:port][/name]
266
+ # format 'postgres[ql]://[user[:password]@][host][:port][/name]. TODO Also
267
+ # allow :database and :username
266
268
  #
267
269
  # If given an array argument, PgConn will not connect to the database and
268
270
  # instead write its commands to the array. In this case, methods extracting
269
271
  # values from the database (eg. #value) will return nil or raise an
270
- # exception
272
+ # exception. TODO: Remove
271
273
  #
272
274
  # The last variant is used to establish a PgConn from an existing
273
275
  # connection. It doesn't change the connection settings and is not
@@ -429,8 +431,8 @@ module PgConn
429
431
  end
430
432
  end
431
433
 
432
- # Mark argument as already being quoted. TODO: Make this the default in all
433
- # quote methods
434
+ # Mark string argument as already being quoted. This is done automatically
435
+ # by all quote_* methods
434
436
  def literal(arg) Literal.new(arg) end
435
437
 
436
438
  # Connection member method variations of the PgConn quote class methods
@@ -784,6 +786,8 @@ module PgConn
784
786
  # There is no variant that takes a single tuple because it would then be
785
787
  # impossible to have array or hash field values
786
788
  def insert(*args, upsert: nil, **opts)
789
+ # Normalize arguments
790
+
787
791
  # Add options to args except the special :upsert option
788
792
  args << opts if !opts.empty?
789
793
 
@@ -1091,6 +1095,43 @@ module PgConn
1091
1095
  end
1092
1096
  end
1093
1097
 
1098
+ # Return true if session triggers are enabled. Triggers are enabled by
1099
+ # default by Postgres
1100
+ def triggers?() self.value "select current_setting('session_replication_role') <> 'replica'" end
1101
+
1102
+ # Enable session triggers
1103
+ def enable_triggers()
1104
+ self.execute "set session session_replication_role = DEFAULT"
1105
+ end
1106
+
1107
+ # Disable session triggers
1108
+ def disable_triggers()
1109
+ self.execute "set session session_replication_role = replica"
1110
+ end
1111
+
1112
+ # Executes block without triggers
1113
+ def without_triggers(&block)
1114
+ triggers(false) { yield }
1115
+ end
1116
+
1117
+ # Execute block with session triggers on or off
1118
+ def triggers(on_off, &block)
1119
+ begin
1120
+ active = triggers?
1121
+ if on_off && !active
1122
+ enable_triggers
1123
+ elsif !on_off && active
1124
+ disable_triggers
1125
+ end
1126
+ yield
1127
+ ensure
1128
+ case active
1129
+ when true; enable_triggers if !triggers?
1130
+ when false; disable_triggers if triggers?
1131
+ end
1132
+ end
1133
+ end
1134
+
1094
1135
  def dump(*query)
1095
1136
  records = self.records(*query)
1096
1137
 
@@ -1155,17 +1196,16 @@ module PgConn
1155
1196
  end
1156
1197
 
1157
1198
  # Common implementation for #quote_record and #quote_records that avoids
1158
- # querying the database multiple times or duplication the code. The :array
1159
- # flag is true when called via #quote_records
1199
+ # querying the database multiple times or duplication the code
1160
1200
  #
1161
1201
  # @data can be a Hash, Array, or OpenStruct. Hash keys must be symbols or
1162
1202
  # strings, they belong to the same namespace so :k and "k" refer to the
1163
- # same value
1203
+ # same value. The :array flag is true when called via #quote_records
1164
1204
  #
1165
1205
  # Note that #quote_record_impl queries the database for information about
1166
1206
  # the type. TODO Cache this information?
1167
- def quote_record_impl(
1168
- datas, schema_name = nil, type, elem_types: nil, array: nil, **opts)
1207
+ #
1208
+ def quote_record_impl(datas, schema_name = nil, type, elem_types: nil, array: nil, **opts)
1169
1209
  datas = [datas] if !array
1170
1210
 
1171
1211
  pg_type = [schema_name, type].compact.join('.')
@@ -1192,10 +1232,9 @@ module PgConn
1192
1232
  }
1193
1233
 
1194
1234
  if array
1195
- # papg_type(table, where_clause, fields...)
1196
- "array[#{literals.join(', ')}]::#{pg_type}[]"
1235
+ Literal.new "array[#{literals.join(', ')}]::#{pg_type}[]"
1197
1236
  else
1198
- literals.first
1237
+ Literal.new literals.first
1199
1238
  end
1200
1239
  end
1201
1240
 
@@ -1235,7 +1274,7 @@ module PgConn
1235
1274
  end
1236
1275
 
1237
1276
  STDOUT_PRODUCER = lambda { |msg| $stdout.puts msg }
1238
- STDERR_PRODUCER = lambda { |msg| $stderr.puts msg }
1277
+ STDERR_PRODUCER = lambda { |msg| $stderr.puts msg; $stderr.flush }
1239
1278
  ERROR_PRODUCER = lambda { |msg, stmt| $stderr.puts stmt, nil, msg; $stderr.flush }
1240
1279
 
1241
1280
  # Map from message level to default producer. Note that we rely on the key
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.35.1
4
+ version: 0.37.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: 2025-03-18 00:00:00.000000000 Z
11
+ date: 2025-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg