foobara-postgresql-crud-driver 0.0.4 → 0.0.5

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: c6ce8966a4872d4a3e81a1bcfec564df6be8903cd20e7bf8b998b9fc6ba423b8
4
- data.tar.gz: ead889be3b61d1293dd8c78ada43f3b2f3c68cbaa11af81ef6fbc3ce975fdc5f
3
+ metadata.gz: 9da1f2e3a8d5170f4badb5d1ec0eeb8b6f443da56ce17b4b97911767620ae099
4
+ data.tar.gz: b12f27eca628e62f38c7f8d2a1af415d1ab3c72006a18d17f983b4fbb03eea91
5
5
  SHA512:
6
- metadata.gz: 3d7bdb1286bc1a27ad62217f278155225722e88cb01c8faa193c80a932f1b07d441ef78bc0dac56ade1ce7e29c51d85e63f28e4b77c7913184ccd695a077caca
7
- data.tar.gz: a4509b915954600c4c7001e0e078856a8ef38063298877ff076cdfd94bf76c1b37b08a2488453e54084c7458189189a19881762406b4019a8d186e4de2f032b9
6
+ metadata.gz: d7d0d2d19bc3cd5d3c1625b7b4958b0f160f9c733bdac7adc3f39ec09010ddad3f7fe8ce02aa9a9d4c9c6a7bc8216a731c13d299209947cf7ebb8a327a35fdb6
7
+ data.tar.gz: 8f4c6fa9014c60caef166ecdff36a22e7d87543276d36604da42ec8f7e95ff421547256e1d4e58b02f7ac789c5e5070b3f5fe0b3ec500406a1780fec49426fc7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## [0.0.5] - 2025-11-10
2
+
3
+ - Improve error messages for foobara type/pg column incompatibilities
4
+ - Validate strings used as integers represent valid integers
5
+
1
6
  ## [0.0.4] - 2025-11-09
2
7
 
3
8
  - Support a few more column types
@@ -23,6 +23,23 @@ module Foobara
23
23
  end
24
24
  end
25
25
 
26
+ class ColumnTypeMismatchError < StandardError
27
+ attr_accessor :pg_type, :foobara_type, :entity_class, :attribute_name
28
+
29
+ def initialize(pg_type:, foobara_type:, entity_class:, attribute_name: nil)
30
+ # TODO: figure out a way to test this code path
31
+ # :nocov:
32
+ self.pg_type = pg_type
33
+ self.foobara_type = foobara_type
34
+ self.entity_class = entity_class
35
+ self.attribute_name = attribute_name
36
+
37
+ super("Column type mismatch between foobara #{foobara_type.type_symbol} " \
38
+ "and postgres #{pg_type} for #{entity_class.entity_name}.#{attribute_name}")
39
+ # :nocov:
40
+ end
41
+ end
42
+
26
43
  class << self
27
44
  def get_transaction_number
28
45
  @get_transaction_number ||= 0
@@ -237,6 +254,7 @@ module Foobara
237
254
  SQL
238
255
 
239
256
  raw_connection.exec(sql)
257
+
240
258
  find(record_id)
241
259
  end
242
260
 
@@ -323,6 +341,7 @@ module Foobara
323
341
  foobara_type = entity_class.model_type.element_types.element_types[attribute_name]
324
342
 
325
343
  value = begin
344
+ check_type_compatibility!(foobara_type, info, attribute_name:)
326
345
  pg_cast_value(value, foobara_type, info)
327
346
  rescue UnsupportedPgColumnTypeError => e
328
347
  # :nocov:
@@ -373,62 +392,86 @@ module Foobara
373
392
  # :nocov:
374
393
  end
375
394
  elsif foobara_type.extends?(:number)
376
- case pg_type
377
- when "integer", "bigint"
378
- value.to_i
395
+ validate_intable!(value)
396
+ value.to_i
397
+ elsif foobara_type.extends?(:string) || foobara_type.extends?(:symbol)
398
+ "'#{PG::Connection.escape(value.to_s)}'"
399
+ elsif foobara_type.extends?(:datetime)
400
+ "'#{PG::Connection.escape(value.inspect)}'"
401
+ elsif foobara_type.extends?(:detached_entity)
402
+ pg_cast_value(value, foobara_type.target_class.primary_key_type, pg_info)
403
+ elsif foobara_type.extends?(:model) || foobara_type.extends?(:attributes) ||
404
+ foobara_type == BuiltinTypes[:duck]
405
+ "'#{PG::Connection.escape(JSON.fast_generate(value))}'"
406
+ elsif foobara_type.extends?(:array)
407
+ element_type = foobara_type.element_type
408
+
409
+ if element_type.extends?(:detached_entity)
410
+ if element_type.target_class.primary_key_type.extends?(:integer)
411
+ value.each { validate_intable!(it) }
412
+ end
413
+ elements_type = ARRAY_ELEMENT_ENCODERS[pg_info[:element_type]]
414
+ array_string = PG::TextEncoder::Array.new(elements_type:).encode(value)
415
+ escaped = PG::Connection.escape(array_string)
416
+
417
+ "'#{escaped}'"
379
418
  else
380
419
  # :nocov:
381
420
  raise UnsupportedPgColumnTypeError.new(pg_type, entity_class)
382
421
  # :nocov:
383
422
  end
423
+ else
424
+ # :nocov:
425
+ raise UnsupportedPgColumnTypeError.new(pg_type, entity_class)
426
+ # :nocov:
427
+ end
428
+ end
429
+
430
+ def validate_intable!(value)
431
+ if value.is_a?(::String) || value.is_a?(::Symbol)
432
+ unless value =~ /\A[+-]?\d+\z/
433
+ # :nocov:
434
+ raise "Expected something that could be cast to an integer but got #{value}"
435
+ # :nocov:
436
+ end
437
+ end
438
+ end
439
+
440
+ def check_type_compatibility!(foobara_type, pg_info, pg_type: pg_info[:type], attribute_name: nil)
441
+ if foobara_type.extends?(:integer)
442
+ unless pg_type == "integer" || pg_type == "bigint" || pg_type == "_int4"
443
+ # :nocov:
444
+ raise ColumnTypeMismatchError.new(pg_type:, foobara_type:, attribute_name:, entity_class:)
445
+ # :nocov:
446
+ end
384
447
  elsif foobara_type.extends?(:string) || foobara_type.extends?(:symbol)
385
- case pg_type
386
- when "text", "character varying"
387
- "'#{PG::Connection.escape(value.to_s)}'"
388
- else
448
+ unless pg_type == "text" || pg_type == "character varying"
389
449
  # :nocov:
390
- raise UnsupportedPgColumnTypeError.new(pg_type, entity_class)
450
+ raise ColumnTypeMismatchError.new(pg_type:, foobara_type:, attribute_name:, entity_class:)
391
451
  # :nocov:
392
452
  end
393
453
  elsif foobara_type.extends?(:datetime)
394
- case pg_type
395
- when "timestamp without time zone"
396
- "'#{PG::Connection.escape(value.inspect)}'"
397
- else
454
+ unless pg_type == "timestamp without time zone"
398
455
  # :nocov:
399
- raise UnsupportedPgColumnTypeError.new(pg_type, entity_class)
456
+ raise ColumnTypeMismatchError.new(pg_type:, foobara_type:, attribute_name:, entity_class:)
400
457
  # :nocov:
401
458
  end
402
459
  elsif foobara_type.extends?(:detached_entity)
403
- pg_cast_value(value, foobara_type.target_class.primary_key_type, pg_info)
404
- elsif foobara_type.extends?(:model) || foobara_type.extends?(:attributes)
405
- case pg_type
406
- when "jsonb", "json"
407
- "'#{PG::Connection.escape(JSON.fast_generate(value))}'"
408
- else
460
+ check_type_compatibility!(foobara_type.target_class.primary_key_type, pg_info, pg_type:, attribute_name:)
461
+ elsif foobara_type.extends?(:model) || foobara_type.extends?(:attributes) ||
462
+ foobara_type == BuiltinTypes[:duck]
463
+ unless pg_type == "jsonb" || pg_type == "json"
409
464
  # :nocov:
410
- raise UnsupportedPgColumnTypeError.new(pg_type, entity_class)
465
+ raise ColumnTypeMismatchError.new(pg_type:, foobara_type:, attribute_name:, entity_class:)
411
466
  # :nocov:
412
467
  end
413
468
  elsif foobara_type.extends?(:array)
414
- element_type = foobara_type.element_type
415
-
416
- if element_type.extends?(:detached_entity)
417
- case pg_type
418
- when "ARRAY"
419
- elements_type = ARRAY_ELEMENT_ENCODERS[pg_info[:element_type]]
420
- array_string = PG::TextEncoder::Array.new(elements_type:).encode(value)
421
- escaped = PG::Connection.escape(array_string)
422
-
423
- "'#{escaped}'"
424
- else
425
- # :nocov:
426
- raise UnsupportedPgColumnTypeError.new(pg_type, entity_class)
427
- # :nocov:
428
- end
429
- else
469
+ if pg_type == "ARRAY"
470
+ element_type = foobara_type.element_type
471
+ check_type_compatibility!(element_type, pg_info, pg_type: pg_info[:element_type], attribute_name:)
430
472
  # :nocov:
431
- raise UnsupportedPgColumnTypeError.new(pg_type, entity_class)
473
+ elsif pg_type != json && pg_type != jsonb
474
+ raise ColumnTypeMismatchError.new(pg_type:, foobara_type:, attribute_name:, entity_class:)
432
475
  # :nocov:
433
476
  end
434
477
  else
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foobara-postgresql-crud-driver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Georgi