pg_conn 0.20.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
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