rubydb 0.1.2 → 0.1.4

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: 5cfc85dc4d3cfca89bd7d89335e4bf887fbfca212836f8a62d92a70a63f0f284
4
- data.tar.gz: 9869f03b9da1a7e0cfd0c01f1f22ae9b5ba0e2b12eedaeac72e1b4deab878a0b
3
+ metadata.gz: 569e9ce02867a64e165f0703b4c2f3135255666ac938b05bed690b846df8b9b7
4
+ data.tar.gz: 29d7e3cb03885093407d20c51e52bfb4bb17257b701b862b5070fff5bc8f9eb3
5
5
  SHA512:
6
- metadata.gz: 76650c2403e26244619687eb6146bb7a1561d3764d664094a6deb94e02566924c60979f481aca972be026987775f8c9b68ed02804d15e583f885528e02833613
7
- data.tar.gz: 60c894fde163c1a47ed84aa2c2d8b374a9b6c70f3d810feb7ca419a06aa2cb5e6026d8aa278d27ed89ad4a83b48405c31137671f1d2dab7240d0efc36b367de8
6
+ metadata.gz: 1a3a33eb843e173974500f6848e395d10b4004b30c05c5c14b3afc353d3eff1c128d3d4ed6c3779889384aa0554b08c28d3722a0bcd623dca8d3576454fcc467
7
+ data.tar.gz: 3784d9d65f06788ab7e6e934b46e9916e1cd3ce0fc86f30bc677fb9b23c0b0d6cc04223b6b7acdcf38122d9ce0972b71eff1681bb02e6c07c350bf73f06d0d3f
data/CHANGELOG.md CHANGED
@@ -23,6 +23,19 @@ All notable changes to RubyDB are documented here. Versions follow
23
23
  vacuum, maintenance, and release workflows.
24
24
  - Clarified the tested common SQLite-style profile and production limits.
25
25
 
26
+ ## 0.1.4 - 2026-09-09
27
+
28
+ - Returned logical generated primary-key values to ActiveRecord separately
29
+ from physical storage row identifiers, including after deletes.
30
+ - Preserved generated insert identifiers through the RubyDB result boundary.
31
+
32
+ ## 0.1.3 - 2026-09-09
33
+
34
+ - Normalized numeric MVCC visibility-map keys after restart to prevent mixed
35
+ string/integer keys and duplicate-key warnings on Ruby 4.
36
+ - Made visibility-map persistence failures raise `RubyDB::StorageError`
37
+ instead of silently reporting success.
38
+
26
39
  ## 0.1.2 - 2026-09-09
27
40
 
28
41
  - Added SQLite/ActiveRecord-compatible `INSERT ... DEFAULT VALUES` parsing,
@@ -249,7 +249,10 @@ module ActiveRecord
249
249
  log(sql, name) do
250
250
  params = bind_values(binds)
251
251
  result = @connection.execute(sql, params)
252
- id = result.row_id
252
+ # RubyDB keeps a physical row id for storage operations and a
253
+ # logical primary-key value for SQL/ActiveRecord. They can differ
254
+ # after deletes, so ActiveRecord must receive the logical id.
255
+ id = result.inserted_id || result.row_id
253
256
  ActiveRecord::Result.new([pk || "id"], id.nil? ? [] : [[id]])
254
257
  end
255
258
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "rubydb-activerecord"
5
- spec.version = "0.1.1"
5
+ spec.version = "0.1.2"
6
6
  spec.authors = ["Aldane Hutchinson"]
7
7
  spec.email = ["aldanehutchinson5@gmail.com"]
8
8
 
@@ -89,6 +89,18 @@ RSpec.describe ActiveRecord::ConnectionAdapters::RubyDBAdapter do
89
89
  expect(settings_model.create!.reload.label).to eq("")
90
90
  end
91
91
 
92
+ it "returns the logical primary key after rows are deleted" do
93
+ connection = ActiveRecord::Base.connection
94
+ connection.execute("CREATE TABLE events (id INTEGER PRIMARY KEY, name VARCHAR(255) NOT NULL)")
95
+
96
+ events = Class.new(ActiveRecord::Base) { self.table_name = "events" }
97
+ events.create!(name: "first").destroy!
98
+ created = events.create!(name: "replacement")
99
+
100
+ expect(created.id).to eq(1)
101
+ expect(events.find(1).name).to eq("replacement")
102
+ end
103
+
92
104
  it "executes an ActiveRecord association join with qualified filtering" do
93
105
  stub_const("RubydbAccount", Class.new(ActiveRecord::Base) do
94
106
  self.table_name = "accounts"
@@ -258,8 +258,10 @@ module RubyDB
258
258
  row_count: inserted.sum { |result| result[:row_count] },
259
259
  affected_rows: inserted.sum { |result| result[:affected_rows] },
260
260
  row_ids: inserted.filter_map { |result| result[:row_id] },
261
- row_id: inserted.reverse_each.map { |result| result[:row_id] }.compact.first,
262
- message: "INSERT #{inserted.sum { |result| result[:affected_rows] }}"
261
+ row_id: inserted.reverse_each.map { |result| result[:row_id] }.compact.first,
262
+ inserted_ids: inserted.filter_map { |result| result[:inserted_id] },
263
+ inserted_id: inserted.reverse_each.map { |result| result[:inserted_id] }.compact.first,
264
+ message: "INSERT #{inserted.sum { |result| result[:affected_rows] }}"
263
265
  }
264
266
  rescue Exception
265
267
  @engine.rollback_transaction if implicit_transaction && @engine.in_transaction?
@@ -277,12 +279,18 @@ module RubyDB
277
279
 
278
280
  # Insert into engine
279
281
  table_columns = @engine.table_columns(table_name)
280
- row_id = @engine.insert_row(table_name, table_columns, row_data)
282
+ row_id = @engine.insert_row(table_name, table_columns, row_data)
283
+ primary_key = table_columns.find(&:primary_key?)
284
+ inserted_row = @engine.select_row(table_name, row_id, table_columns)
285
+ inserted_id = if primary_key && inserted_row
286
+ inserted_row[primary_key.name] || inserted_row[primary_key.name.to_sym]
287
+ end
281
288
 
282
289
  {
283
290
  row_count: 1,
284
291
  affected_rows: 1,
285
- row_id: row_id,
292
+ row_id: row_id,
293
+ inserted_id: inserted_id,
286
294
  message: "INSERT 1"
287
295
  }
288
296
  rescue DatabaseError => error
@@ -313,7 +321,8 @@ module RubyDB
313
321
  end
314
322
  row_id = existing[:_row_id] || existing["_row_id"]
315
323
  @engine.update_row(table_name, row_id, values)
316
- { row_count: 1, affected_rows: 1, row_id: row_id, message: "INSERT 0 UPDATE 1" }
324
+ inserted_id = target.map { |column| existing[column] || existing[column.to_sym] }.first
325
+ { row_count: 1, affected_rows: 1, row_id: row_id, inserted_id: inserted_id, message: "INSERT 0 UPDATE 1" }
317
326
  end
318
327
 
319
328
  def execute_update(plan)
@@ -7,7 +7,7 @@ module RubyDB
7
7
  include Enumerable
8
8
 
9
9
  attr_reader :columns, :rows, :row_count, :affected_rows
10
- attr_reader :command_tag, :statement_id, :row_id
10
+ attr_reader :command_tag, :statement_id, :row_id, :inserted_id
11
11
 
12
12
  def initialize(result)
13
13
  @columns = result[:columns] || result[:column_names] || []
@@ -16,7 +16,8 @@ module RubyDB
16
16
  @affected_rows = result[:affected_rows] || 0
17
17
  @command_tag = result[:command_tag] || "SELECT"
18
18
  @statement_id = result[:statement_id]
19
- @row_id = result[:row_id]
19
+ @row_id = result[:row_id]
20
+ @inserted_id = result[:inserted_id]
20
21
  @transaction_id = result[:transaction_id]
21
22
  @success = result[:success] != false
22
23
  @error = result[:error]
@@ -84,7 +85,9 @@ module RubyDB
84
85
  rows: @rows,
85
86
  row_count: @row_count,
86
87
  affected_rows: @affected_rows,
87
- command_tag: @command_tag,
88
+ command_tag: @command_tag,
89
+ row_id: @row_id,
90
+ inserted_id: @inserted_id,
88
91
  success: @success,
89
92
  error: @error,
90
93
  execution_time_ms: @execution_time
@@ -686,11 +686,13 @@ module RubyDB
686
686
  end
687
687
  end
688
688
 
689
- @active_transactions = parsed[:active_transactions] || {}
690
- @committed_transactions = Set.new(parsed[:committed_transactions] || [])
691
- @aborted_transactions = Set.new(parsed[:aborted_transactions] || [])
692
- @next_version_id = parsed[:next_version_id] || 1
693
- @row_version_chains = parsed[:row_version_chains] || {}
689
+ @active_transactions = normalize_numeric_keyed_hash(parsed[:active_transactions])
690
+ @committed_transactions = Set.new(parsed[:committed_transactions] || [])
691
+ @aborted_transactions = Set.new(parsed[:aborted_transactions] || [])
692
+ @next_version_id = parsed[:next_version_id] || 1
693
+ @row_version_chains = normalize_numeric_keyed_hash(parsed[:row_version_chains]) do |versions|
694
+ Array(versions).map(&:to_i)
695
+ end
694
696
 
695
697
  # Clean up any invalid data
696
698
  @active_transactions.each do |tx_id, info|
@@ -727,24 +729,35 @@ module RubyDB
727
729
 
728
730
  # Write to a temp file first, then rename
729
731
  temp_path = "#{visibility_path}.tmp"
730
- File.write(temp_path, JSON.generate(data))
731
- FileUtils.mv(temp_path, visibility_path)
732
-
733
- true
734
- rescue => e
735
- false
736
- end
737
- end
738
- end
732
+ File.write(temp_path, JSON.generate(data))
733
+ FileUtils.mv(temp_path, visibility_path)
734
+
735
+ true
736
+ rescue => e
737
+ File.delete(temp_path) if defined?(temp_path) && File.file?(temp_path)
738
+ raise StorageError, "Failed to flush visibility map: #{e.message}"
739
+ end
740
+ end
741
+ end
739
742
 
740
743
  # Remove a row from disk storage
741
744
  def remove_row_from_disk(row_id)
742
745
  true
743
746
  end
744
747
 
745
- def remember_version(row_id, info)
746
- @version_history[row_id][info[:version].to_i] = info.dup
747
- end
748
+ def remember_version(row_id, info)
749
+ @version_history[row_id][info[:version].to_i] = info.dup
750
+ end
751
+
752
+ # JSON object keys are strings. Normalize numeric-keyed runtime maps on
753
+ # load so a reopened database cannot accumulate both "1" and 1 keys.
754
+ def normalize_numeric_keyed_hash(value)
755
+ normalized = {}
756
+ (value || {}).each do |key, entry|
757
+ normalized[key.to_s.to_i] = block_given? ? yield(entry) : entry
758
+ end
759
+ normalized
760
+ end
748
761
 
749
762
  def normalize_loaded_info(info)
750
763
  info.each_with_object({}) do |(key, value), normalized|
@@ -11,13 +11,13 @@ module RubyDB
11
11
  # - MAJOR: Incompatible API changes
12
12
  # - MINOR: Backwards-compatible new functionality
13
13
  # - PATCH: Backwards-compatible bug fixes
14
- VERSION = "0.1.2"
14
+ VERSION = "0.1.4"
15
15
 
16
16
  # Version components for easy access
17
17
  module Version
18
18
  MAJOR = 0
19
19
  MINOR = 1
20
- PATCH = 2
20
+ PATCH = 4
21
21
  PRE = nil # e.g., "alpha", "beta", "rc1"
22
22
 
23
23
  def self.to_s
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubydb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aldane Hutchinson