sunstone 7.2.0 → 8.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d280487f58777eebd36b07eb69f9492b77370ea3def7b05321bb2d4fd8e55626
4
- data.tar.gz: a34f616483aca20b1207f12538e08fafb19b16ac01db6c19977b902cd1cd1537
3
+ metadata.gz: dd0eae0dbdee21a95ca29317302b67b50647f7d6654af15553425f55d1d11aed
4
+ data.tar.gz: b0cdc960489411360b1ee4f2cb4cdcce93b7df2464dabdafa2bc26955be002b3
5
5
  SHA512:
6
- metadata.gz: 0211de6c05fded268d04e00af2e0f16e9f4ee0650307288ea890c343e1b20f0a2fcf924631e2c0c3e72b602af7e964c8663a6df51f22cce9bd85798b26d12604
7
- data.tar.gz: 62e9b986925a43ca27c172499bca956ea20e6b8ca237d94dbcb1b07f52772ad1ffc9d5a0fe68d9ef6560e9a3df5ccc7bc27a8a78b56aad4fcccc44604f2a9060
6
+ metadata.gz: ce768f425153db1d5a2242f845bc3de221a4d0d0d8a30a1428c128d2bb1e4353ef986020283fdd1d17e7652937c38d03eb1f42478acbc694da32135f335cab22
7
+ data.tar.gz: ece020a27e0554bad07f0e831919e8969cebdd6970adc344fb4f018b3a45032588dcd7c4eb512d9fcb8ff4acfc09ef27d9fe6124fa2b9db0d5eeff659885d800
@@ -8,6 +8,11 @@ module ActiveRecord
8
8
  return ["1=0"] if attributes.empty?
9
9
 
10
10
  attributes.flat_map do |key, value|
11
+ if key.is_a?(Array) && key.size == 1
12
+ key = key.first
13
+ value = value.flatten
14
+ end
15
+
11
16
  if key.is_a?(Array)
12
17
  queries = Array(value).map do |ids_set|
13
18
  raise ArgumentError, "Expected corresponding value for #{key} to be an Array" unless ids_set.is_a?(Array)
@@ -4,6 +4,27 @@
4
4
  module ActiveRecord
5
5
  module Calculations
6
6
 
7
+ # Prior to Rails 8 we didn't need this method becuase it would
8
+ # return the first value if there was just one - so we'll just
9
+ # do the same as prevously because it doesn't have to be joined
10
+ def select_for_count
11
+ if select_values.empty?
12
+ :all
13
+ else
14
+ with_connection do |conn|
15
+ # Rails compiles this to a string, but we don't have string we
16
+ # have a hash
17
+ if model.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
18
+ sv = arel_columns(select_values)
19
+ sv.one? ? sv.first : sv
20
+ else
21
+ sv = arel_columns(select_values).map { |column| conn.visitor.compile(column) }
22
+ sv.one? ? sv.first : sv.join(", ")
23
+ end
24
+ end
25
+ end
26
+ end
27
+
7
28
  def pluck(*column_names)
8
29
  if @none
9
30
  if @async
@@ -25,20 +46,20 @@ module ActiveRecord
25
46
  if has_include?(column_names.first)
26
47
  relation = apply_join_dependency
27
48
  relation.pluck(*column_names)
28
- elsif klass.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
49
+ elsif model.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
29
50
  load
30
- return records.pluck(*column_names.map{|n| n.to_s.sub(/^#{klass.table_name}\./, "")})
51
+ return records.pluck(*column_names.map{|n| n.to_s.sub(/^#{model.table_name}\./, "")})
31
52
  else
32
- klass.disallow_raw_sql!(flattened_args(column_names))
33
- columns = arel_columns(column_names)
53
+ model.disallow_raw_sql!(flattened_args(column_names))
34
54
  relation = spawn
55
+ columns = relation.arel_columns(column_names)
35
56
  relation.select_values = columns
36
57
  result = skip_query_cache_if_necessary do
37
58
  if where_clause.contradiction?
38
59
  ActiveRecord::Result.empty(async: @async)
39
60
  else
40
- klass.with_connection do |c|
41
- c.select_all(relation.arel, "#{klass.name} Pluck", async: @async)
61
+ model.with_connection do |c|
62
+ c.select_all(relation.arel, "#{model.name} Pluck", async: @async)
42
63
  end
43
64
  end
44
65
  end
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
 
5
5
  def assert_modifiable!
6
6
  raise UnmodifiableRelation if @loaded
7
- raise UnmodifiableRelation if @arel && !klass.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
7
+ raise UnmodifiableRelation if @arel && !model.connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
8
8
  end
9
9
 
10
10
  end
@@ -30,6 +30,7 @@ module ActiveRecord
30
30
  end
31
31
 
32
32
  def to_sar_and_binds(arel_or_sar_string, binds = [], preparable = nil, allow_retry = false)
33
+ # Arel::TreeManager -> Arel::Node
33
34
  if arel_or_sar_string.respond_to?(:ast)
34
35
  arel_or_sar_string = arel_or_sar_string.ast
35
36
  end
@@ -40,10 +41,13 @@ module ActiveRecord
40
41
  "The values must be stored on the AST directly"
41
42
  end
42
43
 
43
- sar = visitor.accept(arel_or_sar_string, collector)
44
+ col = collector()
45
+ col.retryable = true
46
+ sar = visitor.compile(arel_or_sar_string, col)
44
47
  [sar.freeze, sar.binds, false, allow_retry]
45
48
  else
46
- [arel_or_sar_string.dup.freeze, binds, false, allow_retry]
49
+ arel_or_sar_string = arel_or_sar_string.dup.freeze unless arel_or_sar_string.frozen?
50
+ [arel_or_sar_string, binds, false, allow_retry]
47
51
  end
48
52
  end
49
53
 
@@ -115,8 +119,8 @@ module ActiveRecord
115
119
  internal_exec_query(sar, name, binds)
116
120
  end
117
121
 
118
- def internal_exec_query(arel, name = 'SAR', binds = [], prepare: false, async: false, allow_retry: false)
119
- sars = []
122
+ # Lowest level way to execute a query. Doesn't check for illegal writes, doesn't annotate queries, yields a native result object.
123
+ def raw_execute(arel, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, batch: false)
120
124
  multiple_requests = arel.is_a?(Arel::Collectors::Sunstone)
121
125
  type_casted_binds = binds#type_casted_binds(binds)
122
126
 
@@ -137,50 +141,73 @@ module ActiveRecord
137
141
  multiple_requests = true
138
142
  end
139
143
  end
140
-
141
- send_request = lambda { |req_arel|
144
+
145
+ send_request = lambda { |conn, req_arel, batch|
142
146
  sar = to_sar(req_arel, type_casted_binds)
143
- sars.push(sar)
144
147
  log_mess = sar.path.split('?', 2)
145
- log("#{sar.method} #{log_mess[0]} #{(log_mess[1] && !log_mess[1].empty?) ? MessagePack.unpack(CGI.unescape(log_mess[1])) : '' }", name) do
146
- with_raw_connection do |conn|
147
- response = conn.send_request(sar)
148
- if response.is_a?(Net::HTTPNoContent)
149
- nil
148
+ log("#{sar.method} #{log_mess[0]} #{(log_mess[1] && !log_mess[1].empty?) ? MessagePack.unpack(CGI.unescape(log_mess[1])) : '' }", name) do |notification_payload|
149
+ result = perform_query(conn, sar, prepare:, notification_payload:, batch: batch)
150
+ result.instance_variable_set(:@sunstone_calculation, true) if result && sar.instance_variable_get(:@sunstone_calculation)
151
+ result
152
+ end
153
+ }
154
+
155
+ result = with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
156
+ if multiple_requests
157
+ binds.delete_at(limit_bind_index) if limit_bind_index
158
+
159
+ limit, offset, results = allowed_limit, 0, nil
160
+ last_affected_rows = 0
161
+ while requested_limit ? offset < requested_limit : true
162
+ split_arel = arel.dup
163
+ split_arel.limit = limit
164
+ split_arel.offset = offset
165
+ request_results = send_request.call(conn, split_arel, true)
166
+ last_affected_rows += @last_affected_rows
167
+ if results
168
+ results.push(*request_results)
150
169
  else
151
- JSON.parse(response.body)
170
+ results = request_results
152
171
  end
172
+ break if request_results.size < limit
173
+ offset = offset + limit
153
174
  end
175
+ @last_affected_rows = last_affected_rows
176
+ results
177
+ else
178
+ send_request.call(conn, arel, true)
154
179
  end
155
- }
156
-
157
- result = if multiple_requests
158
- binds.delete_at(limit_bind_index) if limit_bind_index
159
-
160
- limit, offset, results = allowed_limit, 0, []
161
- while requested_limit ? offset < requested_limit : true
162
- split_arel = arel.dup
163
- split_arel.limit = limit
164
- split_arel.offset = offset
165
- request_results = send_request.call(split_arel)
166
- results = results + request_results
167
- break if request_results.size < limit
168
- offset = offset + limit
169
- end
170
- results
171
- else
172
- send_request.call(arel)
173
180
  end
174
-
175
- if sars[0].instance_variable_defined?(:@sunstone_calculation) && sars[0].instance_variable_get(:@sunstone_calculation)
181
+
182
+ result
183
+ end
184
+
185
+ def perform_query(raw_connection, sar, prepare:, notification_payload:, batch: false)
186
+ response = raw_connection.send_request(sar)
187
+ result = response.is_a?(Net::HTTPNoContent) ? nil : JSON.parse(response.body)
188
+
189
+ verified!
190
+ # handle_warnings(result)
191
+ @last_affected_rows = response['Affected-Rows'] || result&.count || 0
192
+ notification_payload[:row_count] = @last_affected_rows
193
+ result
194
+ end
195
+
196
+ # Receive a native adapter result object and returns an ActiveRecord::Result object.
197
+ def cast_result(raw_result)
198
+ if raw_result.instance_variable_defined?(:@sunstone_calculation) && raw_result.instance_variable_get(:@sunstone_calculation)
176
199
  # this is a count, min, max.... yea i know..
177
- ActiveRecord::Result.new(['all'], [result], {:all => @type_map.lookup('integer', {})})
178
- elsif result.is_a?(Array)
179
- ActiveRecord::Result.new(result[0] ? result[0].keys : [], result.map{|r| r.values})
200
+ ActiveRecord::Result.new(['all'], [raw_result], {:all => @type_map.lookup('integer', {})})
201
+ elsif raw_result.is_a?(Array)
202
+ ActiveRecord::Result.new(raw_result[0] ? raw_result[0].keys : [], raw_result.map{|r| r.values})
180
203
  else
181
- ActiveRecord::Result.new(result.keys, [result.values])
204
+ ActiveRecord::Result.new(raw_result.keys, [raw_result.values])
182
205
  end
183
206
  end
207
+
208
+ def affected_rows(raw_result)
209
+ @last_affected_rows
210
+ end
184
211
 
185
212
  def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
186
213
  sar, binds = to_sar_and_binds(arel, binds)
@@ -190,13 +217,18 @@ module ActiveRecord
190
217
 
191
218
  id_value || last_inserted_id(value)
192
219
  end
193
-
220
+ alias create insert
221
+
222
+ # Executes the update statement and returns the number of rows affected.
194
223
  def update(arel, name = nil, binds = [])
195
- exec_update(arel, name, binds)
224
+ sar, binds = to_sar_and_binds(arel, binds)
225
+ internal_exec_query(sar, name, binds)
196
226
  end
197
227
 
228
+ # Executes the delete statement and returns the number of rows affected.
198
229
  def delete(arel, name = nil, binds = [])
199
- exec_delete(arel, name, binds)
230
+ sql, binds = to_sar_and_binds(arel, binds)
231
+ exec_delete(sql, name, binds)
200
232
  end
201
233
 
202
234
  def last_inserted_id(result)
@@ -92,18 +92,30 @@ module ActiveRecord
92
92
  end
93
93
 
94
94
  def active?
95
- @raw_connection&.active?
95
+ @lock.synchronize do
96
+ @raw_connection&.active?
97
+ end
96
98
  end
97
99
 
98
- def reconnect
99
- super
100
- @raw_connection&.reconnect!
100
+ # Connects to a StandardAPI server and sets up the adapter depending
101
+ # on the connected server's characteristics.
102
+ def connect
103
+ @raw_connection = self.class.new_client(@connection_parameters)
101
104
  end
102
105
 
106
+ def reconnect
107
+ @lock.synchronize do
108
+ @raw_connection&.reconnect!
109
+ connect unless @raw_connection
110
+ end
111
+ end
112
+
103
113
  def disconnect!
104
- super
105
- @raw_connection&.disconnect!
106
- @raw_connection = nil
114
+ @lock.synchronize do
115
+ super
116
+ @raw_connection&.disconnect!
117
+ @raw_connection = nil
118
+ end
107
119
  end
108
120
 
109
121
  def discard! # :nodoc:
@@ -111,12 +123,6 @@ module ActiveRecord
111
123
  @raw_connection = nil
112
124
  end
113
125
 
114
- # Executes the delete statement and returns the number of rows affected.
115
- def delete(arel, name = nil, binds = [])
116
- r = exec_delete(arel, name, binds)
117
- r.rows.first.to_i
118
- end
119
-
120
126
  def native_database_types #:nodoc:
121
127
  NATIVE_DATABASE_TYPES
122
128
  end
@@ -189,17 +195,6 @@ module ActiveRecord
189
195
  end
190
196
  alias create insert
191
197
 
192
- # Connects to a StandardAPI server and sets up the adapter depending
193
- # on the connected server's characteristics.
194
- def connect
195
- @raw_connection = self.class.new_client(@connection_parameters)
196
- end
197
-
198
- def reconnect
199
- @raw_connection&.reconnect!
200
- connect unless @raw_connection
201
- end
202
-
203
198
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
204
199
  # This is called by #connect and should not be called manually.
205
200
  def configure_connection
@@ -1,3 +1,3 @@
1
1
  module Sunstone
2
- VERSION = '7.2.0'
2
+ VERSION = '8.0.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sunstone
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.2.0
4
+ version: 8.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Bracy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-13 00:00:00.000000000 Z
11
+ date: 2025-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -226,14 +226,14 @@ dependencies:
226
226
  requirements:
227
227
  - - ">="
228
228
  - !ruby/object:Gem::Version
229
- version: 7.2.0
229
+ version: 8.0.1
230
230
  type: :runtime
231
231
  prerelease: false
232
232
  version_requirements: !ruby/object:Gem::Requirement
233
233
  requirements:
234
234
  - - ">="
235
235
  - !ruby/object:Gem::Version
236
- version: 7.2.0
236
+ version: 8.0.1
237
237
  - !ruby/object:Gem::Dependency
238
238
  name: arel-extensions
239
239
  requirement: !ruby/object:Gem::Requirement
@@ -324,7 +324,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
324
324
  - !ruby/object:Gem::Version
325
325
  version: '0'
326
326
  requirements: []
327
- rubygems_version: 3.5.11
327
+ rubygems_version: 3.5.21
328
328
  signing_key:
329
329
  specification_version: 4
330
330
  summary: A library for interacting with REST APIs