sunstone 7.2.0 → 8.0.0

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