pg_conn 0.21.0 → 0.23.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/TODO +2 -1
- data/lib/pg_conn/rdbms_methods.rb +3 -3
- data/lib/pg_conn/role_methods.rb +3 -3
- data/lib/pg_conn/schema_methods.rb +7 -7
- data/lib/pg_conn/version.rb +1 -1
- data/lib/pg_conn.rb +47 -18
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7de75668042e3abdc5b983e0b9a3f6c909ff69aeed2f22c632d1c693f3baac0
|
4
|
+
data.tar.gz: 90cecbdd79e6478d7f6bd1bc3ae042917bc2683771c89de61aab6c05fa2e5279
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76b775138260668bf75e5bc31e220561f665dffc901349a9ec875e7ec594dee2750e0d979bde80e9045e38e44a3894fe55c2be37faefbe7f35173812cea2a22a
|
7
|
+
data.tar.gz: f0e2e9169cd734814cce4e8b73f7ec017cf3fd9a2622290c4acbf06620ddb3b943957ab5dab527ee0e29a9d71ad4efb511f4f219543ca86301d9fd5c4f5838ad
|
data/TODO
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
TODO
|
2
|
-
o
|
2
|
+
o Use :elem_type everywhere
|
3
|
+
o Use 'drop ... cascade' everywhere
|
3
4
|
o db.context(schema: app_portal, transaction: true) { ... }
|
4
5
|
db.context(set: app_portal, transaction: true) { ... }
|
5
6
|
db.context(add: app_portal, transaction: true) { ... }
|
@@ -21,7 +21,7 @@ module PgConn
|
|
21
21
|
end
|
22
22
|
|
23
23
|
# Create a new database
|
24
|
-
def create(database, owner: ENV['USER'], template: "template1")
|
24
|
+
def create(database, owner: ENV['USER'], template: "template1")
|
25
25
|
owner_clause = owner ? "owner = \"#{owner}\"" : nil
|
26
26
|
template_clause = template ? "template = \"#{template}\"" : nil
|
27
27
|
stmt = ["create database \"#{database}\"", owner_clause, template_clause].compact.join(" ")
|
@@ -80,7 +80,7 @@ module PgConn
|
|
80
80
|
local = !database.nil?
|
81
81
|
begin
|
82
82
|
conn = local ? PgConn.new(database) : self.conn
|
83
|
-
schemas =
|
83
|
+
schemas =
|
84
84
|
conn
|
85
85
|
.values("select nspname from pg_namespace where nspowner != 10 or nspname = 'public'")
|
86
86
|
.select { |schema| !exclude.include?(schema) }
|
@@ -90,7 +90,7 @@ module PgConn
|
|
90
90
|
# FIXME FIXME FIXME SECURITY Why grant 'create' to public?
|
91
91
|
conn.exec %(
|
92
92
|
create schema public authorization postgres;
|
93
|
-
grant usage, create on schema public to public
|
93
|
+
grant usage, create on schema public to public
|
94
94
|
) if public
|
95
95
|
ensure
|
96
96
|
conn&.terminate if local
|
data/lib/pg_conn/role_methods.rb
CHANGED
@@ -18,7 +18,7 @@ module PgConn
|
|
18
18
|
else
|
19
19
|
nil
|
20
20
|
end
|
21
|
-
can_login_clause =
|
21
|
+
can_login_clause =
|
22
22
|
case can_login
|
23
23
|
when true; "rolcanlogin"
|
24
24
|
when false; "not rolcanlogin"
|
@@ -62,7 +62,7 @@ module PgConn
|
|
62
62
|
# counldn't be deleted
|
63
63
|
#
|
64
64
|
# Note that cascade only works if connected to the database where the
|
65
|
-
# privileges exist.
|
65
|
+
# privileges exist.
|
66
66
|
#
|
67
67
|
# TODO The :silent option is used in tests - fix it somehow!
|
68
68
|
def drop(*rolenames, cascade: false, fail: true, silent: false)
|
@@ -86,7 +86,7 @@ module PgConn
|
|
86
86
|
superuser_clause = superuser.nil? ? nil : "rolsuper = #{superuser}"
|
87
87
|
can_login_clause = can_login.nil? ? nil : "rolcanlogin = #{can_login}"
|
88
88
|
query = [
|
89
|
-
"select rolname from pg_roles where true",
|
89
|
+
"select rolname from pg_roles where true",
|
90
90
|
database_clause, superuser_clause, can_login_clause
|
91
91
|
].compact.join(" and ")
|
92
92
|
conn.values(query)
|
@@ -49,8 +49,8 @@ module PgConn
|
|
49
49
|
# Empty all tables in the given schema
|
50
50
|
def clean!(schema, exclude: [])
|
51
51
|
conn.session.triggers(false) {
|
52
|
-
self.list_tables(schema, exclude: exclude).each { |table|
|
53
|
-
conn.exec "delete from #{schema}.#{table}"
|
52
|
+
self.list_tables(schema, exclude: exclude).each { |table|
|
53
|
+
conn.exec "delete from #{schema}.#{table}"
|
54
54
|
}
|
55
55
|
}
|
56
56
|
end
|
@@ -125,8 +125,8 @@ module PgConn
|
|
125
125
|
end
|
126
126
|
|
127
127
|
# Return name of the table's sequence (if any)
|
128
|
-
def sequence(schema, table)
|
129
|
-
conn.value "select pg_get_serial_sequence('#{schema}.#{table}', 'id')"
|
128
|
+
def sequence(schema, table)
|
129
|
+
conn.value "select pg_get_serial_sequence('#{schema}.#{table}', 'id')"
|
130
130
|
end
|
131
131
|
|
132
132
|
# Get the current serial value for the table. Returns nil if the serial has
|
@@ -168,7 +168,7 @@ module PgConn
|
|
168
168
|
kind_sql_list = "'" + (kind.nil? ? %w(r f v m) : Array(kind).flatten).join("', '") + "'"
|
169
169
|
%(
|
170
170
|
select 1
|
171
|
-
from pg_class
|
171
|
+
from pg_class
|
172
172
|
where relnamespace::regnamespace::text = '#{schema}'
|
173
173
|
and relname = '#{relation}'
|
174
174
|
and relkind in (#{kind_sql_list})
|
@@ -183,7 +183,7 @@ module PgConn
|
|
183
183
|
exclude_expr = exclude.empty? ? "true = true" : "not relname in (#{exclude_list})"
|
184
184
|
%(
|
185
185
|
select relname
|
186
|
-
from pg_class
|
186
|
+
from pg_class
|
187
187
|
where relnamespace::regnamespace::text = '#{schema}'
|
188
188
|
and #{kind_expr}
|
189
189
|
and #{exclude_expr}
|
@@ -211,7 +211,7 @@ module PgConn
|
|
211
211
|
join pg_attribute a on a.attrelid = c.oid
|
212
212
|
where relnamespace::regnamespace::text = '#{schema}'
|
213
213
|
and a.attnum > 0
|
214
|
-
),
|
214
|
+
),
|
215
215
|
relation_clause
|
216
216
|
].compact.join(" and ")
|
217
217
|
end
|
data/lib/pg_conn/version.rb
CHANGED
data/lib/pg_conn.rb
CHANGED
@@ -239,12 +239,16 @@ module PgConn
|
|
239
239
|
end
|
240
240
|
end
|
241
241
|
|
242
|
-
# Quote
|
242
|
+
# Quote argument as an identifier. The argument should be a non-nil string
|
243
|
+
# or a symbol
|
243
244
|
def quote_identifier(s)
|
244
245
|
s = s.to_s if s.is_a?(Symbol)
|
245
246
|
@pg_connection.escape_identifier(s)
|
246
247
|
end
|
247
248
|
|
249
|
+
# Quote identifiers and concatenate them using ',' as separator
|
250
|
+
def quote_identifiers(idents) = idents.map { |ident| quote_identifier(ident) }.join(", ")
|
251
|
+
|
248
252
|
# Quote the value as a string. Emit 'null' if value is nil
|
249
253
|
#
|
250
254
|
# The value can be of any type but is converted to a string using #to_s
|
@@ -255,7 +259,13 @@ module PgConn
|
|
255
259
|
# Note that a tuple value (an array) must be quoted using #quote_tuple
|
256
260
|
# because #quote_value would quote the tuple as an array instead of a list
|
257
261
|
# of values
|
258
|
-
|
262
|
+
#
|
263
|
+
# The :elem_type option can be a postgres type name (String or Symbol) or
|
264
|
+
# an array of type names. They are used as the required explicit element
|
265
|
+
# type when the argument is an empty array. The element types shoud be in
|
266
|
+
# the same order as the array arguments. Nested arrays is not supported
|
267
|
+
#
|
268
|
+
def quote_value(value, elem_type: nil)
|
259
269
|
case value
|
260
270
|
when String; @pg_connection.escape_literal(value)
|
261
271
|
when Integer, Float; value.to_s
|
@@ -263,23 +273,34 @@ module PgConn
|
|
263
273
|
when nil; 'null'
|
264
274
|
when Date, DateTime; "'#{value}'"
|
265
275
|
when Time; "'#{value.strftime("%FT%T%:z")}'"
|
266
|
-
when Array
|
276
|
+
when Array
|
277
|
+
if value.empty?
|
278
|
+
elem_type or raise Error, "Empty array without elem_type"
|
279
|
+
"array[]::#{elem_type}[]"
|
280
|
+
else
|
281
|
+
"array[#{value.map { |elem| quote_value(elem) }.join(', ')}]"
|
282
|
+
end
|
267
283
|
else
|
268
284
|
@pg_connection.escape_literal(value.to_s)
|
269
285
|
end
|
270
286
|
end
|
271
287
|
|
272
|
-
# Quote an array of values as a tuple. Just an alias for #quote_values
|
273
|
-
def quote_tuple(tuple) = quote_values(tuple)
|
274
|
-
|
275
|
-
# Quote identifiers and concatenate them using ',' as separator
|
276
|
-
def quote_identifiers(idents) = idents.map { |ident| quote_identifier(ident) }.join(", ")
|
277
|
-
|
278
288
|
# Quote values and concatenate them using ',' as separator
|
279
|
-
def quote_values(values
|
289
|
+
def quote_values(values, elem_type: nil)
|
290
|
+
elem_types = Array(elem_type)
|
291
|
+
values.map { |value|
|
292
|
+
elem_type = value.is_a?(Array) ? elem_types&.shift : nil
|
293
|
+
quote_value(value, elem_type: elem_type)
|
294
|
+
}.join(", ")
|
295
|
+
end
|
296
|
+
|
297
|
+
# Quote an array of values as a tuple. Just an alias for #quote_values
|
298
|
+
def quote_tuple(tuple, elem_type: nil) = quote_values(tuple, elem_type: elem_type)
|
280
299
|
|
281
300
|
# Quote an array of tuples
|
282
|
-
def quote_tuples(tuples
|
301
|
+
def quote_tuples(tuples, elem_type: nil)
|
302
|
+
tuples.map { |tuple| "(#{quote_values(tuple, elem_type: elem_type)})" }.join(", ")
|
303
|
+
end
|
283
304
|
|
284
305
|
# :call-seq:
|
285
306
|
# exist?(query)
|
@@ -515,8 +536,9 @@ module PgConn
|
|
515
536
|
|
516
537
|
# Returns a hash from the first field to a tuple of the remaining fields.
|
517
538
|
# If there is only one remaining field then that value is used instead of a
|
518
|
-
# tuple
|
519
|
-
|
539
|
+
# tuple. The optional +key+ argument sets the mapping field and the
|
540
|
+
# +symbol+ option convert key to Symbol objects when true
|
541
|
+
def map(query, key = nil, symbol: false) # TODO Swap arguments
|
520
542
|
r = pg_exec(query)
|
521
543
|
begin
|
522
544
|
key = (key || r.fname(0)).to_s
|
@@ -528,13 +550,18 @@ module PgConn
|
|
528
550
|
h = {}
|
529
551
|
r.each_row { |row|
|
530
552
|
key_value = row.delete_at(key_index)
|
553
|
+
key_value = key_value.to_sym if symbol
|
531
554
|
!h.key?(key_value) or raise Error, "Duplicate key: #{key_value}"
|
532
555
|
h[key_value] = (one ? row.first : row)
|
533
556
|
}
|
534
557
|
h
|
535
558
|
end
|
536
559
|
|
537
|
-
|
560
|
+
# Like #map but values of duplicate keys are concatenated. It acts as a
|
561
|
+
# group-by on the key and array_agg on the remaining values. The value is
|
562
|
+
# an array of tuples if the query has more than one value field and an
|
563
|
+
# array of values if there is only one value field
|
564
|
+
def multimap(query, key = nil, symbol: false)
|
538
565
|
r = pg_exec(query)
|
539
566
|
begin
|
540
567
|
key = (key || r.fname(0)).to_s
|
@@ -546,6 +573,7 @@ module PgConn
|
|
546
573
|
h = {}
|
547
574
|
r.each_row { |row|
|
548
575
|
key_value = row.delete_at(key_index)
|
576
|
+
key_value = key_value.to_sym if symbol
|
549
577
|
(h[key_value] ||= []) << (one ? row.first : row)
|
550
578
|
}
|
551
579
|
h
|
@@ -557,10 +585,11 @@ module PgConn
|
|
557
585
|
# values if the result contained only one column (like #value or #values),
|
558
586
|
# a tuple if the record has multiple columns (like #tuple), and an array of
|
559
587
|
# of tuples if the result contained more than one record with multiple
|
560
|
-
# columns (like #tuples)
|
588
|
+
# columns (like #tuples). If the :proc option is true the "function" is
|
589
|
+
# assumed to be a procedure
|
561
590
|
#
|
562
|
-
def call(name, *args, proc: false) # :proc may interfere with hashes
|
563
|
-
args_seq = quote_values(args)
|
591
|
+
def call(name, *args, elem_type: nil, proc: false) # :proc may interfere with hashes
|
592
|
+
args_seq = quote_values(args, elem_type: elem_type)
|
564
593
|
if proc
|
565
594
|
pg_exec "call #{name}(#{args_seq})"
|
566
595
|
return nil
|
@@ -582,7 +611,7 @@ module PgConn
|
|
582
611
|
end
|
583
612
|
end
|
584
613
|
|
585
|
-
# :call-seq
|
614
|
+
# :call-seq:
|
586
615
|
# insert(table, record|records)
|
587
616
|
# insert(table, fields, record|records|tuples)
|
588
617
|
# insert(schema, table, record|records)
|
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.
|
4
|
+
version: 0.23.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-08-
|
11
|
+
date: 2024-08-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|