oklahoma_mixer 0.2.0 → 0.3.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.
Files changed (52) hide show
  1. data/CHANGELOG.rdoc +7 -0
  2. data/README.rdoc +4 -0
  3. data/Rakefile +1 -1
  4. data/TODO.rdoc +2 -3
  5. data/lib/oklahoma_mixer.rb +7 -20
  6. data/lib/oklahoma_mixer/array_list.rb +21 -7
  7. data/lib/oklahoma_mixer/array_list/c.rb +9 -1
  8. data/lib/oklahoma_mixer/b_tree_database.rb +25 -9
  9. data/lib/oklahoma_mixer/b_tree_database/c.rb +5 -0
  10. data/lib/oklahoma_mixer/cursor.rb +3 -0
  11. data/lib/oklahoma_mixer/cursor/c.rb +2 -0
  12. data/lib/oklahoma_mixer/error.rb +2 -0
  13. data/lib/oklahoma_mixer/extensible_string.rb +4 -2
  14. data/lib/oklahoma_mixer/extensible_string/c.rb +2 -0
  15. data/lib/oklahoma_mixer/fixed_length_database.rb +11 -3
  16. data/lib/oklahoma_mixer/fixed_length_database/c.rb +2 -0
  17. data/lib/oklahoma_mixer/hash_database.rb +24 -7
  18. data/lib/oklahoma_mixer/hash_database/c.rb +2 -0
  19. data/lib/oklahoma_mixer/hash_map.rb +42 -0
  20. data/lib/oklahoma_mixer/hash_map/c.rb +27 -0
  21. data/lib/oklahoma_mixer/query.rb +73 -0
  22. data/lib/oklahoma_mixer/query/c.rb +51 -0
  23. data/lib/oklahoma_mixer/table_database.rb +402 -0
  24. data/lib/oklahoma_mixer/table_database/c.rb +104 -0
  25. data/lib/oklahoma_mixer/utilities.rb +26 -1
  26. data/test/{b_tree_binary_data_test.rb → b_tree_database/b_tree_binary_data_test.rb} +2 -2
  27. data/test/{b_tree_tuning_test.rb → b_tree_database/b_tree_tuning_test.rb} +2 -2
  28. data/test/{cursor_based_iteration_test.rb → b_tree_database/cursor_based_iteration_test.rb} +2 -2
  29. data/test/{duplicate_storage_test.rb → b_tree_database/duplicate_storage_test.rb} +18 -12
  30. data/test/{binary_data_test.rb → core_database/binary_data_test.rb} +2 -2
  31. data/test/{file_system_test.rb → core_database/file_system_test.rb} +0 -0
  32. data/test/{getting_and_setting_keys_test.rb → core_database/getting_and_setting_keys_test.rb} +31 -60
  33. data/test/{iteration_test.rb → core_database/iteration_test.rb} +2 -2
  34. data/test/{transactions_test.rb → core_database/transactions_test.rb} +0 -0
  35. data/test/core_database/tuning_test.rb +31 -0
  36. data/test/{fixed_length_tuning_test.rb → fixed_length_database/fixed_length_tuning_test.rb} +0 -0
  37. data/test/{getting_and_setting_by_id_test.rb → fixed_length_database/getting_and_setting_by_id_test.rb} +8 -0
  38. data/test/{shared_binary_data.rb → shared/binary_data_tests.rb} +1 -1
  39. data/test/{tuning_test.rb → shared/hash_tuning_tests.rb} +18 -42
  40. data/test/{shared_iteration.rb → shared/iteration_tests.rb} +8 -7
  41. data/test/{key_range_test.rb → shared/key_range_test.rb} +0 -0
  42. data/test/{order_test.rb → shared/order_test.rb} +0 -0
  43. data/test/shared/storage_tests.rb +65 -0
  44. data/test/{top_level_interface_test.rb → shared/top_level_interface_test.rb} +39 -2
  45. data/test/{shared_tuning.rb → shared/tuning_tests.rb} +1 -1
  46. data/test/table_database/document_iteration_test.rb +22 -0
  47. data/test/table_database/document_storage_test.rb +225 -0
  48. data/test/table_database/index_test.rb +57 -0
  49. data/test/table_database/query_test.rb +866 -0
  50. data/test/table_database/table_tuning_test.rb +56 -0
  51. data/test/test_helper.rb +27 -0
  52. metadata +35 -36
@@ -0,0 +1,104 @@
1
+ require "oklahoma_mixer/utilities"
2
+
3
+ module OklahomaMixer
4
+ class TableDatabase < HashDatabase
5
+ module C # :nodoc:
6
+ extend OklahomaMixer::Utilities::FFIDSL
7
+
8
+ prefix :tctdb
9
+
10
+ const :OPTS, %w[TLARGE TDEFLATE TBZIP TTCBS]
11
+ INDEXES = enum :TDBITLEXICAL, 0,
12
+ :TDBITDECIMAL, 1,
13
+ :TDBITTOKEN, 2,
14
+ :TDBITQGRAM, 3,
15
+ :TDBITOPT, 9998,
16
+ :TDBITVOID, 9999,
17
+ :TDBITKEEP, 1 << 24
18
+ FLAGS = enum :TDBQPPUT, 1 << 0,
19
+ :TDBQPOUT, 1 << 1,
20
+ :TDBQPSTOP, 1 << 24
21
+ SEARCHES = enum :TDBMSUNION, 0,
22
+ :TDBMSISECT, 1,
23
+ :TDBMSDIFF, 2
24
+
25
+
26
+ def_core_database_consts_and_funcs
27
+
28
+ func :name => :tune,
29
+ :args => [:pointer, :int64, :int8, :int8, OPTS],
30
+ :returns => :bool
31
+ func :name => :setcache,
32
+ :args => [:pointer, :int32, :int32, :int32],
33
+ :returns => :bool
34
+ func :name => :setxmsiz,
35
+ :args => [:pointer, :int64],
36
+ :returns => :bool
37
+ func :name => :setdfunit,
38
+ :args => [:pointer, :int32],
39
+ :returns => :bool
40
+ func :name => :optimize,
41
+ :args => [:pointer, :int64, :int8, :int8, OPTS],
42
+ :returns => :bool
43
+
44
+ func :name => :put,
45
+ :args => [:pointer, :pointer, :int, :pointer],
46
+ :returns => :bool
47
+ func :name => :putkeep,
48
+ :args => [:pointer, :pointer, :int, :pointer],
49
+ :returns => :bool
50
+ func :name => :putcat,
51
+ :args => [:pointer, :pointer, :int, :pointer],
52
+ :returns => :bool
53
+ call :name => :TCPDPROC,
54
+ :args => [:pointer, :int, :pointer, :pointer],
55
+ :returns => :pointer
56
+ func :name => :putproc,
57
+ :args => [:pointer, :pointer, :int, :pointer, :int, :TCPDPROC,
58
+ :pointer],
59
+ :returns => :bool
60
+ func :name => :addint,
61
+ :args => [:pointer, :pointer, :int, :int],
62
+ :returns => :int
63
+ func :name => :adddouble,
64
+ :args => [:pointer, :pointer, :int, :double],
65
+ :returns => :double
66
+ func :name => :out,
67
+ :args => [:pointer, :pointer, :int],
68
+ :returns => :bool
69
+ func :name => :get,
70
+ :args => [:pointer, :pointer, :int],
71
+ :returns => :pointer
72
+ func :name => :fwmkeys,
73
+ :args => [:pointer, :pointer, :int, :int],
74
+ :returns => :pointer
75
+ func :name => :genuid,
76
+ :args => :pointer,
77
+ :returns => :int64
78
+
79
+ func :name => :iterinit,
80
+ :args => :pointer,
81
+ :returns => :bool
82
+ func :name => :iternext,
83
+ :args => [:pointer, :pointer],
84
+ :returns => :pointer
85
+ func :name => :iternext3,
86
+ :args => :pointer,
87
+ :returns => :pointer
88
+
89
+ func :name => :setindex,
90
+ :args => [:pointer, :string, INDEXES],
91
+ :returns => :bool
92
+
93
+ call :name => :TDBQRYPROC,
94
+ :args => [:pointer, :int, :pointer, :pointer],
95
+ :returns => :int
96
+ func :name => :qryproc,
97
+ :args => [:pointer, :TDBQRYPROC, :pointer],
98
+ :returns => :bool
99
+ func :name => :metasearch,
100
+ :args => [:pointer, :int, SEARCHES],
101
+ :returns => :pointer
102
+ end
103
+ end
104
+ end
@@ -1,3 +1,5 @@
1
+ require "ffi"
2
+
1
3
  module OklahomaMixer
2
4
  module Utilities # :nodoc:
3
5
  module FFIDSL # :nodoc:
@@ -38,13 +40,15 @@ module OklahomaMixer
38
40
  end
39
41
 
40
42
  def read_from_func(func, *args)
43
+ no_free = args.shift if args.first.is_a?(Symbol) and
44
+ args.first == :no_free
41
45
  Utilities.temp_int do |size|
42
46
  begin
43
47
  args << size
44
48
  pointer = send(func, *args)
45
49
  pointer.address.zero? ? nil : pointer.get_bytes(0, size.get_int(0))
46
50
  ensure
47
- Utilities.free(pointer) if pointer
51
+ Utilities.free(pointer) if pointer and not no_free
48
52
  end
49
53
  end
50
54
  end
@@ -166,6 +170,13 @@ module OklahomaMixer
166
170
  end
167
171
  INT_MIN = int_min && int_min.to_i
168
172
 
173
+ def self.temp_pointer(size)
174
+ pointer = FFI::MemoryPointer.new(:pointer, size)
175
+ yield pointer
176
+ ensure
177
+ pointer.free if pointer
178
+ end
179
+
169
180
  def self.temp_int
170
181
  int = FFI::MemoryPointer.new(:int)
171
182
  yield int
@@ -179,6 +190,20 @@ module OklahomaMixer
179
190
  ensure
180
191
  xstr.free if xstr
181
192
  end
193
+
194
+ def self.temp_list(size)
195
+ list = ArrayList.new(size)
196
+ yield list
197
+ ensure
198
+ list.free if list
199
+ end
200
+
201
+ def self.temp_map
202
+ map = HashMap.new
203
+ yield map
204
+ ensure
205
+ map.free if map
206
+ end
182
207
 
183
208
  extend FFIDSL
184
209
 
@@ -1,5 +1,5 @@
1
1
  require "test_helper"
2
- require "shared_binary_data"
2
+ require "shared/binary_data_tests"
3
3
 
4
4
  class TestBTreeBinaryData < Test::Unit::TestCase
5
5
  def setup
@@ -15,7 +15,7 @@ class TestBTreeBinaryData < Test::Unit::TestCase
15
15
  remove_db_files
16
16
  end
17
17
 
18
- include SharedBinaryData
18
+ include BinaryDataTests
19
19
 
20
20
  def test_null_bytes_are_preserved_during_value_iteration
21
21
  @db.each_value do |value|
@@ -1,12 +1,12 @@
1
1
  require "test_helper"
2
- require "shared_tuning"
2
+ require "shared/tuning_tests"
3
3
 
4
4
  class TestBTreeTuning < Test::Unit::TestCase
5
5
  def teardown
6
6
  remove_db_files
7
7
  end
8
8
 
9
- include SharedTuning
9
+ include TuningTests
10
10
 
11
11
  def test_leaf_members_can_be_set_with_other_tuning_defaults
12
12
  members = rand(1_000) + 1
@@ -1,5 +1,5 @@
1
1
  require "test_helper"
2
- require "shared_iteration"
2
+ require "shared/iteration_tests"
3
3
 
4
4
  class TestCursorBasedIteration < Test::Unit::TestCase
5
5
  def setup
@@ -16,7 +16,7 @@ class TestCursorBasedIteration < Test::Unit::TestCase
16
16
  remove_db_files
17
17
  end
18
18
 
19
- include SharedIteration
19
+ include IterationTests
20
20
 
21
21
  def test_each_key_can_begin_iteration_at_a_passed_key
22
22
  @db.each_key("b") do |key|
@@ -11,9 +11,15 @@ class TestDuplicateStorage < Test::Unit::TestCase
11
11
  end
12
12
 
13
13
  def test_duplicates_can_be_stored
14
- assert(@db.store("Gray", "Dana", :dup), "Failed to store initial value")
15
- assert(@db.store("Gray", "James", :dup), "Failed to store duplicate value")
16
- assert_equal("Dana", @db["Gray"]) # always returns the first
14
+ assert_equal("Dana", @db.store("Gray", "Dana", :dup))
15
+ assert_equal("James", @db.store("Gray", "James", :dup))
16
+ assert_equal("Dana", @db["Gray"]) # always returns the first
17
+ end
18
+
19
+ def test_multiple_duplicates_can_be_stored_at_once
20
+ assert_equal(%w[a b c], @db.store(:letters, %w[a b c], :dup))
21
+ assert_equal("a", @db[:letters])
22
+ assert_equal(%w[a b c], @db.values(:letters))
17
23
  end
18
24
 
19
25
  def test_keys_are_a_unique_listing_not_showing_duplicates
@@ -27,9 +33,9 @@ class TestDuplicateStorage < Test::Unit::TestCase
27
33
  @db[:other] = "one record"
28
34
  assert_equal(["one record"], @db.values)
29
35
  assert_equal([ ], @db.values("Gray"))
30
- assert(@db.store("Gray", "Dana", :dup), "Failed to store initial value")
31
- assert_equal(%w[Dana], @db.values("Gray"))
32
- assert(@db.store("Gray", "James", :dup), "Failed to store duplicate value")
36
+ assert_equal("Dana", @db.store("Gray", "Dana", :dup))
37
+ assert_equal(%w[Dana], @db.values("Gray"))
38
+ assert_equal("James", @db.store("Gray", "James", :dup))
33
39
  assert_equal(%w[Dana James], @db.values("Gray"))
34
40
  end
35
41
 
@@ -57,12 +63,12 @@ class TestDuplicateStorage < Test::Unit::TestCase
57
63
 
58
64
  def test_size_can_be_scoped_to_a_key_to_retrieve_all_duplicates
59
65
  @db[:other] = "one record"
60
- assert_equal(1, @db.size)
61
- assert_equal(0, @db.size("Gray"))
62
- assert(@db.store("Gray", "Dana", :dup), "Failed to store initial value")
63
- assert_equal(1, @db.size("Gray"))
64
- assert(@db.store("Gray", "James", :dup), "Failed to store duplicate value")
65
- assert_equal(2, @db.size("Gray"))
66
+ assert_equal(1, @db.size)
67
+ assert_equal(0, @db.size("Gray"))
68
+ assert_equal("Dana", @db.store("Gray", "Dana", :dup))
69
+ assert_equal(1, @db.size("Gray"))
70
+ assert_equal("James", @db.store("Gray", "James", :dup))
71
+ assert_equal(2, @db.size("Gray"))
66
72
  end
67
73
 
68
74
  def test_duplicates_show_up_in_cursor_based_iteration_of_keys
@@ -1,5 +1,5 @@
1
1
  require "test_helper"
2
- require "shared_binary_data"
2
+ require "shared/binary_data_tests"
3
3
 
4
4
  class TestBinaryData < Test::Unit::TestCase
5
5
  def setup
@@ -14,7 +14,7 @@ class TestBinaryData < Test::Unit::TestCase
14
14
  remove_db_files
15
15
  end
16
16
 
17
- include SharedBinaryData
17
+ include BinaryDataTests
18
18
 
19
19
  def test_keys_and_values_can_be_read_with_null_bytes
20
20
  assert_equal(@value, @db[@key])
@@ -1,4 +1,5 @@
1
1
  require "test_helper"
2
+ require "shared/storage_tests"
2
3
 
3
4
  class TestGettingAndSettingKeys < Test::Unit::TestCase
4
5
  def setup
@@ -10,6 +11,8 @@ class TestGettingAndSettingKeys < Test::Unit::TestCase
10
11
  remove_db_files
11
12
  end
12
13
 
14
+ include StorageTests
15
+
13
16
  def test_a_key_value_pair_can_be_stored_and_fetched_from_the_database
14
17
  assert_equal("value", @db.store("key", "value"))
15
18
  assert_equal("value", @db.fetch("key")) # later
@@ -20,27 +23,6 @@ class TestGettingAndSettingKeys < Test::Unit::TestCase
20
23
  assert_equal("42", @db.fetch("num")) # effectively the same key
21
24
  end
22
25
 
23
- def test_fetching_a_missing_value_fails_with_an_index_error
24
- assert_raise(IndexError) do
25
- @db.fetch(:missing)
26
- end
27
- end
28
-
29
- def test_fetch_can_return_a_default_for_a_missing_value
30
- assert_equal(42, @db.fetch(:missing, 42))
31
- end
32
-
33
- def test_fetch_can_run_a_block_returning_its_result_for_a_missing_value
34
- assert_equal(:missing, @db.fetch(:missing) { |key| key })
35
- end
36
-
37
- def test_fetching_with_a_block_overrides_a_default_and_triggers_a_warning
38
- warning = capture_stderr do
39
- assert_equal(42, @db.fetch(:missing, 13) { 42 })
40
- end
41
- assert(!warning.empty?, "A warning was not issued for a default and block")
42
- end
43
-
44
26
  def test_storing_with_keep_mode_adds_a_value_only_if_it_didnt_already_exist
45
27
  assert(@db.store(:key, :new, :keep), "Failed to store a new key")
46
28
  assert_equal("new", @db.fetch(:key))
@@ -117,9 +99,28 @@ class TestGettingAndSettingKeys < Test::Unit::TestCase
117
99
  warning = capture_stderr do
118
100
  assert_equal(:value, db.store(:key, :value, :async)) # normal store
119
101
  end
120
- assert(!warning.empty?, "A warning was not issued for an unsupported mode")
102
+ assert( !warning.empty?,
103
+ "A warning was not issued for an unsupported mode" )
121
104
  assert_equal("value", db[:key])
122
105
  end
106
+
107
+ fdb do |db|
108
+ warning = capture_stderr do
109
+ assert_equal(:value, db.store(1, :value, :async)) # normal store
110
+ end
111
+ assert( !warning.empty?,
112
+ "A warning was not issued for an unsupported mode" )
113
+ assert_equal("value", db[1])
114
+ end
115
+
116
+ tdb do |db|
117
+ warning = capture_stderr do
118
+ assert_equal({ }, db.store(:key, { }, :async)) # normal store
119
+ end
120
+ assert( !warning.empty?,
121
+ "A warning was not issued for an unsupported mode" )
122
+ assert_equal({ }, db[:key])
123
+ end
123
124
  end
124
125
 
125
126
  def test_store_and_fetch_can_also_be_used_through_the_indexing_brackets
@@ -127,36 +128,6 @@ class TestGettingAndSettingKeys < Test::Unit::TestCase
127
128
  assert_equal("42", @db[:num])
128
129
  end
129
130
 
130
- def test_indexing_returns_nil_instead_of_failing_with_index_error
131
- assert_nil(@db[:missing])
132
- end
133
-
134
- def test_a_default_can_be_set_for_indexing_to_return
135
- @db.default = 42
136
- assert_equal(42, @db[:missing])
137
- end
138
-
139
- def test_the_indexing_default_will_be_run_if_it_is_a_proc
140
- @db.default = lambda { |key| "is #{key}" }
141
- assert_equal("is missing", @db[:missing])
142
- end
143
-
144
- def test_the_indexing_default_for_a_given_key_can_be_retrieved
145
- @db.default = lambda { |key| "is #{key}" }
146
- assert_equal("is ", @db.default)
147
- assert_equal("is missing", @db.default(:missing))
148
- end
149
-
150
- def test_the_indexing_default_can_be_changed
151
- assert_nil(@db[:missing])
152
- assert_equal(42, @db.default = 42)
153
- assert_equal(42, @db[:missing])
154
- proc = lambda { |key| fail RuntimeError, "%p not found" % [key]}
155
- assert_equal(proc, @db.default = proc)
156
- error = assert_raise(RuntimeError) { @db[:missing] }
157
- assert_equal(":missing not found", error.message)
158
- end
159
-
160
131
  def test_include_and_aliases_can_be_used_to_check_for_the_existance_of_a_key
161
132
  @db[:exist] = true
162
133
  %w[include? has_key? key? member?].each do |query|
@@ -228,14 +199,6 @@ class TestGettingAndSettingKeys < Test::Unit::TestCase
228
199
  assert_nil(@db[:key])
229
200
  end
230
201
 
231
- def test_delete_returns_nil_for_a_missing_key
232
- assert_nil(@db.delete(:missing))
233
- end
234
-
235
- def test_delete_can_be_passed_a_block_to_handle_missing_keys
236
- assert_equal(42, @db.delete(:missing) { 42 })
237
- end
238
-
239
202
  def test_clear_removes_all_keys_from_the_database
240
203
  @db.update(:a => 1, :b => 2, :c => 3)
241
204
  assert_equal(@db, @db.clear)
@@ -249,4 +212,12 @@ class TestGettingAndSettingKeys < Test::Unit::TestCase
249
212
  assert_equal(3, @db.size)
250
213
  assert_equal(3, @db.length)
251
214
  end
215
+
216
+ def test_empty_returns_true_while_no_pairs_are_in_the_database
217
+ assert(@db.empty?, "An empty database was not detected")
218
+ @db[:key] = :value
219
+ assert(!@db.empty?, "A non-empty database was reported empty")
220
+ @db.delete(:key)
221
+ assert(@db.empty?, "An empty database was not detected")
222
+ end
252
223
  end
@@ -1,5 +1,5 @@
1
1
  require "test_helper"
2
- require "shared_iteration"
2
+ require "shared/iteration_tests"
3
3
 
4
4
  class TestIteration < Test::Unit::TestCase
5
5
  def setup
@@ -16,5 +16,5 @@ class TestIteration < Test::Unit::TestCase
16
16
  remove_db_files
17
17
  end
18
18
 
19
- include SharedIteration
19
+ include IterationTests
20
20
  end
@@ -0,0 +1,31 @@
1
+ require "test_helper"
2
+ require "shared/hash_tuning_tests"
3
+
4
+ class TestTuning < Test::Unit::TestCase
5
+ def teardown
6
+ remove_db_files
7
+ end
8
+
9
+ include HashTuningTests
10
+
11
+ def test_limit_for_cached_records_can_be_set
12
+ limit = rand(1_000) + 1
13
+ assert_option_calls([:setcache, limit], :rcnum => limit)
14
+ end
15
+
16
+ def test_limit_for_cached_records_is_converted_to_an_int
17
+ assert_option_calls([:setcache, 42], :rcnum => "42")
18
+ end
19
+
20
+ #######
21
+ private
22
+ #######
23
+
24
+ def lib
25
+ OKMixer::HashDatabase::C
26
+ end
27
+
28
+ def db(*args, &block)
29
+ hdb(*args, &block)
30
+ end
31
+ end