diametric 0.0.4 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +21 -18
- data/Jarfile +15 -1
- data/README.md +22 -14
- data/Rakefile +17 -1
- data/bin/datomic-rest +33 -0
- data/bin/download-datomic +13 -0
- data/datomic_version.yml +4 -0
- data/diametric.gemspec +9 -6
- data/ext/diametric/DiametricCollection.java +147 -0
- data/ext/diametric/DiametricConnection.java +113 -0
- data/ext/diametric/DiametricDatabase.java +107 -0
- data/ext/diametric/DiametricEntity.java +90 -0
- data/ext/diametric/DiametricListenableFuture.java +47 -0
- data/ext/diametric/DiametricObject.java +66 -0
- data/ext/diametric/DiametricPeer.java +414 -0
- data/ext/diametric/DiametricService.java +102 -0
- data/ext/diametric/DiametricUUID.java +61 -0
- data/ext/diametric/DiametricUtils.java +183 -0
- data/lib/boolean_type.rb +3 -0
- data/lib/diametric.rb +24 -0
- data/lib/diametric/entity.rb +219 -14
- data/lib/diametric/generators/active_model.rb +2 -2
- data/lib/diametric/persistence.rb +0 -1
- data/lib/diametric/persistence/common.rb +28 -9
- data/lib/diametric/persistence/peer.rb +122 -87
- data/lib/diametric/persistence/rest.rb +4 -3
- data/lib/diametric/query.rb +94 -23
- data/lib/diametric/rest_service.rb +74 -0
- data/lib/diametric/service_base.rb +77 -0
- data/lib/diametric/transactor.rb +86 -0
- data/lib/diametric/version.rb +1 -1
- data/lib/diametric_service.jar +0 -0
- data/lib/value_enums.rb +8 -0
- data/spec/conf_helper.rb +55 -0
- data/spec/config/free-transactor-template.properties +73 -0
- data/spec/config/logback.xml +59 -0
- data/spec/data/seattle-data0.dtm +452 -0
- data/spec/data/seattle-data1.dtm +326 -0
- data/spec/developer_create_sample.rb +39 -0
- data/spec/developer_query_spec.rb +120 -0
- data/spec/diametric/config_spec.rb +1 -1
- data/spec/diametric/entity_spec.rb +263 -0
- data/spec/diametric/peer_api_spec.rb +147 -0
- data/spec/diametric/persistence/peer_many2many_spec.rb +76 -0
- data/spec/diametric/persistence/peer_spec.rb +13 -22
- data/spec/diametric/persistence/rest_spec.rb +12 -19
- data/spec/diametric/query_spec.rb +4 -5
- data/spec/diametric/rest_service_spec.rb +56 -0
- data/spec/diametric/transactor_spec.rb +68 -0
- data/spec/integration_spec.rb +5 -3
- data/spec/parent_child_sample.rb +42 -0
- data/spec/peer_integration_spec.rb +106 -22
- data/spec/peer_seattle_spec.rb +200 -0
- data/spec/rc2013_seattle_big.rb +82 -0
- data/spec/rc2013_seattle_small.rb +60 -0
- data/spec/rc2013_simple_sample.rb +72 -0
- data/spec/seattle_integration_spec.rb +106 -0
- data/spec/simple_validation_sample.rb +31 -0
- data/spec/spec_helper.rb +31 -45
- data/spec/support/entities.rb +157 -0
- data/spec/support/gen_entity_class.rb +2 -0
- data/spec/support/persistence_examples.rb +9 -5
- data/spec/test_version_file.yml +4 -0
- metadata +131 -75
- data/Jarfile.lock +0 -134
- data/lib/jrclj.rb +0 -63
@@ -8,8 +8,8 @@ module Diametric
|
|
8
8
|
"#{klass}.all"
|
9
9
|
end
|
10
10
|
|
11
|
-
def self.find(klass, params=nil)
|
12
|
-
"#{klass}.get(#{params})"
|
11
|
+
def self.find(klass, params=nil, connection=nil, resolve=false)
|
12
|
+
"#{klass}.get(#{params}, #{connection}, #{resolve})"
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.build(klass, params=nil)
|
@@ -36,25 +36,44 @@ module Diametric
|
|
36
36
|
end
|
37
37
|
|
38
38
|
module ClassMethods
|
39
|
-
def create_schema
|
40
|
-
|
39
|
+
def create_schema(connection=nil)
|
40
|
+
if self.instance_variable_get("@peer")
|
41
|
+
connection ||= Diametric::Persistence::Peer.connect
|
42
|
+
connection.transact(schema)
|
43
|
+
else
|
44
|
+
transact(schema)
|
45
|
+
end
|
41
46
|
end
|
42
47
|
|
43
|
-
def all
|
44
|
-
|
48
|
+
def all(connection=nil, resolve=false)
|
49
|
+
if self.instance_variable_get("@peer")
|
50
|
+
connection ||= Diametric::Persistence::Peer.connect
|
51
|
+
end
|
52
|
+
Diametric::Query.new(self, connection, resolve).all
|
45
53
|
end
|
46
54
|
|
47
|
-
def first(conditions = {})
|
48
|
-
|
55
|
+
def first(conditions = {}, connection=nil, resolve=false)
|
56
|
+
if self.instance_variable_get("@peer")
|
57
|
+
connection ||= Diametric::Persistence::Peer.connect
|
58
|
+
end
|
59
|
+
where(conditions, connection, resolve).first
|
49
60
|
end
|
50
61
|
|
51
|
-
def where(conditions = {})
|
52
|
-
|
62
|
+
def where(conditions = {}, connection=nil, resolve=false)
|
63
|
+
if self.instance_variable_get("@peer")
|
64
|
+
connection ||= Diametric::Persistence::Peer.connect
|
65
|
+
end
|
66
|
+
query = Diametric::Query.new(self, connection, resolve)
|
53
67
|
query.where(conditions)
|
54
68
|
end
|
55
69
|
|
56
70
|
def filter(*filter)
|
57
|
-
|
71
|
+
if self.instance_variable_get("@peer")
|
72
|
+
connection = Diametric::Persistence::Peer.connect
|
73
|
+
else
|
74
|
+
connection = nil
|
75
|
+
end
|
76
|
+
query = Diametric::Query.new(self, connection, true)
|
58
77
|
query.filter(*filter)
|
59
78
|
end
|
60
79
|
end
|
@@ -1,118 +1,153 @@
|
|
1
|
-
unless defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
2
|
-
raise "This module requires the use of JRuby."
|
3
|
-
end
|
4
|
-
|
5
|
-
require 'diametric'
|
6
1
|
require 'diametric/persistence/common'
|
7
2
|
|
8
|
-
require 'java'
|
9
|
-
require 'lock_jar'
|
10
|
-
lockfile = File.expand_path( "../../../../Jarfile.lock", __FILE__ )
|
11
|
-
# Loads the classpath with Jars from the lockfile
|
12
|
-
LockJar.load(lockfile)
|
13
|
-
|
14
|
-
require 'jrclj'
|
15
|
-
java_import "clojure.lang.Keyword"
|
16
|
-
|
17
3
|
module Diametric
|
18
4
|
module Persistence
|
19
5
|
module Peer
|
20
|
-
include_package "datomic"
|
21
6
|
|
22
|
-
|
23
|
-
|
7
|
+
def save(connection=nil)
|
8
|
+
return false unless valid?
|
9
|
+
return true unless changed?
|
10
|
+
connection ||= Diametric::Persistence::Peer.connect
|
24
11
|
|
25
|
-
|
26
|
-
|
27
|
-
base.send(:extend, ClassMethods)
|
28
|
-
@persisted_classes.add(base)
|
29
|
-
end
|
12
|
+
parsed_data = []
|
13
|
+
parse_tx_data(tx_data, parsed_data)
|
30
14
|
|
31
|
-
|
32
|
-
@connection
|
33
|
-
end
|
15
|
+
map = connection.transact(parsed_data).get
|
34
16
|
|
35
|
-
|
36
|
-
@persisted_classes.each do |klass|
|
37
|
-
klass.create_schema
|
38
|
-
end
|
39
|
-
end
|
17
|
+
resolve_changes([self], map)
|
40
18
|
|
41
|
-
|
42
|
-
|
19
|
+
map
|
20
|
+
end
|
43
21
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
@connection = Java::Datomic::Peer.connect(uri)
|
22
|
+
def resolve_tempid(map, id)
|
23
|
+
if id.to_s =~ /-\d+/
|
24
|
+
return Diametric::Persistence::Peer.resolve_tempid(map, id)
|
48
25
|
end
|
26
|
+
return id
|
27
|
+
end
|
49
28
|
|
50
|
-
|
51
|
-
|
29
|
+
def parse_tx_data(data, result)
|
30
|
+
queue = []
|
31
|
+
data.each do |c_data|
|
32
|
+
if c_data.is_a? Hash
|
33
|
+
parse_hash_data(c_data, result, queue)
|
34
|
+
elsif c_data.is_a? Array
|
35
|
+
parse_array_data(c_data, result)
|
36
|
+
end
|
52
37
|
end
|
38
|
+
parse_tx_data(queue, result) unless queue.empty?
|
39
|
+
end
|
53
40
|
|
54
|
-
|
55
|
-
|
56
|
-
|
41
|
+
def parse_hash_data(c_hash, result, queue)
|
42
|
+
hash = {}
|
43
|
+
c_hash.each do |c_key, c_value|
|
44
|
+
if c_value.respond_to?(:tx_data)
|
45
|
+
if c_value.tx_data.empty?
|
46
|
+
hash[c_key] = c_value.dbid
|
47
|
+
else
|
48
|
+
c_value.dbid = c_value.tempid
|
49
|
+
hash[c_key] = c_value.dbid
|
50
|
+
queue << c_value.tx_data.first
|
51
|
+
end
|
52
|
+
elsif c_value.is_a? Set
|
53
|
+
set_value = Set.new
|
54
|
+
c_value.each do |s|
|
55
|
+
if s.respond_to?(:tx_data) && s.tx_data.empty?
|
56
|
+
set_value << s.dbid
|
57
|
+
elsif s.respond_to?(:tx_data)
|
58
|
+
set_value << s.tx_data[":db/id"]
|
59
|
+
parsed_tx_data(s, result)
|
60
|
+
else
|
61
|
+
set_value << s
|
62
|
+
end
|
63
|
+
end
|
64
|
+
hash[c_key] = set_value
|
65
|
+
elsif c_value.respond_to?(:eid)
|
66
|
+
hash[c_key] = c_value.eid
|
67
|
+
else
|
68
|
+
hash[c_key] = c_value
|
69
|
+
end
|
70
|
+
end
|
71
|
+
result << hash
|
72
|
+
end
|
57
73
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
74
|
+
def parse_array_data(c_array, result)
|
75
|
+
array = []
|
76
|
+
c_array.each do |c_value|
|
77
|
+
if c_value.respond_to?(:to_a)
|
78
|
+
a_values = []
|
79
|
+
c_value.to_a.each do |a_value|
|
80
|
+
if a_value.respond_to?(:tx_data) && a_value.tx_data.empty?
|
81
|
+
a_values << a_value.dbid
|
82
|
+
else
|
83
|
+
a_values << a_value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
array << a_values
|
87
|
+
else
|
88
|
+
array << c_value
|
89
|
+
end
|
62
90
|
end
|
91
|
+
result << array
|
92
|
+
end
|
63
93
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
94
|
+
def resolve_changes(parents, map)
|
95
|
+
queue = []
|
96
|
+
parents.each do |child|
|
97
|
+
child.attributes.each do |a_key, a_val|
|
98
|
+
if a_val.respond_to?(:tx_data)
|
99
|
+
queue << a_val
|
100
|
+
end
|
69
101
|
end
|
70
|
-
|
71
|
-
|
102
|
+
child.instance_variable_set("@previously_changed", child.changes)
|
103
|
+
child.changed_attributes.clear
|
104
|
+
child.dbid = resolve_tempid(map, child.dbid)
|
105
|
+
child.instance_variable_set("@tx_map", map)
|
72
106
|
end
|
107
|
+
resolve_changes(queue, map) unless queue.empty?
|
108
|
+
end
|
73
109
|
|
74
|
-
|
75
|
-
|
76
|
-
|
110
|
+
def retract_entity(dbid)
|
111
|
+
Diametric::Persistence::Peer.retract_entity(dbid)
|
112
|
+
end
|
77
113
|
|
78
|
-
|
79
|
-
|
114
|
+
def method_missing(method_name, *args, &block)
|
115
|
+
result = /(.+)_from_this_(.+)/.match(method_name)
|
116
|
+
if result
|
117
|
+
query_string = ":#{result[1]}/_#{result[2]}"
|
118
|
+
entities = Diametric::Persistence::Peer.reverse_q(args[0].db, self.dbid, query_string)
|
119
|
+
entities.collect {|e| self.class.from_dbid_or_entity(e, args[0])}
|
120
|
+
else
|
121
|
+
super
|
80
122
|
end
|
81
123
|
end
|
82
124
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
# @return [ true, false ] True is success, false if not.
|
91
|
-
def save
|
92
|
-
return false unless valid?
|
93
|
-
return true unless changed?
|
94
|
-
|
95
|
-
res = self.class.transact(tx_data)
|
96
|
-
if dbid.nil?
|
97
|
-
self.dbid = Java::Datomic::Peer.resolve_tempid(
|
98
|
-
res[:"db-after".to_clj],
|
99
|
-
res[:tempids.to_clj],
|
100
|
-
self.class.clj.edn_convert(tempid))
|
125
|
+
module ClassMethods
|
126
|
+
def get(dbid, connection=nil, resolve=false)
|
127
|
+
entity = self.new
|
128
|
+
dbid = dbid.to_i if dbid.is_a?(String)
|
129
|
+
connection ||= Diametric::Persistence::Peer.connect
|
130
|
+
entity = self.from_dbid_or_entity(dbid, connection, resolve)
|
131
|
+
entity
|
101
132
|
end
|
102
133
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
self.dbid
|
111
|
-
end
|
134
|
+
def q(query, args, conn_or_db=nil)
|
135
|
+
conn_or_db ||= Diametric::Persistence::Peer.connect
|
136
|
+
if conn_or_db.is_a?(Diametric::Persistence::Connection)
|
137
|
+
db = conn_or_db.db
|
138
|
+
else
|
139
|
+
db = conn_or_db
|
140
|
+
end
|
112
141
|
|
113
|
-
|
114
|
-
|
115
|
-
|
142
|
+
results = Diametric::Persistence::Peer.q(query, db, args)
|
143
|
+
# Diametric query expects the first element of each array in
|
144
|
+
# results is dbid. Wraps dbid here by
|
145
|
+
# Diametric::Persistence::Object to make it consistent
|
146
|
+
results.each do |r|
|
147
|
+
r[0] = Diametric::Persistence::Object.new(r[0])
|
148
|
+
end
|
149
|
+
results
|
150
|
+
end
|
116
151
|
end
|
117
152
|
end
|
118
153
|
end
|
@@ -47,8 +47,9 @@ module Diametric
|
|
47
47
|
connection.transact(database, data)
|
48
48
|
end
|
49
49
|
|
50
|
-
def get(dbid)
|
51
|
-
|
50
|
+
def get(dbid, conn=nil, resolve=false)
|
51
|
+
conn ||= connection
|
52
|
+
res = conn.entity(database, dbid.to_i)
|
52
53
|
|
53
54
|
# TODO tighten regex to only allow fields with the model name
|
54
55
|
attrs = res.data.map { |attr_symbol, value|
|
@@ -61,7 +62,7 @@ module Diametric
|
|
61
62
|
entity
|
62
63
|
end
|
63
64
|
|
64
|
-
def q(query, args)
|
65
|
+
def q(query, args, unused=nil)
|
65
66
|
args.unshift(connection.db_alias(database))
|
66
67
|
res = connection.query(query, args)
|
67
68
|
res.data
|
data/lib/diametric/query.rb
CHANGED
@@ -15,16 +15,19 @@ module Diametric
|
|
15
15
|
class Query
|
16
16
|
include Enumerable
|
17
17
|
|
18
|
-
attr_reader :conditions, :filters, :model
|
18
|
+
attr_reader :conditions, :filters, :filter_attrs, :model, :connection, :resolve
|
19
19
|
|
20
20
|
# Create a new Datomic query.
|
21
21
|
#
|
22
22
|
# @param model [Entity] This model must include +Datomic::Entity+. Including
|
23
23
|
# a persistence module is optional.
|
24
|
-
def initialize(model)
|
24
|
+
def initialize(model, connection_or_database=nil, resolve=false)
|
25
25
|
@model = model
|
26
26
|
@conditions = {}
|
27
27
|
@filters = []
|
28
|
+
@filter_attrs = []
|
29
|
+
@conn_or_db = connection_or_database
|
30
|
+
@resolve = resolve
|
28
31
|
end
|
29
32
|
|
30
33
|
# Add conditions to your Datomic query. Conditions check for equality
|
@@ -69,6 +72,7 @@ module Diametric
|
|
69
72
|
# that will be converted into a Datalog query.
|
70
73
|
# @return [Query]
|
71
74
|
def filter(*filter)
|
75
|
+
return peer_filter(filter) if self.model.instance_variable_get("@peer")
|
72
76
|
query = self.dup
|
73
77
|
|
74
78
|
if filter.first.is_a?(EDN::Type::List)
|
@@ -82,6 +86,22 @@ module Diametric
|
|
82
86
|
query
|
83
87
|
end
|
84
88
|
|
89
|
+
def peer_filter(*filter)
|
90
|
+
query = self.dup
|
91
|
+
|
92
|
+
if filter.first.is_a?(Array)
|
93
|
+
filter = filter.first
|
94
|
+
end
|
95
|
+
filter_attrs << filter[1].to_s
|
96
|
+
filter[1] = "?#{filter[1].to_s}"
|
97
|
+
filter = filter.tap {|e| e.to_s }.join(" ")
|
98
|
+
filter = "[(#{filter})]"
|
99
|
+
|
100
|
+
query.filters << filter
|
101
|
+
query
|
102
|
+
end
|
103
|
+
|
104
|
+
|
85
105
|
# Loop through the query results. In order to use +each+, your model *must*
|
86
106
|
# include a persistence API. At a minimum, it must have a +.q+ method that
|
87
107
|
# returns an +Enumerable+ object.
|
@@ -90,12 +110,17 @@ module Diametric
|
|
90
110
|
def each
|
91
111
|
# TODO check to see if the model has a `.q` method and give
|
92
112
|
# an appropriate error if not.
|
93
|
-
res = model.q(*data)
|
94
|
-
|
113
|
+
res = model.q(*data, @conn_or_db)
|
95
114
|
collapse_results(res).each do |entity|
|
115
|
+
if self.model.instance_variable_get("@peer") && @resolve
|
116
|
+
yield model.from_dbid_or_entity(entity.first.to_java, @conn_or_db, @resolve)
|
117
|
+
elsif self.model.instance_variable_get("@peer")
|
118
|
+
yield entity.first.to_java
|
96
119
|
# The map is for compatibility with Java peer persistence.
|
97
120
|
# TODO remove if possible
|
98
|
-
|
121
|
+
else
|
122
|
+
yield model.from_query(entity.map { |x| x }, @conn_or_db, @resolve)
|
123
|
+
end
|
99
124
|
end
|
100
125
|
end
|
101
126
|
|
@@ -113,6 +138,8 @@ module Diametric
|
|
113
138
|
# is the Datomic query composed of Ruby data. The second element is
|
114
139
|
# the arguments that used with the query.
|
115
140
|
def data
|
141
|
+
return peer_data if self.model.instance_variable_get("@peer")
|
142
|
+
|
116
143
|
vars = model.attribute_names.map { |attribute| ~"?#{attribute}" }
|
117
144
|
|
118
145
|
from = conditions.map { |k, _| ~"?#{k}" }
|
@@ -133,6 +160,39 @@ module Diametric
|
|
133
160
|
[query, args]
|
134
161
|
end
|
135
162
|
|
163
|
+
def peer_data
|
164
|
+
if conditions.empty? && filters.empty?
|
165
|
+
args = [model.prefix]
|
166
|
+
query = <<-EOQ
|
167
|
+
[:find ?e
|
168
|
+
:in $ [?include-ns ...]
|
169
|
+
:where
|
170
|
+
[?e ?aid ?v]
|
171
|
+
[?aid :db/ident ?a]
|
172
|
+
[(namespace ?a) ?ns]
|
173
|
+
[(= ?ns ?include-ns)]]
|
174
|
+
EOQ
|
175
|
+
else
|
176
|
+
unless conditions.empty?
|
177
|
+
clauses = conditions.keys.inject("") do |memo, attribute|
|
178
|
+
memo + "[?e " + model.namespace(model.prefix, attribute) + " ?#{attribute} ] "
|
179
|
+
end
|
180
|
+
from = conditions.inject("[") {|memo, kv| memo + "?#{kv.shift} "} +"]"
|
181
|
+
args = conditions.map { |_, v| v }
|
182
|
+
end
|
183
|
+
|
184
|
+
unless filter_attrs.empty?
|
185
|
+
clauses ||= ""
|
186
|
+
clauses = filter_attrs.inject(clauses) do |memo, attribute|
|
187
|
+
memo + "[?e " + model.namespace(model.prefix, attribute) + " ?#{attribute} ] "
|
188
|
+
end
|
189
|
+
end
|
190
|
+
query = "[:find ?e :in $ #{from} :where #{clauses} #{filters.join}]"
|
191
|
+
end
|
192
|
+
|
193
|
+
[query, args]
|
194
|
+
end
|
195
|
+
|
136
196
|
protected
|
137
197
|
|
138
198
|
def conditions=(conditions)
|
@@ -156,32 +216,43 @@ module Diametric
|
|
156
216
|
end
|
157
217
|
|
158
218
|
def collapse_results(res)
|
219
|
+
return res unless @resolve
|
220
|
+
|
159
221
|
res.group_by(&:first).map do |dbid, results|
|
160
222
|
# extract dbid from results
|
161
223
|
# NOTE: we prefer to_a.drop(1) over block arg decomposition because
|
162
224
|
# result may be a Java::ClojureLang::PersistentVector
|
163
225
|
results = results.map {|result| result.to_a.drop(1) }
|
164
226
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
227
|
+
unless results.flatten.empty?
|
228
|
+
# Group values from all results into one result set
|
229
|
+
# [["b", 123], ["c", 123]] #=> [["b", "c"], [123, 123]]
|
230
|
+
grouped_values = results.transpose
|
231
|
+
attr_grouped_values = grouped_values[0...model.attributes.size]
|
232
|
+
enum_grouped_values = grouped_values[model.attributes.size..-1]
|
233
|
+
|
234
|
+
# Attach attribute names to each collection of values
|
235
|
+
# => [[:letters, ["b", "c"]], [:number, [123, 123]]]
|
236
|
+
attr_to_values = model.attributes.keys.zip(attr_grouped_values)
|
237
|
+
|
238
|
+
# Retain cardinality/many attributes as a collection,
|
239
|
+
# but pick only one value for cardinality/one attributes
|
240
|
+
collapsed_values = attr_to_values.map do |attr, values|
|
241
|
+
if model.attributes[attr][:cardinality] == :many
|
242
|
+
values
|
243
|
+
elsif model.attributes[attr][:value_type] == "ref" &&
|
244
|
+
model.enum_names.include?(attr)
|
245
|
+
enum_grouped_values.shift.first
|
246
|
+
else
|
247
|
+
values.first
|
248
|
+
end
|
180
249
|
end
|
181
|
-
end
|
182
250
|
|
183
|
-
|
184
|
-
|
251
|
+
# Returning a singular result for each dbid
|
252
|
+
[dbid, *collapsed_values]
|
253
|
+
else
|
254
|
+
[dbid]
|
255
|
+
end
|
185
256
|
end
|
186
257
|
end
|
187
258
|
end
|