sequel 5.82.0 → 5.84.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequel +9 -17
  3. data/lib/sequel/adapters/jdbc/derby.rb +1 -1
  4. data/lib/sequel/adapters/shared/db2.rb +1 -1
  5. data/lib/sequel/adapters/shared/mssql.rb +14 -2
  6. data/lib/sequel/adapters/shared/postgres.rb +42 -4
  7. data/lib/sequel/adapters/shared/sqlite.rb +3 -1
  8. data/lib/sequel/database/connecting.rb +1 -4
  9. data/lib/sequel/database/misc.rb +27 -7
  10. data/lib/sequel/database/schema_methods.rb +17 -1
  11. data/lib/sequel/dataset/sql.rb +13 -0
  12. data/lib/sequel/extensions/pg_json_ops.rb +328 -1
  13. data/lib/sequel/extensions/stdio_logger.rb +48 -0
  14. data/lib/sequel/extensions/string_agg.rb +15 -2
  15. data/lib/sequel/plugins/defaults_setter.rb +16 -4
  16. data/lib/sequel/plugins/optimistic_locking.rb +2 -0
  17. data/lib/sequel/sql.rb +8 -5
  18. data/lib/sequel/version.rb +1 -1
  19. metadata +4 -235
  20. data/CHANGELOG +0 -1377
  21. data/README.rdoc +0 -936
  22. data/doc/advanced_associations.rdoc +0 -884
  23. data/doc/association_basics.rdoc +0 -1859
  24. data/doc/bin_sequel.rdoc +0 -146
  25. data/doc/cheat_sheet.rdoc +0 -255
  26. data/doc/code_order.rdoc +0 -104
  27. data/doc/core_extensions.rdoc +0 -405
  28. data/doc/dataset_basics.rdoc +0 -96
  29. data/doc/dataset_filtering.rdoc +0 -222
  30. data/doc/extensions.rdoc +0 -77
  31. data/doc/fork_safety.rdoc +0 -84
  32. data/doc/mass_assignment.rdoc +0 -98
  33. data/doc/migration.rdoc +0 -660
  34. data/doc/model_dataset_method_design.rdoc +0 -129
  35. data/doc/model_hooks.rdoc +0 -254
  36. data/doc/model_plugins.rdoc +0 -270
  37. data/doc/mssql_stored_procedures.rdoc +0 -43
  38. data/doc/object_model.rdoc +0 -563
  39. data/doc/opening_databases.rdoc +0 -439
  40. data/doc/postgresql.rdoc +0 -611
  41. data/doc/prepared_statements.rdoc +0 -144
  42. data/doc/querying.rdoc +0 -1070
  43. data/doc/reflection.rdoc +0 -120
  44. data/doc/release_notes/5.0.0.txt +0 -159
  45. data/doc/release_notes/5.1.0.txt +0 -31
  46. data/doc/release_notes/5.10.0.txt +0 -84
  47. data/doc/release_notes/5.11.0.txt +0 -83
  48. data/doc/release_notes/5.12.0.txt +0 -141
  49. data/doc/release_notes/5.13.0.txt +0 -27
  50. data/doc/release_notes/5.14.0.txt +0 -63
  51. data/doc/release_notes/5.15.0.txt +0 -39
  52. data/doc/release_notes/5.16.0.txt +0 -110
  53. data/doc/release_notes/5.17.0.txt +0 -31
  54. data/doc/release_notes/5.18.0.txt +0 -69
  55. data/doc/release_notes/5.19.0.txt +0 -28
  56. data/doc/release_notes/5.2.0.txt +0 -33
  57. data/doc/release_notes/5.20.0.txt +0 -89
  58. data/doc/release_notes/5.21.0.txt +0 -87
  59. data/doc/release_notes/5.22.0.txt +0 -48
  60. data/doc/release_notes/5.23.0.txt +0 -56
  61. data/doc/release_notes/5.24.0.txt +0 -56
  62. data/doc/release_notes/5.25.0.txt +0 -32
  63. data/doc/release_notes/5.26.0.txt +0 -35
  64. data/doc/release_notes/5.27.0.txt +0 -21
  65. data/doc/release_notes/5.28.0.txt +0 -16
  66. data/doc/release_notes/5.29.0.txt +0 -22
  67. data/doc/release_notes/5.3.0.txt +0 -121
  68. data/doc/release_notes/5.30.0.txt +0 -20
  69. data/doc/release_notes/5.31.0.txt +0 -148
  70. data/doc/release_notes/5.32.0.txt +0 -46
  71. data/doc/release_notes/5.33.0.txt +0 -24
  72. data/doc/release_notes/5.34.0.txt +0 -40
  73. data/doc/release_notes/5.35.0.txt +0 -56
  74. data/doc/release_notes/5.36.0.txt +0 -60
  75. data/doc/release_notes/5.37.0.txt +0 -30
  76. data/doc/release_notes/5.38.0.txt +0 -28
  77. data/doc/release_notes/5.39.0.txt +0 -19
  78. data/doc/release_notes/5.4.0.txt +0 -80
  79. data/doc/release_notes/5.40.0.txt +0 -40
  80. data/doc/release_notes/5.41.0.txt +0 -25
  81. data/doc/release_notes/5.42.0.txt +0 -136
  82. data/doc/release_notes/5.43.0.txt +0 -98
  83. data/doc/release_notes/5.44.0.txt +0 -32
  84. data/doc/release_notes/5.45.0.txt +0 -34
  85. data/doc/release_notes/5.46.0.txt +0 -87
  86. data/doc/release_notes/5.47.0.txt +0 -59
  87. data/doc/release_notes/5.48.0.txt +0 -14
  88. data/doc/release_notes/5.49.0.txt +0 -59
  89. data/doc/release_notes/5.5.0.txt +0 -61
  90. data/doc/release_notes/5.50.0.txt +0 -78
  91. data/doc/release_notes/5.51.0.txt +0 -47
  92. data/doc/release_notes/5.52.0.txt +0 -87
  93. data/doc/release_notes/5.53.0.txt +0 -23
  94. data/doc/release_notes/5.54.0.txt +0 -27
  95. data/doc/release_notes/5.55.0.txt +0 -21
  96. data/doc/release_notes/5.56.0.txt +0 -51
  97. data/doc/release_notes/5.57.0.txt +0 -23
  98. data/doc/release_notes/5.58.0.txt +0 -31
  99. data/doc/release_notes/5.59.0.txt +0 -73
  100. data/doc/release_notes/5.6.0.txt +0 -31
  101. data/doc/release_notes/5.60.0.txt +0 -22
  102. data/doc/release_notes/5.61.0.txt +0 -43
  103. data/doc/release_notes/5.62.0.txt +0 -132
  104. data/doc/release_notes/5.63.0.txt +0 -33
  105. data/doc/release_notes/5.64.0.txt +0 -50
  106. data/doc/release_notes/5.65.0.txt +0 -21
  107. data/doc/release_notes/5.66.0.txt +0 -24
  108. data/doc/release_notes/5.67.0.txt +0 -32
  109. data/doc/release_notes/5.68.0.txt +0 -61
  110. data/doc/release_notes/5.69.0.txt +0 -26
  111. data/doc/release_notes/5.7.0.txt +0 -108
  112. data/doc/release_notes/5.70.0.txt +0 -35
  113. data/doc/release_notes/5.71.0.txt +0 -21
  114. data/doc/release_notes/5.72.0.txt +0 -33
  115. data/doc/release_notes/5.73.0.txt +0 -66
  116. data/doc/release_notes/5.74.0.txt +0 -45
  117. data/doc/release_notes/5.75.0.txt +0 -35
  118. data/doc/release_notes/5.76.0.txt +0 -86
  119. data/doc/release_notes/5.77.0.txt +0 -63
  120. data/doc/release_notes/5.78.0.txt +0 -67
  121. data/doc/release_notes/5.79.0.txt +0 -28
  122. data/doc/release_notes/5.8.0.txt +0 -170
  123. data/doc/release_notes/5.80.0.txt +0 -40
  124. data/doc/release_notes/5.81.0.txt +0 -31
  125. data/doc/release_notes/5.82.0.txt +0 -61
  126. data/doc/release_notes/5.9.0.txt +0 -99
  127. data/doc/schema_modification.rdoc +0 -679
  128. data/doc/security.rdoc +0 -443
  129. data/doc/sharding.rdoc +0 -286
  130. data/doc/sql.rdoc +0 -648
  131. data/doc/testing.rdoc +0 -204
  132. data/doc/thread_safety.rdoc +0 -15
  133. data/doc/transactions.rdoc +0 -250
  134. data/doc/validations.rdoc +0 -558
  135. data/doc/virtual_rows.rdoc +0 -265
@@ -132,6 +132,17 @@
132
132
  # j.is_not_json(type: :array) # j IS NOT JSON ARRAY
133
133
  # j.is_not_json(unique: true) # j IS NOT JSON WITH UNIQUE
134
134
  #
135
+ # On PostgreSQL 17+, the additional JSON functions are supported (see method documentation
136
+ # for additional options):
137
+ #
138
+ # j.exists('$.foo') # json_exists(jsonb_column, '$.foo')
139
+ # j.value('$.foo') # json_value(jsonb_column, '$.foo')
140
+ # j.query('$.foo') # json_query(jsonb_column, '$.foo')
141
+ #
142
+ # j.exists('$.foo', passing: {a: 1}) # json_exists(jsonb_column, '$.foo' PASSING 1 AS a)
143
+ # j.value('$.foo', returning: Time) # json_value(jsonb_column, '$.foo' RETURNING timestamp)
144
+ # j.query('$.foo', wrapper: true) # json_query(jsonb_column, '$.foo' WITH WRAPPER)
145
+ #
135
146
  # If you are also using the pg_json extension, you should load it before
136
147
  # loading this extension. Doing so will allow you to use the #op method on
137
148
  # JSONHash, JSONHarray, JSONBHash, and JSONBArray, allowing you to perform json/jsonb operations
@@ -224,7 +235,25 @@ module Sequel
224
235
  function(:each_text)
225
236
  end
226
237
 
227
- # Returns a json value for the object at the given path.
238
+ # Return whether the given JSON path yields any items in the receiver.
239
+ # Options:
240
+ #
241
+ # :on_error :: How to handle errors when evaluating the JSON path expression.
242
+ # true :: Return true
243
+ # false :: Return false (default behavior)
244
+ # :null :: Return nil
245
+ # :error :: raise a DatabaseError
246
+ # :passing :: Variables to pass to the JSON path expression. Keys are variable
247
+ # names, values are the values of the variable.
248
+ #
249
+ # json_op.exists("$.a") # json_exists(json, '$.a')
250
+ # json_op.exists("$.a", passing: {a: 1}) # json_exists(json, '$.a' PASSING 1 AS a)
251
+ # json_op.exists("$.a", on_error: :error) # json_exists(json, '$.a' ERROR ON ERROR)
252
+ def exists(path, opts=OPTS)
253
+ Sequel::SQL::BooleanExpression.new(:NOOP, JSONExistsOp.new(self, path, opts))
254
+ end
255
+
256
+ # Returns a JSON value for the object at the given path.
228
257
  #
229
258
  # json_op.extract('a') # json_extract_path(json, 'a')
230
259
  # json_op.extract('a', 'b') # json_extract_path(json, 'a', 'b')
@@ -299,6 +328,35 @@ module Sequel
299
328
  SQL::Function.new(function_name(:populate_recordset), arg, self)
300
329
  end
301
330
 
331
+ # Return the result of applying the JSON path expression to the receiver, by default
332
+ # returning results as jsonb. Options:
333
+ #
334
+ # :on_empty :: How to handle case where path expression yields an empty set.
335
+ # Uses same values as :on_error option.
336
+ # :on_error :: How to handle errors when evaluating the JSON path expression:
337
+ # :null :: Return nil (default)
338
+ # :empty_array :: Return an empty array
339
+ # :empty_object :: Return an empty object
340
+ # :error :: raise a DatabaseError
341
+ # any other value :: used as default value
342
+ # :passing :: Variables to pass to the JSON path expression. Keys are variable
343
+ # names, values are the values of the variable.
344
+ # :returning :: The data type to return (jsonb by default)
345
+ # :wrapper :: How to wrap returned values:
346
+ # true, :unconditional :: Always wrap returning values in an array
347
+ # :conditional :: Only wrap multiple return values in an array
348
+ # :omit_quotes :: Do not wrap scalar strings in quotes
349
+ #
350
+ # json_op.query("$.a") # json_query(json, '$.a')
351
+ # json_op.query("$.a", passing: {a: 1}) # json_query(json, '$.a' PASSING 1 AS a)
352
+ # json_op.query("$.a", on_error: :empty_array) # json_query(json, '$.a' EMPTY ARRAY ON ERROR)
353
+ # json_op.query("$.a", returning: Time) # json_query(json, '$.a' RETURNING timestamp)
354
+ # json_op.query("$.a", on_empty: 2) # json_query(json, '$.a' DEFAULT 2 ON EMPTY)
355
+ # json_op.query("$.a", wrapper: true) # json_query(json, '$.a' WITH WRAPPER)
356
+ def query(path, opts=OPTS)
357
+ self.class.new(JSONQueryOp.new(self, path, opts))
358
+ end
359
+
302
360
  # Returns a json value stripped of all internal null values.
303
361
  #
304
362
  # json_op.strip_nulls # json_strip_nulls(json)
@@ -329,6 +387,34 @@ module Sequel
329
387
  function(:typeof)
330
388
  end
331
389
 
390
+ # If called without arguments, operates as SQL::Wrapper#value. Otherwise,
391
+ # return the result of applying the JSON path expression to the receiver, by default
392
+ # returning results as text. Options:
393
+ #
394
+ # :on_empty :: How to handle case where path expression yields an empty set.
395
+ # Uses same values as :on_error option.
396
+ # :on_error :: How to handle errors when evaluating the JSON path expression.
397
+ # :null :: Return nil (default)
398
+ # :error :: raise a DatabaseError
399
+ # any other value :: used as default value
400
+ # :passing :: Variables to pass to the JSON path expression. Keys are variable
401
+ # names, values are the values of the variable.
402
+ # :returning :: The data type to return (text by default)
403
+ #
404
+ # json_op.value("$.a") # json_value(json, '$.a')
405
+ # json_op.value("$.a", passing: {a: 1}) # json_value(json, '$.a' PASSING 1 AS a)
406
+ # json_op.value("$.a", on_error: :error) # json_value(json, '$.a' ERROR ON ERROR)
407
+ # json_op.value("$.a", returning: Time) # json_value(json, '$.a' RETURNING timestamp)
408
+ # json_op.value("$.a", on_empty: 2) # json_value(json, '$.a' DEFAULT 2 ON EMPTY)
409
+ def value(path=(no_args_given = true), opts=OPTS)
410
+ if no_args_given
411
+ # Act as SQL::Wrapper#value
412
+ super()
413
+ else
414
+ Sequel::SQL::StringExpression.new(:NOOP, JSONValueOp.new(self, path, opts))
415
+ end
416
+ end
417
+
332
418
  private
333
419
 
334
420
  # Internals of IS [NOT] JSON support
@@ -705,6 +791,247 @@ module Sequel
705
791
  end
706
792
  end
707
793
 
794
+ # Object representing json_exists calls
795
+ class JSONExistsOp < SQL::Expression
796
+ ON_ERROR_SQL = {
797
+ true => 'TRUE',
798
+ false => 'FALSE',
799
+ :null => 'UNKNOWN',
800
+ :error => 'ERROR',
801
+ }.freeze
802
+ private_constant :ON_ERROR_SQL
803
+
804
+ # Expression (context_item in PostgreSQL terms), usually JSONBaseOp instance
805
+ attr_reader :expr
806
+
807
+ # JSON path expression to apply against the expression
808
+ attr_reader :path
809
+
810
+ # Variables to set in the JSON path expression
811
+ attr_reader :passing
812
+
813
+ # How to handle errors when evaluating the JSON path expression
814
+ attr_reader :on_error
815
+
816
+ # See JSONBaseOp#exists for documentation on the options.
817
+ def initialize(expr, path, opts=OPTS)
818
+ @expr = expr
819
+ @path = path
820
+ @passing = opts[:passing]
821
+ @on_error = opts[:on_error]
822
+ freeze
823
+ end
824
+
825
+ # Append the SQL function call expression to the SQL
826
+ def to_s_append(ds, sql)
827
+ to_s_append_function_name(ds, sql)
828
+ to_s_append_args_passing(ds, sql)
829
+ to_s_append_on_error(ds, sql)
830
+ sql << ')'
831
+ end
832
+
833
+ # Support transforming of function call expression
834
+ def sequel_ast_transform(transformer)
835
+ opts = {}
836
+ transform_opts(transformer, opts)
837
+ self.class.new(transformer.call(@expr), @path, opts)
838
+ end
839
+
840
+ private
841
+
842
+ # Set the :passing and :on_error options when doing an
843
+ # AST transform.
844
+ def transform_opts(transformer, opts)
845
+ if @passing
846
+ passing = opts[:passing] = {}
847
+ @passing.each do |k, v|
848
+ passing[k] = transformer.call(v)
849
+ end
850
+ end
851
+
852
+ opts[:on_error] = @on_error
853
+ end
854
+
855
+ def to_s_append_function_name(ds, sql)
856
+ sql << 'json_exists('
857
+ end
858
+
859
+ # Append the expression, path, and optional PASSING fragments
860
+ def to_s_append_args_passing(ds, sql)
861
+ ds.literal_append(sql, @expr)
862
+ sql << ', '
863
+ ds.literal_append(sql, @path)
864
+
865
+ if (passing = @passing) && !passing.empty?
866
+ sql << ' PASSING '
867
+ comma = false
868
+ passing.each do |k, v|
869
+ if comma
870
+ sql << ', '
871
+ else
872
+ comma = true
873
+ end
874
+ ds.literal_append(sql, v)
875
+ sql << " AS " << k.to_s
876
+ end
877
+ end
878
+ end
879
+
880
+ # Append the optional ON ERROR fragments
881
+ def to_s_append_on_error(ds, sql)
882
+ unless @on_error.nil?
883
+ sql << " "
884
+ to_s_append_on_value(ds, sql, @on_error)
885
+ sql << " ON ERROR"
886
+ end
887
+ end
888
+
889
+ # Append the value to use for ON ERROR
890
+ def to_s_append_on_value(ds, sql, value)
891
+ sql << ON_ERROR_SQL.fetch(value)
892
+ end
893
+ end
894
+
895
+ # Object representing json_value calls
896
+ class JSONValueOp < JSONExistsOp
897
+ ON_SQL = {
898
+ :null => 'NULL',
899
+ :error => 'ERROR',
900
+ }.freeze
901
+ private_constant :ON_SQL
902
+
903
+ # The database type to cast returned values to
904
+ attr_reader :returning
905
+
906
+ # How to handle cases where the JSON path expression evaluation yields
907
+ # an empty set.
908
+ attr_reader :on_empty
909
+
910
+ # See JSONBaseOp#value for documentation of the options.
911
+ def initialize(expr, path, opts=OPTS)
912
+ @returning = opts[:returning]
913
+ @on_empty = opts[:on_empty]
914
+ super
915
+ end
916
+
917
+ private
918
+
919
+ # Also handle transforming the returning and on_empty options.
920
+ def transform_opts(transformer, opts)
921
+ super
922
+ opts[:returning] = @returning
923
+ on_error = @on_error
924
+ on_error = transformer.call(on_error) unless on_sql_value(on_error)
925
+ opts[:on_error] = on_error
926
+ on_empty = @on_empty
927
+ on_empty = transformer.call(on_empty) unless on_sql_value(on_empty)
928
+ opts[:on_empty] = on_empty
929
+ end
930
+
931
+ def to_s_append_function_name(ds, sql)
932
+ sql << 'json_value('
933
+ end
934
+
935
+ # Also append the optional RETURNING fragment
936
+ def to_s_append_args_passing(ds, sql)
937
+ super
938
+
939
+ if @returning
940
+ sql << ' RETURNING ' << ds.db.cast_type_literal(@returning).to_s
941
+ end
942
+ end
943
+
944
+ # Also append the optional ON EMPTY fragment
945
+ def to_s_append_on_error(ds, sql)
946
+ unless @on_empty.nil?
947
+ sql << " "
948
+ to_s_append_on_value(ds, sql, @on_empty)
949
+ sql << " ON EMPTY"
950
+ end
951
+
952
+ super
953
+ end
954
+
955
+ # Handle DEFAULT values in ON EMPTY/ON ERROR fragments
956
+ def to_s_append_on_value(ds, sql, value)
957
+ if v = on_sql_value(value)
958
+ sql << v
959
+ else
960
+ sql << 'DEFAULT '
961
+ default_literal_append(ds, sql, value)
962
+ end
963
+ end
964
+
965
+ # Do not auto paramterize default value, as PostgreSQL doesn't allow it.
966
+ def default_literal_append(ds, sql, v)
967
+ if sql.respond_to?(:skip_auto_param)
968
+ sql.skip_auto_param do
969
+ ds.literal_append(sql, v)
970
+ end
971
+ else
972
+ ds.literal_append(sql, v)
973
+ end
974
+ end
975
+
976
+ def on_sql_value(value)
977
+ ON_SQL[value]
978
+ end
979
+ end
980
+
981
+ # Object representing json_query calls
982
+ class JSONQueryOp < JSONValueOp
983
+ ON_SQL = {
984
+ :null => 'NULL',
985
+ :error => 'ERROR',
986
+ :empty_array => 'EMPTY ARRAY',
987
+ :empty_object => 'EMPTY OBJECT',
988
+ }.freeze
989
+ private_constant :ON_SQL
990
+
991
+ WRAPPER = {
992
+ :conditional => ' WITH CONDITIONAL WRAPPER',
993
+ :unconditional => ' WITH WRAPPER',
994
+ :omit_quotes => ' OMIT QUOTES'
995
+ }
996
+ WRAPPER[true] = WRAPPER[:unconditional]
997
+ WRAPPER.freeze
998
+ private_constant :WRAPPER
999
+
1000
+ # How to handle wrapping of results
1001
+ attr_reader :wrapper
1002
+
1003
+ # See JSONBaseOp#query for documentation of the options.
1004
+ def initialize(expr, path, opts=OPTS)
1005
+ @wrapper = opts[:wrapper]
1006
+ super
1007
+ end
1008
+
1009
+ private
1010
+
1011
+ # Also handle transforming the wrapper option
1012
+ def transform_opts(transformer, opts)
1013
+ super
1014
+ opts[:wrapper] = @wrapper
1015
+ end
1016
+
1017
+ def to_s_append_function_name(ds, sql)
1018
+ sql << 'json_query('
1019
+ end
1020
+
1021
+ # Also append the optional WRAPPER/OMIT QUOTES fragment
1022
+ def to_s_append_args_passing(ds, sql)
1023
+ super
1024
+
1025
+ if @wrapper
1026
+ sql << WRAPPER.fetch(@wrapper)
1027
+ end
1028
+ end
1029
+
1030
+ def on_sql_value(value)
1031
+ ON_SQL[value]
1032
+ end
1033
+ end
1034
+
708
1035
  module JSONOpMethods
709
1036
  # Wrap the receiver in an JSONOp so you can easily use the PostgreSQL
710
1037
  # json functions and operators with it.
@@ -0,0 +1,48 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The stdio_logger extension exposes a Sequel::StdioLogger class that
4
+ # can be used for logging with Sequel, as a minimal alternative to
5
+ # the logger library. It exposes debug/info/warn/error methods for the
6
+ # different warning levels. The debug method is a no-op, so that setting
7
+ # the Database sql_log_level to debug will result in no output for normal
8
+ # queries. The info/warn/error methods log the current time, log level,
9
+ # and the given message.
10
+ #
11
+ # To use this extension:
12
+ #
13
+ # Sequel.extension :stdio_logger
14
+ #
15
+ # Then you you can use Sequel::StdioLogger to wrap IO objects that you
16
+ # would like Sequel to log to:
17
+ #
18
+ # DB.loggers << Sequel::StdioLogger.new($stdout)
19
+ #
20
+ # log_file = File.open("db_queries.log", 'a')
21
+ # log_file.sync = true
22
+ # DB.loggers << Sequel::StdioLogger.new(log_file)
23
+ #
24
+ # This is implemented as a global extension instead of a Database extension
25
+ # because Database loggers must be set before Database extensions are loaded.
26
+ #
27
+ # Related module: Sequel::StdioLogger
28
+
29
+ #
30
+ module Sequel
31
+ class StdioLogger
32
+ def initialize(device)
33
+ @device = device
34
+ end
35
+
36
+ # Do not log debug messages. This is so setting the Database
37
+ # sql_log_level to debug will result in no output.
38
+ def debug(msg)
39
+ end
40
+
41
+ [:info, :warn, :error].each do |meth|
42
+ define_method(meth) do |msg|
43
+ @device.write("#{Time.now.strftime('%F %T')} #{meth.to_s.upcase}: #{msg}\n")
44
+ nil
45
+ end
46
+ end
47
+ end
48
+ end
@@ -54,6 +54,7 @@
54
54
  # * MySQL
55
55
  # * HSQLDB
56
56
  # * H2
57
+ # * SQLite 3.44+ (distinct only works when separator is ',')
57
58
  #
58
59
  # Related module: Sequel::SQL::StringAgg
59
60
 
@@ -94,6 +95,19 @@ module Sequel
94
95
  distinct = sa.is_distinct?
95
96
 
96
97
  case db_type = db.database_type
98
+ when :sqlite
99
+ raise Error, "string_agg(DISTINCT) is not supported with a non-comma separator on #{db.database_type}" if distinct && separator != ","
100
+
101
+ args = [expr]
102
+ args << separator unless distinct
103
+ f = Function.new(:group_concat, *args)
104
+ if order
105
+ f = f.order(*order)
106
+ end
107
+ if distinct
108
+ f = f.distinct
109
+ end
110
+ literal_append(sql, f)
97
111
  when :postgres, :sqlanywhere
98
112
  f = Function.new(db_type == :postgres ? :string_agg : :list, expr, separator)
99
113
  if order
@@ -151,7 +165,7 @@ module Sequel
151
165
  freeze
152
166
  end
153
167
 
154
- # Whether the current expression uses distinct expressions
168
+ # Whether the current expression uses distinct expressions
155
169
  def is_distinct?
156
170
  @distinct == true
157
171
  end
@@ -178,4 +192,3 @@ module Sequel
178
192
 
179
193
  Dataset.register_extension(:string_agg, SQL::StringAgg::DatasetMethods)
180
194
  end
181
-
@@ -26,6 +26,12 @@ module Sequel
26
26
  # Album.default_values[:a] = lambda{Date.today}
27
27
  # Album.new.a # => Date.today
28
28
  #
29
+ # If the proc accepts a single argument, it is passed the instance, allowing
30
+ # default values to depend on instance-specific state:
31
+ #
32
+ # Album.default_values[:a] = lambda{|album| album.b + 1}
33
+ # Album.new(b: 10).a # => 11
34
+ #
29
35
  # By default, default values returned are not cached:
30
36
  #
31
37
  # Album.new.a.equal?(Album.new.a) # => false
@@ -43,13 +49,13 @@ module Sequel
43
49
  # album.values # => {}
44
50
  # album.a
45
51
  # album.values # => {:a => Date.today}
46
- #
52
+ #
47
53
  # Usage:
48
54
  #
49
55
  # # Make all model subclass instances set defaults (called before loading subclasses)
50
56
  # Sequel::Model.plugin :defaults_setter
51
57
  #
52
- # # Make the Album class set defaults
58
+ # # Make the Album class set defaults
53
59
  # Album.plugin :defaults_setter
54
60
  module DefaultsSetter
55
61
  # Set the default values based on the model schema. Options:
@@ -76,7 +82,7 @@ module Sequel
76
82
  def cache_default_values?
77
83
  @cache_default_values
78
84
  end
79
-
85
+
80
86
  # Freeze default values when freezing model class
81
87
  def freeze
82
88
  @default_values.freeze
@@ -133,7 +139,13 @@ module Sequel
133
139
  def [](k)
134
140
  if new? && !values.has_key?(k)
135
141
  v = model.default_values.fetch(k){return}
136
- v = v.call if v.respond_to?(:call)
142
+ if v.respond_to?(:call)
143
+ v = if v.respond_to?(:arity) && v.arity == 1
144
+ v.call(self)
145
+ else
146
+ v.call
147
+ end
148
+ end
137
149
  values[k] = v if model.cache_default_values?
138
150
  v
139
151
  else
@@ -45,6 +45,8 @@ module Sequel
45
45
  columns[lc] = lcv + 1
46
46
  super
47
47
  set_column_value("#{lc}=", lcv + 1)
48
+ changed_columns.delete(lc)
49
+ nil
48
50
  end
49
51
  end
50
52
  end
data/lib/sequel/sql.rb CHANGED
@@ -153,13 +153,16 @@ module Sequel
153
153
  class ComplexExpression < Expression
154
154
  # A hash of the opposite for each operator symbol, used for inverting
155
155
  # objects.
156
- OPERTATOR_INVERSIONS = {:AND => :OR, :OR => :AND, :< => :>=, :> => :<=,
156
+ OPERATOR_INVERSIONS = {:AND => :OR, :OR => :AND, :< => :>=, :> => :<=,
157
157
  :<= => :>, :>= => :<, :'=' => :'!=' , :'!=' => :'=', :LIKE => :'NOT LIKE',
158
158
  :'NOT LIKE' => :LIKE, :~ => :'!~', :'!~' => :~, :IN => :'NOT IN',
159
159
  :'NOT IN' => :IN, :IS => :'IS NOT', :'IS NOT' => :IS, :'~*' => :'!~*',
160
160
  :'!~*' => :'~*', :NOT => :NOOP, :NOOP => :NOT, :ILIKE => :'NOT ILIKE',
161
161
  :'NOT ILIKE'=>:ILIKE}.freeze
162
162
 
163
+ # SEQUEL6: Remove
164
+ OPERTATOR_INVERSIONS = OPERATOR_INVERSIONS
165
+
163
166
  # Standard mathematical operators used in +NumericMethods+
164
167
  MATHEMATICAL_OPERATORS = [:+, :-, :/, :*, :**].freeze
165
168
 
@@ -1142,9 +1145,9 @@ module Sequel
1142
1145
  when BooleanExpression
1143
1146
  case op = ce.op
1144
1147
  when :AND, :OR
1145
- BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.map{|a| BooleanExpression.invert(a)})
1148
+ BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.map{|a| BooleanExpression.invert(a)})
1146
1149
  when :IN, :"NOT IN"
1147
- BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.dup)
1150
+ BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.dup)
1148
1151
  else
1149
1152
  if ce.args.length == 2
1150
1153
  case ce.args[1]
@@ -1153,10 +1156,10 @@ module Sequel
1153
1156
  # can result in incorrect behavior for ANY/SOME/ALL operators.
1154
1157
  BooleanExpression.new(:NOT, ce)
1155
1158
  else
1156
- BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.dup)
1159
+ BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.dup)
1157
1160
  end
1158
1161
  else
1159
- BooleanExpression.new(OPERTATOR_INVERSIONS[op], *ce.args.dup)
1162
+ BooleanExpression.new(OPERATOR_INVERSIONS[op], *ce.args.dup)
1160
1163
  end
1161
1164
  end
1162
1165
  when StringExpression, NumericExpression
@@ -6,7 +6,7 @@ module Sequel
6
6
 
7
7
  # The minor version of Sequel. Bumped for every non-patch level
8
8
  # release, generally around once a month.
9
- MINOR = 82
9
+ MINOR = 84
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.