oinky 0.1.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 (41) hide show
  1. data/LICENSE +22 -0
  2. data/README.md +141 -0
  3. data/ext/extconf.rb +79 -0
  4. data/ext/include/oinky.h +424 -0
  5. data/ext/include/oinky.hpp +63 -0
  6. data/ext/include/oinky/nky_base.hpp +1116 -0
  7. data/ext/include/oinky/nky_core.hpp +1603 -0
  8. data/ext/include/oinky/nky_cursor.hpp +665 -0
  9. data/ext/include/oinky/nky_dialect.hpp +107 -0
  10. data/ext/include/oinky/nky_error.hpp +164 -0
  11. data/ext/include/oinky/nky_fixed_table.hpp +710 -0
  12. data/ext/include/oinky/nky_handle.hpp +334 -0
  13. data/ext/include/oinky/nky_index.hpp +1038 -0
  14. data/ext/include/oinky/nky_log.hpp +15 -0
  15. data/ext/include/oinky/nky_merge_itr.hpp +403 -0
  16. data/ext/include/oinky/nky_model.hpp +110 -0
  17. data/ext/include/oinky/nky_pool.hpp +760 -0
  18. data/ext/include/oinky/nky_public.hpp +808 -0
  19. data/ext/include/oinky/nky_serializer.hpp +1625 -0
  20. data/ext/include/oinky/nky_strtable.hpp +504 -0
  21. data/ext/include/oinky/nky_table.hpp +1996 -0
  22. data/ext/nky_lib.cpp +390 -0
  23. data/ext/nky_lib_core.hpp +212 -0
  24. data/ext/nky_lib_index.cpp +158 -0
  25. data/ext/nky_lib_table.cpp +224 -0
  26. data/lib/oinky.rb +1284 -0
  27. data/lib/oinky/compiler.rb +106 -0
  28. data/lib/oinky/cpp_emitter.rb +311 -0
  29. data/lib/oinky/dsl.rb +167 -0
  30. data/lib/oinky/error.rb +19 -0
  31. data/lib/oinky/modelbase.rb +12 -0
  32. data/lib/oinky/nbuffer.rb +152 -0
  33. data/lib/oinky/normalize.rb +132 -0
  34. data/lib/oinky/oc_builder.rb +44 -0
  35. data/lib/oinky/query.rb +193 -0
  36. data/lib/oinky/rb_emitter.rb +147 -0
  37. data/lib/oinky/shard.rb +40 -0
  38. data/lib/oinky/testsup.rb +104 -0
  39. data/lib/oinky/version.rb +9 -0
  40. data/oinky.gemspec +36 -0
  41. metadata +120 -0
@@ -0,0 +1,147 @@
1
+ # This source is distributed under the terms of the MIT License. Refer
2
+ # to the 'LICENSE' file for details.
3
+ #
4
+ # Copyright (c) Jacob Lacouture, 2012
5
+
6
+ require 'oinky/normalize.rb'
7
+
8
+ module Oinky
9
+ module Model
10
+ class Ruby
11
+ def initialize(schema)
12
+ @schema = Oinky::Model.normalize_schema(schema)
13
+ end
14
+
15
+ def rbify_method(str)
16
+ str = str.gsub(/[^a-z0-9A-Z_]/,'_')
17
+ return str unless /$[^a-z]/ =~ str
18
+ return "_" + str
19
+ end
20
+
21
+ def rbify_chars(str)
22
+ str = str.gsub(/[^a-z0-9A-Z_]/,'_')
23
+ end
24
+
25
+ def schema_classname
26
+ cn = @schema[:classname]
27
+ return "S#{@schema[:name].to_s}Adapter" unless cn
28
+ return cn[:ruby] if cn.is_a? Hash
29
+ return cn.call(:ruby) if cn.is_a? Proc
30
+ return cn.to_s
31
+ end
32
+
33
+ def tbl_classname(str) ; "Table_#{rbify_chars(str)}" ; end
34
+ #def handle_from_tablename(str) ; "#{cppify(str)}_handle" ; end
35
+ #def accessor_from_tablename(str) ; cppify(str) ; end
36
+
37
+ def default_value_expression(col)
38
+ value = col
39
+ value = col[:default] if col.is_a? Hash
40
+ return "DateTime.parse(#{value.to_s.inspect})" if value.is_a? DateTime
41
+ return value.inspect
42
+ end
43
+
44
+ def to_ruby(h)
45
+ str = ['{']
46
+ h.each {|k,v|
47
+ str << k.inspect + "=>"
48
+ if v.is_a? Hash
49
+ str << to_ruby(v)
50
+ elsif v.is_a? DateTime
51
+ str << default_value_expression(v)
52
+ else
53
+ str << v.inspect
54
+ end
55
+ str << ','
56
+ }
57
+ str << '}'
58
+ return str * ''
59
+ end
60
+
61
+ # Generate a string, containing the Ruby code for the model classes.
62
+ # this can be eval'd directly in ruby, or saved and required
63
+ # more normally. Supporting both lends itself to static analysis
64
+ # and transformation moreso than if we solved this with ruby
65
+ # metaprogramming.
66
+ #
67
+ def emit
68
+ p = Oinky::Detail::Builder.new
69
+ schemacls = self.schema_classname
70
+ p.next("class #{schemacls}", "end ##{schemacls}") {
71
+ tables = @schema[:tables]
72
+ p.next("def self.schema", "end") {
73
+ p << to_ruby(@schema.merge(:classname=>schemacls))
74
+ }
75
+ p.next("def schema", "end") {
76
+ p << 'self.class.schema'
77
+ }
78
+ p << ''
79
+ p.next("def inspect", "end") {
80
+ # Hide all the internal state, which gets really obnoxious
81
+ p << '"#<#{self.class}:0x#{self.__id__.to_s(16)}>"'
82
+ # This is just a bit long to include.
83
+ #schema=#{self.schema.to_s}>"'
84
+ }
85
+ p << ''
86
+ tables.each{|nm,t|
87
+ clsname = tbl_classname(nm)
88
+ p.next("class #{clsname}","end ##{clsname}") {
89
+ p << "include Oinky::Model::Table"
90
+ p << ''
91
+ p.next("def initialize(tbl)","end") {
92
+ p << "@tbl = tbl"
93
+ p << "self.class.migrate(tbl)"
94
+ p << ''
95
+ t[:indices].each {|k,v|
96
+ accessor = rbify_method(v[:accessor])
97
+ p << "@#{accessor} = @tbl.indices[#{k.inspect}]"
98
+ }
99
+ }
100
+ p << ''
101
+ p.next("def self.migrate(tbl)","end") {
102
+ p.next("tbl.db.atomic {", "}") {
103
+ p << "cols = tbl.columns"
104
+ t[:columns].each{|k,v|
105
+ p.next("if c = cols[#{k.inspect}]","end") {
106
+ p << "raise OinkyException.new('Mismatched column type for column [#{k.to_s}]') unless c[:type] == :#{v[:type]}"
107
+ p.write("else",-1)
108
+ p << "tbl.add_column(#{k.inspect},#{v[:type].inspect},#{default_value_expression(v)})"
109
+ }
110
+ }
111
+ p << "ixs = tbl.indices"
112
+ t[:indices].each{|k,v|
113
+ p.next("unless ixs[#{k.inspect}]","end") {
114
+ p << "tbl.add_index(:name=>#{k.inspect},:unique=>#{v[:unique]},:columns=>#{v[:columns]})"
115
+ }
116
+ }
117
+ }
118
+ }
119
+ p << ''
120
+ t[:indices].each {|k,v|
121
+ accessor = rbify_method(v[:accessor])
122
+ p << "attr_accessor :#{accessor}"
123
+ }
124
+ p << "def table ; @tbl ; end"
125
+ p << "alias :t :table"
126
+ } #tablecls
127
+ p << ''
128
+ } #tables.each
129
+
130
+ p.next("def initialize(db, prefix = '')","end") {
131
+ tables.each{|nm,t|
132
+ accessor = rbify_method(t[:accessor])
133
+ p << "tn = prefix + #{nm.inspect}"
134
+ p << "db[tn] or db.create_table(tn)"
135
+ p << "@#{accessor} = #{tbl_classname(nm)}.new(db[tn])"
136
+ }
137
+ }
138
+ tables.each{|nm,t|
139
+ accessor = rbify_method(t[:accessor])
140
+ p << "attr_accessor :#{accessor}"
141
+ }
142
+ } #schemacls
143
+ return p.format
144
+ end #emit
145
+ end #class Ruby
146
+ end #Model
147
+ end #Oinky
@@ -0,0 +1,40 @@
1
+ s = build_schema {
2
+ create_table(:foo) {
3
+ string :key
4
+ string :type
5
+ string :payload
6
+
7
+ datetime :timestamp
8
+
9
+ # Create an index on this table (called 'id') which is unique
10
+ # over the given columnset. The resolver requires that this field
11
+ # be identified.
12
+ identity [:key]
13
+
14
+ # When facing two identical
15
+ resolve_on :timestamp
16
+
17
+ #shard_on :key
18
+ #shard_on lambda {|r| r[:key]}
19
+ }
20
+
21
+ create_table(:footype) {
22
+ # Make sure that on any shard, all types are available
23
+ shard_join([:foo,:type], [:footype,:type])
24
+ }
25
+
26
+ # This is how we generate the names of the storage objects.
27
+ # fooset_0, fooset_1, etc.
28
+ shardset_name :fooset
29
+
30
+ }
31
+
32
+ # A table spans many shards. To search for an exact row, we can generate
33
+ # a shard key from the given row. However, to enumerate a range of rows,
34
+ # we need to generate a range of shard keys. (The range may only be one
35
+ # shard...but in general we'll need to do enumerations across shards).
36
+ # The atlantic DB is 150BM with just half of 2012.
37
+
38
+ #In the implementation, we just need a set of shard keys.
39
+
40
+
@@ -0,0 +1,104 @@
1
+ # Test-related support code for oinky
2
+
3
+ module Oinky
4
+ module Test
5
+
6
+ class DBCompare
7
+ def check_eq(l,r)
8
+ raise RuntimeError.new("Inequality: [#{l.inspect}] != [#{r.inspect}] Context:[#{@context.to_s}]") unless l == r
9
+ end
10
+
11
+ def kv_compare(ls, rs, fn)
12
+ check_eq(ls.size,rs.size)
13
+ ls.each{|k,v|
14
+ @context.push k
15
+ fn.call(v, rs[k])
16
+ @context.pop
17
+ }
18
+ end
19
+ def seq_compare(ls, rs, fn, neq_prev = false)
20
+ l = ls.each
21
+ r = rs.each
22
+ count = 0
23
+ last = self
24
+ begin
25
+ while true
26
+ left = true
27
+ lv = l.next
28
+ left = false
29
+ rv = r.next
30
+ @context.push count
31
+ fn.call(lv,rv)
32
+
33
+ # If we are verifying that each list is also unique, then
34
+ # check against the previous value
35
+ if neq_prev and (last != self)
36
+ # We EXPECT this comparison to raise NEQ
37
+ peq = false
38
+ begin
39
+ fn.call(last, lv)
40
+ peq = true
41
+ rescue
42
+ end
43
+ if peq
44
+ check_eq("Sequence is not unique",nil)
45
+ end
46
+ end
47
+ last = lv
48
+
49
+ @context.pop
50
+ count += 1
51
+ end
52
+ rescue StopIteration
53
+ # Left should have raised.
54
+ check_eq(left, true)
55
+ end
56
+ # verify that the right enumerator is also done
57
+ begin
58
+ r.next
59
+ # These are obviously not equal if the above didn't raise.
60
+ check_eq("nonempty enumerator",nil)
61
+ rescue StopIteration
62
+ end
63
+ end
64
+
65
+ def table_compare(l, r)
66
+ kv_compare(l.columns, r.columns, self.method(:check_eq))
67
+ check_eq(l.row_count, r.row_count)
68
+ # Table ordering is not guaranteed to match. We have to sort.
69
+ # However, we may have to sort over variant types, so convert
70
+ # everything to a string before ordering.
71
+ seq_compare(
72
+ l.cursor_each.map {|c| c.select_all_values.map{|v| v.to_s} }.sort,
73
+ r.cursor_each.map {|c| c.select_all_values.map{|v| v.to_s} }.sort,
74
+ self.method(:check_eq))
75
+ kv_compare(l.indices, r.indices, self.method(:index_compare))
76
+ end
77
+
78
+ def index_compare(l, r)
79
+ kv_compare(l.definition, r.definition, self.method(:check_eq))
80
+ check_eq(l.row_count, r.row_count)
81
+ # We will only compare the indexed columns. Building the selectors
82
+ # outside of the loop saves work.
83
+ ls = l.selector
84
+ rs = r.selector
85
+ seq_compare(l.each(ls), r.each(rs), self.method(:check_eq), l.definition[:unique])
86
+ end
87
+
88
+ def compare(l, r)
89
+ begin
90
+ kv_compare(l.tables, r.tables, self.method(:table_compare))
91
+ rescue StandardError => e
92
+ puts "Exception comparing database instances: [#{@context}]"
93
+ raise e
94
+ end
95
+ self
96
+ end
97
+ def initialize
98
+ @context = []
99
+ end
100
+ end
101
+
102
+
103
+ end #module Test
104
+ end #module Oinky
@@ -0,0 +1,9 @@
1
+ # This source is distributed under the terms of the MIT License. Refer
2
+ # to the 'LICENSE' file for details.
3
+ #
4
+ # Copyright (c) Jacob Lacouture, 2012
5
+
6
+ module Oinky
7
+ VERSION = "0.1.0"
8
+ end
9
+
@@ -0,0 +1,36 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ class T
4
+ def self.expand_dirname(__file__)
5
+ File.expand_path(__file__).sub(/\/[^\/]+$/,'')
6
+ end
7
+ end
8
+
9
+ gem_root = T.expand_dirname(__FILE__)
10
+
11
+ require(File.join(gem_root,'lib/oinky/version.rb'))
12
+
13
+ Gem::Specification.new do |s|
14
+ s.name = "oinky"
15
+ s.version = Oinky::VERSION
16
+ # There's no version of FFI that's consistent across MRI, JRuby, and RBX.
17
+ # Hopefully that will get fixed, but for now it has to be MRI.
18
+ # The relevant issues are FFI::Struct.by_value and MemoryPointer.get_int64
19
+ s.platform = Gem::Platform::RUBY #['mri_19'] <== this isn't recognized??
20
+ s.authors = ["Jacob Lacouture"]
21
+ s.email = ["jacobx984@gmail.com"]
22
+ s.homepage = "http://github.com/oinky/oinky"
23
+ s.summary = "Relational data serialization library"
24
+ s.description = "Oinky is an in-memory relational database which can serialize and mount extremely quickly, making it an ideal solution for relational microsharding atop distributed key-value storage."
25
+
26
+ s.rubyforge_project = "oinky"
27
+
28
+ s.add_development_dependency "rake"
29
+ s.add_dependency('ffi','>= 1.0.11')
30
+
31
+ s.files = Dir.glob("**/*")
32
+ s.executables = []
33
+ s.extensions << "ext/extconf.rb"
34
+
35
+ s.require_path = 'lib'
36
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oinky
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jacob Lacouture
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: ffi
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.0.11
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.11
46
+ description: Oinky is an in-memory relational database which can serialize and mount
47
+ extremely quickly, making it an ideal solution for relational microsharding atop
48
+ distributed key-value storage.
49
+ email:
50
+ - jacobx984@gmail.com
51
+ executables: []
52
+ extensions:
53
+ - ext/extconf.rb
54
+ extra_rdoc_files: []
55
+ files:
56
+ - ext/extconf.rb
57
+ - ext/include/oinky/nky_base.hpp
58
+ - ext/include/oinky/nky_core.hpp
59
+ - ext/include/oinky/nky_cursor.hpp
60
+ - ext/include/oinky/nky_dialect.hpp
61
+ - ext/include/oinky/nky_error.hpp
62
+ - ext/include/oinky/nky_fixed_table.hpp
63
+ - ext/include/oinky/nky_handle.hpp
64
+ - ext/include/oinky/nky_index.hpp
65
+ - ext/include/oinky/nky_log.hpp
66
+ - ext/include/oinky/nky_merge_itr.hpp
67
+ - ext/include/oinky/nky_model.hpp
68
+ - ext/include/oinky/nky_pool.hpp
69
+ - ext/include/oinky/nky_public.hpp
70
+ - ext/include/oinky/nky_serializer.hpp
71
+ - ext/include/oinky/nky_strtable.hpp
72
+ - ext/include/oinky/nky_table.hpp
73
+ - ext/include/oinky.h
74
+ - ext/include/oinky.hpp
75
+ - ext/nky_lib.cpp
76
+ - ext/nky_lib_core.hpp
77
+ - ext/nky_lib_index.cpp
78
+ - ext/nky_lib_table.cpp
79
+ - lib/oinky/compiler.rb
80
+ - lib/oinky/cpp_emitter.rb
81
+ - lib/oinky/dsl.rb
82
+ - lib/oinky/error.rb
83
+ - lib/oinky/modelbase.rb
84
+ - lib/oinky/nbuffer.rb
85
+ - lib/oinky/normalize.rb
86
+ - lib/oinky/oc_builder.rb
87
+ - lib/oinky/query.rb
88
+ - lib/oinky/rb_emitter.rb
89
+ - lib/oinky/shard.rb
90
+ - lib/oinky/testsup.rb
91
+ - lib/oinky/version.rb
92
+ - lib/oinky.rb
93
+ - LICENSE
94
+ - oinky.gemspec
95
+ - README.md
96
+ homepage: http://github.com/oinky/oinky
97
+ licenses: []
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project: oinky
116
+ rubygems_version: 1.8.24
117
+ signing_key:
118
+ specification_version: 3
119
+ summary: Relational data serialization library
120
+ test_files: []