oklahoma_mixer 0.2.0 → 0.3.0

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