pg_conn 0.20.0 → 0.21.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 (5) hide show
  1. checksums.yaml +4 -4
  2. data/TODO +1 -0
  3. data/lib/pg_conn/version.rb +1 -1
  4. data/lib/pg_conn.rb +51 -73
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfe715a08cebb48bb84da6b467a54258e05a774cabfcf7dc0b5492a4059b9f6f
4
- data.tar.gz: 26753d7a87a7a6bb7cb91e9e88ed06e08161e9893f739311bae34cbbba9faffc
3
+ metadata.gz: 6f293ad25581975cbd57dc5973d072043d6b5cf13093a21bd354720b39c91c12
4
+ data.tar.gz: 1ad472d136a9b626717f90058cb220eaaf4c87bced2f8802c8cab68b69bf187a
5
5
  SHA512:
6
- metadata.gz: a54dc14b754e355a2e71a3c23a00b89c334afe33c721334f08ce937f285978719541605ef972a4b55a7fbdbd2fd498f5cd1ce39535704b2385b112568a5c6b4b
7
- data.tar.gz: 89bdf2c5d23f3f0f8ce4d0973f982a998523ea9b1c496dff20ccd995c6f19abc1faa9e789dbec07b14b29ba6af5e06af4bd2689e106a59cdf75475caba8dbb44
6
+ metadata.gz: 7720aef7f128e0b8e3c47fdd2acb752b1d44af9625653eae0552a2ac619e3c29ae2cad25789f0f0806fe4576104a64210cf5a5d7e7c7fa66817b8538a556bd35
7
+ data.tar.gz: 61425eaeabb6b6e0872f6ed49064f8650361ecd8be684b7db2322a6a346265e253489e49be37688c9ef74171733d2c8f19a12d38105a188ba263cf5ea48c9593
data/TODO CHANGED
@@ -1,4 +1,5 @@
1
1
  TODO
2
+ o "use 'drop ... cascade' everywhere
2
3
  o db.context(schema: app_portal, transaction: true) { ... }
3
4
  db.context(set: app_portal, transaction: true) { ... }
4
5
  db.context(add: app_portal, transaction: true) { ... }
@@ -1,3 +1,3 @@
1
1
  module PgConn
2
- VERSION = "0.20.0"
2
+ VERSION = "0.21.0"
3
3
  end
data/lib/pg_conn.rb CHANGED
@@ -67,7 +67,7 @@ module PgConn
67
67
  # #exec or #transaction block. The timestamp is without time zone
68
68
  attr_reader :timestamp
69
69
 
70
- # The transaction timestamp of the most recent SQL statement executed by
70
+ # The transaction timestamp of the most recent SQL statement executed by
71
71
  # #exec or #transaction block. The timestamp includes the current time zone
72
72
  attr_reader :timestamptz
73
73
 
@@ -190,7 +190,7 @@ module PgConn
190
190
  @pg_connection.set_notice_processor { |message| ; } # Intentionally a nop
191
191
 
192
192
  # Auto-convert to ruby types
193
- type_map = PG::BasicTypeMapForResults.new(@pg_connection)
193
+ type_map = PG::BasicTypeMapForResults.new(@pg_connection)
194
194
 
195
195
  # Use String as default type. Kills 'Warning: no type cast defined for
196
196
  # type "uuid" with oid 2950..' warnings
@@ -198,7 +198,7 @@ module PgConn
198
198
 
199
199
  # Timestamp decoder
200
200
  type_map.add_coder PG::TextDecoder::Timestamp.new( # Timestamp without time zone
201
- oid: 1114,
201
+ oid: 1114,
202
202
  flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_UTC)
203
203
 
204
204
  # Decode anonymous records but note that this is only useful to convert the
@@ -222,7 +222,7 @@ module PgConn
222
222
 
223
223
  # Close the database connection. TODO: Rename 'close'
224
224
  def terminate()
225
- @pg_connection.close if @pg_connection && !@pg_connection.finished?
225
+ @pg_connection.close if @pg_connection && !@pg_connection.finished?
226
226
  end
227
227
 
228
228
  def self.new(*args, **opts, &block)
@@ -251,7 +251,11 @@ module PgConn
251
251
  # before quoting. This works by default for the regular types Integer,
252
252
  # true/false, Time/Date/DateTime, and arrays. Other types may require
253
253
  # special handling
254
- def quote_value(value)
254
+ #
255
+ # Note that a tuple value (an array) must be quoted using #quote_tuple
256
+ # because #quote_value would quote the tuple as an array instead of a list
257
+ # of values
258
+ def quote_value(value)
255
259
  case value
256
260
  when String; @pg_connection.escape_literal(value)
257
261
  when Integer, Float; value.to_s
@@ -265,33 +269,17 @@ module PgConn
265
269
  end
266
270
  end
267
271
 
268
- # Quote array as a comma-separated sequence of identifiers
269
- def quote_identifier_seq(identifiers) = identifiers.map { quote_identifier(_1) }.join(', ')
270
-
271
- # Quote array as a parenthesis-enclosed sequence of identifiers. TODO: Rename quote_identifier_tuple
272
- def quote_identifier_list(identifiers) = "(#{quote_identifier_seq(identifiers)})"
273
-
274
- # Quote array as a bracket-enclosed sequence of identifiers
275
- # def quote_identifier_array(identifiers) = "(#{quote_identifier_seq(identifiers)})"
276
-
277
- # Quote array as a curly-bracket-enclosed sequence of identifiers
278
- # def quote_identifier_hash(identifiers) = "(#{quote_identifier_seq(identifiers)})"
272
+ # Quote an array of values as a tuple. Just an alias for #quote_values
273
+ def quote_tuple(tuple) = quote_values(tuple)
279
274
 
280
- # Quote array as a comma-separated sequence of quoted values
281
- def quote_value_seq(values) = values.map { quote_literal(_1) }.join(', ')
275
+ # Quote identifiers and concatenate them using ',' as separator
276
+ def quote_identifiers(idents) = idents.map { |ident| quote_identifier(ident) }.join(", ")
282
277
 
283
- # Quote array as a parenthesis-enclosed list of quoted values. TODO: Rename to quote_value_tuple
284
- def quote_value_list(values) = "(#{quote_value_seq(values)})"
278
+ # Quote values and concatenate them using ',' as separator
279
+ def quote_values(values) = values.map { |value| quote_value(value) }.join(", ")
285
280
 
286
- # Quote array as a bracket-enclosed sequence of values
287
- # def quote_identifier_array(values) = "(#{quote_identifier_seq(values)})"
288
-
289
- # Quote array as a curly-bracket-enclosed sequence of values
290
- # def quote_identifier_hash(values) = "(#{quote_identifier_seq(values)})"
291
-
292
- # Old aliases. TODO Remove
293
- def quote_literal(value) = quote_value(value)
294
- def quote_literal_list(values) = quote_value_list(values)
281
+ # Quote an array of tuples
282
+ def quote_tuples(tuples) = tuples.map { |tuple| "(#{quote_values(tuple)})" }.join(", ")
295
283
 
296
284
  # :call-seq:
297
285
  # exist?(query)
@@ -302,7 +290,7 @@ module PgConn
302
290
  # check if the query returns one or more records
303
291
  def exist?(*args)
304
292
  arg1, arg2 = *args
305
- query =
293
+ query =
306
294
  case arg2
307
295
  when Integer; "select from #{arg1} where id = #{arg2}"
308
296
  when String; "select from #{arg1} where #{arg2}"
@@ -316,7 +304,7 @@ module PgConn
316
304
  # count(table, where_clause = nil)
317
305
  #
318
306
  # Return true if the table or the result of the query is empty
319
- def empty?(arg, where_clause = nil)
307
+ def empty?(arg, where_clause = nil)
320
308
  if arg =~ /\s/
321
309
  value "select count(*) from (#{arg} limit 1) as inner_query"
322
310
  elsif where_clause
@@ -572,23 +560,12 @@ module PgConn
572
560
  # columns (like #tuples)
573
561
  #
574
562
  def call(name, *args, proc: false) # :proc may interfere with hashes
575
- args_sql = args.map { |arg| # TODO: Use pg's encoder
576
- case arg
577
- when NilClass; "null"
578
- when String; "'#{arg}'"
579
- when Integer; arg
580
- when TrueClass, FalseClass; arg
581
- when Array; "Array['#{arg.join("', '")}']" # Quick and dirty # FIXME
582
- when Hash; raise NotImplementedError
583
- else
584
- raise ArgumentError, "Unrecognized value: #{arg.inspect}"
585
- end
586
- }.join(", ")
563
+ args_seq = quote_values(args)
587
564
  if proc
588
- pg_exec "call #{name}(#{args_sql})"
565
+ pg_exec "call #{name}(#{args_seq})"
589
566
  return nil
590
567
  else
591
- r = pg_exec "select * from #{name}(#{args_sql})"
568
+ r = pg_exec "select * from #{name}(#{args_seq})"
592
569
  if r.ntuples == 0
593
570
  raise Error, "No records returned"
594
571
  elsif r.ntuples == 1
@@ -632,15 +609,14 @@ module PgConn
632
609
  table = schema ? "#{schema}.#{table}" : table
633
610
 
634
611
  # Find method and normalize data
635
- if data.is_a?(Array)
612
+ if data.is_a?(Array) # Array of tuples
613
+ method = :values
636
614
  if data.empty?
637
615
  return []
638
- elsif data.first.is_a?(Array)
639
- method = :values
616
+ elsif data.first.is_a?(Array) # Tuple (array) element. Requires the 'fields' argument
640
617
  fields or raise ArgumentError
641
618
  tuples = data
642
- elsif data.first.is_a?(Hash)
643
- method = :values
619
+ elsif data.first.is_a?(Hash) # Hash element
644
620
  fields ||= data.first.keys
645
621
  tuples = data.map { |record| fields.map { |field| record[field] } }
646
622
  else
@@ -654,21 +630,23 @@ module PgConn
654
630
  raise ArgumentError
655
631
  end
656
632
 
657
- # Build and execute SQL statement
658
- columns = quote_identifier_list(fields)
659
- values = tuples.map { |tuple| quote_literal_list(tuple) }.join(', ')
660
- self.send method, %(insert into #{table} #{columns} values #{values} returning id)
633
+ # Execute SQL statement using either :value or :values depending on data arity
634
+ self.send method, %(
635
+ insert into #{table} (#{quote_identifiers(fields)})
636
+ values #{quote_tuples(tuples)}
637
+ returning id
638
+ )
661
639
  end
662
640
 
663
641
  # Update record(s)
664
642
  def update(schema = nil, table, expr, hash)
665
643
  table = [schema, table].compact.join(".")
666
- assignments = hash.map { |k,v| "#{k} = #{quote_literal(v)}" }.join(", ")
667
- constraint =
644
+ assignments = hash.map { |k,v| "#{k} = #{quote_value(v)}" }.join(", ")
645
+ constraint =
668
646
  case expr
669
647
  when String; expr
670
- when Integer; "id = #{quote_literal(expr)}"
671
- when Array; "id in #{quote_literal_list(expr)}"
648
+ when Integer; "id = #{quote_value(expr)}"
649
+ when Array; "id in (#{quote_values(expr)})"
672
650
  else
673
651
  raise ArgumentError
674
652
  end
@@ -678,11 +656,11 @@ module PgConn
678
656
  # Delete record(s)
679
657
  def delete(schema = nil, table, expr)
680
658
  table = [schema, table].compact.join(".")
681
- constraint =
659
+ constraint =
682
660
  case expr
683
661
  when String; expr
684
- when Integer; "id = #{quote_literal(expr)}"
685
- when Array; "id in #{quote_literal_list(expr)}"
662
+ when Integer; "id = #{quote_value(expr)}"
663
+ when Array; "id in (#{quote_values(expr)})"
686
664
  else
687
665
  raise ArgumentError
688
666
  end
@@ -697,7 +675,7 @@ module PgConn
697
675
  # is not.
698
676
  #
699
677
  # #exec pass Postgres exceptions to the caller unless :fail is false in which case
700
- # it returns nil.
678
+ # it returns nil.
701
679
  #
702
680
  # Note that postgres crashes the whole transaction stack if any error is
703
681
  # met so if you're inside a transaction, the transaction will be in an
@@ -713,7 +691,7 @@ module PgConn
713
691
  # There is not a corresponding #execute? method because any failure rolls
714
692
  # back the whole transaction stack. TODO: Check which exceptions that
715
693
  # should be captured
716
- def exec?(sql, commit: true, silent: true)
694
+ def exec?(sql, commit: true, silent: true)
717
695
  begin
718
696
  exec(sql, commit: commit, fail: true, silent: silent)
719
697
  rescue PG::Error
@@ -748,7 +726,7 @@ module PgConn
748
726
  # back to the original user
749
727
  #
750
728
  # FIXME: The out-commented transaction block makes postspec fail for some reason
751
- # TODO: Rename 'sudo' because it acts just like it.
729
+ # TODO: Rename 'sudo' because it acts just like it.
752
730
  def su(username, &block)
753
731
  raise Error, "Missing block in call to PgConn::Connection#su" if !block_given?
754
732
  realuser = self.value "select current_user"
@@ -798,13 +776,13 @@ module PgConn
798
776
  pg_exec("begin")
799
777
  @error = @err = nil
800
778
  # FIXME This special-cases the situation where commands are logged to a
801
- # file instead of being executed. Maybe remove logging (or execute always
779
+ # file instead of being executed. Maybe remove logging (or execute always
802
780
  # and log as a side-effect)
803
781
  if @pg_connection
804
782
  @timestamp, @timestamptz = @pg_connection.exec(
805
783
  'select current_timestamp, current_timestamp::timestamp without time zone'
806
784
  ).tuple_values(0)
807
- end
785
+ end
808
786
  end
809
787
  end
810
788
 
@@ -941,7 +919,7 @@ module PgConn
941
919
  @error = ex
942
920
  @err = nil
943
921
  end
944
- if !silent # FIXME Why do we handle this?
922
+ if !silent # FIXME Why do we handle this?
945
923
  $stderr.puts arg
946
924
  $stderr.puts
947
925
  $stderr.puts ex.message
@@ -961,20 +939,20 @@ module PgConn
961
939
  end
962
940
  end
963
941
 
964
- def check_1c(r)
942
+ def check_1c(r)
965
943
  case r.nfields
966
944
  when 0; raise Error, "No columns returned"
967
- when 1;
945
+ when 1;
968
946
  else
969
- raise Error, "More than one column returned"
947
+ raise Error, "More than one column returned"
970
948
  end
971
949
  end
972
950
 
973
- def check_1r(r)
951
+ def check_1r(r)
974
952
  if r.ntuples == 0
975
953
  raise Error, "No records returned"
976
- elsif r.ntuples > 1
977
- raise Error, "More than one record returned"
954
+ elsif r.ntuples > 1
955
+ raise Error, "More than one record returned"
978
956
  end
979
957
  end
980
958
  end
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.20.0
4
+ version: 0.21.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-07-27 00:00:00.000000000 Z
11
+ date: 2024-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg