pg_conn 0.30.0 → 0.32.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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pg_conn/version.rb +1 -1
  3. data/lib/pg_conn.rb +134 -73
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 28c2a6429f9c8e987f27686e323643baf77f4429599bfb457ecdd6d5b659a289
4
- data.tar.gz: e8e2c96d92401a3eceff86b77152acdfe9019c03306577d260c6039d39881630
3
+ metadata.gz: e4afbef1cc74d235f8434c2c92a6537ec24b37cd36e85bb9f07eb6153e6c7938
4
+ data.tar.gz: cd5af7434cb01ae1f873a7cb18a4cc350e425976c695b93252f2e36cefca2273
5
5
  SHA512:
6
- metadata.gz: b6f08ca6bd0b47206623435dbf6691f0d8276fa5a8ac1c71fcd95a0a763356333528afa0013622665cc0ac5f94960e0f7231abe7c688bfa081bc82034e6fd8ed
7
- data.tar.gz: 1406fdffcb6d44b7d7d0d0eab5fb2d970e501ff90336b88ed80962680021a845753463e14348203123ca612531248f1aab88ebbe0a3ec7cb30d8eb887e4b1ceb
6
+ metadata.gz: c94d25247ae2167f022a704bdf5b03691f87691dfe88c5187a5cbf2fb117c5ac252c3f649b5b0a20578cfe325b08cfff7f09e41da1d8f6c0bbf81c11cab501bf
7
+ data.tar.gz: 689be40d28cb3a7ea0c84ab0109b7dc7749b16c47a8240b694ae746ff8602ebed6e5fa4394dc628d1df699a6c7c091db7f345c1ffb81cfc93e81e98d48c60ea0
@@ -1,3 +1,3 @@
1
1
  module PgConn
2
- VERSION = "0.30.0"
2
+ VERSION = "0.32.0"
3
3
  end
data/lib/pg_conn.rb CHANGED
@@ -32,6 +32,84 @@ module PgConn
32
32
  end
33
33
  end
34
34
 
35
+ # Quote argument as an identifier. The argument should be a non-nil string
36
+ # or a symbol
37
+ #
38
+ # Quote metods are both defined as PgConn class methods and as Connection
39
+ # member methods
40
+ #
41
+ def self.quote_identifier(s)
42
+ s = s.to_s if s.is_a?(Symbol)
43
+ escape_identifier(s).gsub(/\./, '"."').sub(/"\*"/, "*")
44
+ end
45
+
46
+ # Quote identifiers and concatenate them using ',' as separator
47
+ def self.quote_identifiers(idents) = idents.map { |ident| quote_identifier(ident) }.join(", ")
48
+
49
+ # Quote the value as a string. Emit 'null' if value is nil
50
+ #
51
+ # The value can be of any type but is converted to a string using #to_s
52
+ # before quoting. This works by default for the regular types Integer,
53
+ # true/false, Time/Date/DateTime, and arrays. Other types may require
54
+ # special handling
55
+ #
56
+ # Hashes are quoted as a literal JSON expression. The result is a string
57
+ # and it is the application's responsibility to cast them to either 'json'
58
+ # or 'jsonb'
59
+ #
60
+ # Note that a tuple value (an array) must be quoted using #quote_tuple
61
+ # because #quote_value would quote the tuple as an array instead of a list
62
+ # of values
63
+ #
64
+ # The :elem_type option can be a postgres type name (String or Symbol) or
65
+ # an array of type names. It is used as the required explicit element
66
+ # type when the argument is an empty array. It is not needed if the array
67
+ # is guaranteed to be non-empty. Nested arrays are not supported
68
+ #
69
+ def self.quote_value(value, elem_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}[]"
82
+ else
83
+ "array[#{value.map { |elem| quote_value(elem) }.join(', ')}]"
84
+ end
85
+ when Hash; "'#{value.to_json}'"
86
+ else
87
+ escape_literal(value.to_s)
88
+ end
89
+ end
90
+
91
+ # Quote values and concatenate them using ',' as separator
92
+ def self.quote_values(values, elem_type: nil)
93
+ values.map { |value| quote_value(value, elem_type: elem_type) }.join(", ")
94
+ end
95
+
96
+ # Quote an array of values as a tuple. The element types should be in the
97
+ # same order as the array arguments. #quote_tuples is same as #quote_values
98
+ # except the values may have different types (this makes no difference
99
+ # except in the case when the tuple may contain empty array(s))
100
+ def self.quote_tuple(tuple, elem_types: nil)
101
+ elem_types = Array(elem_types)
102
+ tuple.map { |value|
103
+ elem_type = value.is_a?(Array) ? elem_types&.shift : nil
104
+ quote_value(value, elem_type: elem_type)
105
+ }.join(", ")
106
+ end
107
+
108
+ # Quote an array of tuples
109
+ def self.quote_tuples(tuples, elem_types: nil)
110
+ tuples.map { |tuple| "(#{quote_tuple(tuple, elem_types: elem_types)})" }.join(", ")
111
+ end
112
+
35
113
  # Used to mark strings as literals that should not be quoted. This is the
36
114
  # case for row and record values
37
115
  class Literal < String; end
@@ -253,79 +331,13 @@ module PgConn
253
331
 
254
332
  def literal(arg) Literal.new(arg) end
255
333
 
256
- # Quote argument as an identifier. The argument should be a non-nil string
257
- # or a symbol
258
- def quote_identifier(s)
259
- s = s.to_s if s.is_a?(Symbol)
260
- @pg_connection.escape_identifier(s).gsub(/\./, '"."').sub(/"\*"/, "*")
261
- end
262
-
263
- # Quote identifiers and concatenate them using ',' as separator
264
- def quote_identifiers(idents) = idents.map { |ident| quote_identifier(ident) }.join(", ")
265
-
266
- # Quote the value as a string. Emit 'null' if value is nil
267
- #
268
- # The value can be of any type but is converted to a string using #to_s
269
- # before quoting. This works by default for the regular types Integer,
270
- # true/false, Time/Date/DateTime, and arrays. Other types may require
271
- # special handling
272
- #
273
- # Hashes are quoted as a literal JSON expression. The result is a string
274
- # and it is the application's responsibility to cast them to either 'json'
275
- # or 'jsonb'
276
- #
277
- # Note that a tuple value (an array) must be quoted using #quote_tuple
278
- # because #quote_value would quote the tuple as an array instead of a list
279
- # of values
280
- #
281
- # The :elem_type option can be a postgres type name (String or Symbol) or
282
- # an array of type names. It is used as the required explicit element
283
- # type when the argument is an empty array. It is not needed if the array
284
- # is guaranteed to be non-empty. Nested arrays are not supported
285
- #
286
- def quote_value(value, elem_type: nil)
287
- case value
288
- when Literal; value
289
- when String; @pg_connection.escape_literal(value)
290
- when Integer, Float; value.to_s
291
- when true, false; value.to_s
292
- when nil; 'null'
293
- when Date, DateTime; "'#{value}'"
294
- when Time; "'#{value.strftime("%FT%T%:z")}'"
295
- when Array
296
- if value.empty?
297
- elem_type or raise Error, "Empty array without elem_type"
298
- "array[]::#{elem_type}[]"
299
- else
300
- "array[#{value.map { |elem| quote_value(elem) }.join(', ')}]"
301
- end
302
- when Hash; "'#{value.to_json}'"
303
- else
304
- @pg_connection.escape_literal(value.to_s)
305
- end
306
- end
307
-
308
- # Quote values and concatenate them using ',' as separator
309
- def quote_values(values, elem_type: nil)
310
- values.map { |value| quote_value(value, elem_type: elem_type) }.join(", ")
311
- end
312
-
313
- # Quote an array of values as a tuple. The element types should be in the
314
- # same order as the array arguments. #quote_tuples is same as #quote_values
315
- # except the values may have different types (this makes no difference
316
- # except in the case when the tuple may contain empty array(s))
317
- def quote_tuple(tuple, elem_types: nil)
318
- elem_types = Array(elem_types)
319
- tuple.map { |value|
320
- elem_type = value.is_a?(Array) ? elem_types&.shift : nil
321
- quote_value(value, elem_type: elem_type)
322
- }.join(", ")
323
- end
324
-
325
- # Quote an array of tuples
326
- def quote_tuples(tuples, elem_types: nil)
327
- tuples.map { |tuple| "(#{quote_tuple(tuple, elem_types: elem_types)})" }.join(", ")
328
- end
334
+ # Connection member method variations of the PgConn quote class methods
335
+ def quote_identifier(s) = PgConn.quote_identifier(s)
336
+ def quote_identifiers(idents) = PgConn.quote_identifiers(idents)
337
+ def quote_value(value, **opts) = PgConn.quote_value(value, **opts)
338
+ def quote_values(values, **opts) = PgConn.quote_values(values, **opts)
339
+ def quote_tuple(tuple, **opts) = PgConn.quote_tuple(tuple, **opts)
340
+ def quote_tuples(tuples, **opts) = PgConn.quote_tuples(tuples, **opts)
329
341
 
330
342
  # Quote a record and cast it into the given type, the type can also be a
331
343
  # table or view. 'data' is an array, hash, or struct representation of the
@@ -335,6 +347,9 @@ module PgConn
335
347
  # as fast as the other quote-methods. It is however very convenient when
336
348
  # you're testing and need a composite type because record-quoting can
337
349
  # easily become unwieldly
350
+ #
351
+ # Also note that there is not class-method variant of this method because
352
+ # it requires a connection
338
353
  def quote_record(data, schema_name = nil, type, elem_types: nil)
339
354
  quote_record_impl(data, schema_name, type, elem_types: elem_types, array: false)
340
355
  end
@@ -943,6 +958,40 @@ module PgConn
943
958
  end
944
959
  end
945
960
 
961
+ def dump(*query)
962
+ records = self.records(*query)
963
+
964
+ if records.empty?
965
+ puts "No records found"
966
+ else
967
+ headers = records.first.keys
968
+ column_widths = headers.map(&:size)
969
+ column_signs = [nil] * headers.size
970
+
971
+ records.each { |r|
972
+ r.values.each.with_index { |v, i|
973
+ value_width = v.to_s.size
974
+ column_widths[i] = [column_widths[i], value_width].max
975
+
976
+ column_signs[i] ||=
977
+ case v
978
+ when nil; nil
979
+ when Integer; ""
980
+ else
981
+ "-"
982
+ end
983
+ }
984
+ }
985
+
986
+ header_format = column_widths.map { |w,t| "%-#{w}s" }.join(" ")
987
+ body_format = column_widths.zip(column_signs).map { |w,s| "%#{s}#{w}s" }.join(" ")
988
+
989
+ printf "#{header_format}\n", *headers
990
+ printf "#{header_format}\n", *column_widths.map { |w| "-" * w }
991
+ records.each { |r| printf "#{body_format}\n", *r.values }
992
+ end
993
+ end
994
+
946
995
  private
947
996
  # Wrapper around PG::Connection.new that switches to the postgres user
948
997
  # before connecting if the current user is the root user
@@ -1123,5 +1172,17 @@ module PgConn
1123
1172
 
1124
1173
  def self.sql_values(values) "'" + values.join("', '") + "'" end
1125
1174
  def self.sql_idents(values) '"' + values.join('", "') + '"' end
1175
+
1176
+ # Same as postgres PG#escape_literal but do not need a connection
1177
+ def self.escape_literal(s) = s.nil? ? 'NULL' : "'#{s.gsub("'", "''")}'"
1178
+
1179
+ # Same as postgres PG#escape_identifier but do not need a connection
1180
+ def self.escape_identifier(s)
1181
+ !s.nil? or raise TypeError, "Identifier can't be nil"
1182
+ s.is_a?(String) or raise TypeError, "Identifier can't be a #{s.class}"
1183
+ s !~ /\A\s*\z/ or raise TypeError, "Identifier be blank"
1184
+ %("#{s.gsub('"', '""')}")
1185
+ end
1186
+
1126
1187
  end
1127
1188
 
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.30.0
4
+ version: 0.32.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-01-11 00:00:00.000000000 Z
11
+ date: 2025-01-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg