norikra 0.0.5-java → 0.0.6-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -29,7 +29,7 @@ module Norikra
29
29
  @targets = []
30
30
  @queries = []
31
31
 
32
- @waiting_queries = {} # target => [query]
32
+ @waiting_queries = []
33
33
  end
34
34
 
35
35
  def start
@@ -58,8 +58,8 @@ module Norikra
58
58
 
59
59
  def register(query)
60
60
  info "registering query", :name => query.name, :targets => query.targets, :expression => query.expression
61
- unless @targets.include?(query.targets.first)
62
- open(query.targets.first) # open as lazy defined target
61
+ query.targets.each do |target|
62
+ open(target) unless @targets.include?(target)
63
63
  end
64
64
  register_query(query)
65
65
  end
@@ -120,8 +120,7 @@ module Norikra
120
120
  end
121
121
 
122
122
  def update(new_events, old_events)
123
- #TODO: trace log
124
- #p "updated event query:#{@query_name}, event:#{new_events.inspect}"
123
+ trace "updated event", :query => @query_name, :event => new_events
125
124
  @output_pool.push(@query_name, new_events)
126
125
  end
127
126
  end
@@ -161,25 +160,27 @@ module Norikra
161
160
  return false unless @typedef_manager.lazy?(target)
162
161
 
163
162
  @typedef_manager.activate(target, fieldset)
164
- # @typedef_manager.bind_fieldset(target, :base, fieldset)
165
163
  register_fieldset_actually(target, fieldset, :base)
166
164
  end
167
165
  nil
168
166
  end
169
167
 
170
168
  def register_query(query)
171
- #TODO: support JOINs
172
- target = query.targets.first
173
169
  @mutex.synchronize do
174
- if @typedef_manager.lazy?(target) || !@typedef_manager.fields_defined?(target, query.fields)
175
- @waiting_queries[target] ||= []
176
- @waiting_queries[target].push(query)
177
- else
178
- query_fieldset = @typedef_manager.generate_query_fieldset(target, query.fields)
170
+ unless @typedef_manager.ready?(query)
171
+ @waiting_queries.push(query)
172
+ @queries.push(query)
173
+ return
174
+ end
175
+
176
+ mapping = @typedef_manager.generate_fieldset_mapping(query)
177
+ mapping.each do |target, query_fieldset|
179
178
  @typedef_manager.bind_fieldset(target, :query, query_fieldset)
180
179
  register_fieldset_actually(target, query_fieldset, :query)
181
- register_query_actually(target, query_fieldset.event_type_name, query)
180
+ end
181
+ register_query_actually(query, mapping)
182
182
 
183
+ mapping.each do |target, query_fieldset|
183
184
  # replace registered data fieldsets with new fieldset inherits this query fieldset
184
185
  @typedef_manager.supersets(target, query_fieldset).each do |set|
185
186
  rebound = set.rebind(true) # update event_type_name with new inheritations
@@ -189,33 +190,38 @@ module Norikra
189
190
  remove_fieldset_actually(target, set, :data)
190
191
  end
191
192
  end
193
+
192
194
  @queries.push(query)
193
195
  end
194
196
  end
195
197
 
196
198
  def register_waiting_queries(target)
197
- waitings = @waiting_queries.delete(target) || []
198
- not_registered = []
199
+ ready = []
200
+ not_ready = []
201
+ @waiting_queries.each do |q|
202
+ if @typedef_manager.ready?(q)
203
+ ready.push(q)
204
+ else
205
+ not_ready.push(q)
206
+ end
207
+ end
208
+ @waiting_queries = not_ready
199
209
 
200
- waitings.each do |query|
201
- if @typedef_manager.fields_defined?(target, query.fields)
202
- query_fieldset = @typedef_manager.generate_query_fieldset(target, query.fields)
210
+ ready.each do |query|
211
+ mapping = @typedef_manager.generate_fieldset_mapping(query)
212
+ mapping.each do |target, query_fieldset|
203
213
  @typedef_manager.bind_fieldset(target, :query, query_fieldset)
204
214
  register_fieldset_actually(target, query_fieldset, :query)
205
- register_query_actually(target, query_fieldset.event_type_name, query)
206
- else
207
- not_registered.push(query)
208
215
  end
216
+ register_query_actually(query, mapping)
209
217
  end
210
-
211
- @waiting_queries[target] = not_registered if not_registered.size > 0
212
218
  end
213
219
 
214
220
  def register_fieldset(target, fieldset)
215
221
  @mutex.synchronize do
216
222
  @typedef_manager.bind_fieldset(target, :data, fieldset)
217
223
 
218
- if @waiting_queries[target]
224
+ if @waiting_queries.size > 0
219
225
  register_waiting_queries(target)
220
226
  end
221
227
 
@@ -224,12 +230,20 @@ module Norikra
224
230
  end
225
231
 
226
232
  # this method should be protected with @mutex lock
227
- def register_query_actually(target, stream_name, query)
228
- query = query.dup_with_stream_name(stream_name)
229
- epl = @service.getEPAdministrator.createEPL(query.expression)
233
+ def register_query_actually(query, mapping)
234
+ # 'mapping' argument is {target => fieldset}
235
+ event_type_name_map = {}
236
+ mapping.keys.each do |key|
237
+ event_type_name_map[key] = mapping[key].event_type_name
238
+ end
239
+
240
+ administrator = @service.getEPAdministrator
241
+
242
+ statement_model = administrator.compileEPL(query.expression)
243
+ Norikra::Query.rewrite_event_type_name(statement_model, event_type_name_map)
244
+
245
+ epl = administrator.create(statement_model)
230
246
  epl.java_send :addListener, [com.espertech.esper.client.UpdateListener.java_class], Listener.new(query.name, @output_pool)
231
- #TODO: debug log
232
- #p "addListener target:#{target}, query_name:#{query.name}, query:#{query.expression}"
233
247
  end
234
248
 
235
249
  # this method should be protected with @mutex lock
@@ -242,19 +256,16 @@ module Norikra
242
256
  # .addEventType("AccountUpdate", accountUpdateDef, new String[] {"BaseUpdate"});
243
257
  case level
244
258
  when :base
259
+ debug "add event type", :target => target, :level => 'base', :event_type => fieldset.event_type_name
245
260
  @config.addEventType(fieldset.event_type_name, fieldset.definition)
246
- #TODO: debug log
247
- #p "addEventType target:#{target}, level:base, eventType:#{fieldset.event_type_name}"
248
261
  when :query
249
262
  base_name = @typedef_manager.base_fieldset(target).event_type_name
263
+ debug "add event type", :target => target, :level => 'query', :event_type => fieldset.event_type_name, :base => base_name
250
264
  @config.addEventType(fieldset.event_type_name, fieldset.definition, [base_name].to_java(:string))
251
- #TODO: debug log
252
- #p "addEventType target:#{target}, level:query, eventType:#{fieldset.event_type_name}, base:#{base_name}"
253
265
  else
254
266
  subset_names = @typedef_manager.subsets(target, fieldset).map(&:event_type_name)
267
+ debug "add event type", :target => target, :level => 'data', :event_type => fieldset.event_type_name, :inherit => subset_names
255
268
  @config.addEventType(fieldset.event_type_name, fieldset.definition, subset_names.to_java(:string))
256
- #TODO: debug log
257
- #p "addEventType target:#{target}, level:data, eventType:#{fieldset.event_type_name}, inherit:#{subset_names.join(',')}"
258
269
 
259
270
  @registered_fieldsets[target][level][fieldset.summary] = fieldset
260
271
  end
@@ -267,7 +278,7 @@ module Norikra
267
278
 
268
279
  # DON'T check @registered_fieldsets[target][level][fieldset.summary]
269
280
  # removed fieldset should be already replaced with register_fieldset_actually w/ replace flag
270
- #TODO: debug log
281
+ debug "remove event type", :target => target, :event_type => fieldset.event_type_name
271
282
  @config.removeEventType(fieldset.event_type_name, true)
272
283
  end
273
284
  end
@@ -4,6 +4,8 @@ require 'esper/lib/commons-logging-1.1.1.jar'
4
4
  require 'esper/lib/antlr-runtime-3.2.jar'
5
5
  require 'esper/lib/cglib-nodep-2.2.jar'
6
6
 
7
+ require 'norikra/query/ast'
8
+
7
9
  module Norikra
8
10
  class Query
9
11
  attr_accessor :name, :expression
@@ -13,6 +15,7 @@ module Norikra
13
15
  @expression = param[:expression]
14
16
  @ast = nil
15
17
  @targets = nil
18
+ @subqueries = nil
16
19
  @fields = nil
17
20
  end
18
21
 
@@ -36,139 +39,168 @@ module Norikra
36
39
 
37
40
  def targets
38
41
  return @targets if @targets
39
- #TODO: test with JOINs.
40
- @targets = self.ast.find('STREAM_EXPR').find('EVENT_FILTER_EXPR').children.map(&:name)
42
+ @targets = (self.ast.listup(:stream).map(&:target) + self.subqueries.map(&:targets).flatten).sort.uniq
41
43
  @targets
42
44
  end
43
45
 
44
- def fields
45
- return @fields if @fields
46
- #TODO: this code doesn't care JOINs.
47
- ### fields -> {'target_name' => fields} ?
48
-
49
- # Norikra::Query.new(
50
- # :name => 'hoge',
51
- # :expression => 'select count(*) AS cnt from www.win:time_batch(10 seconds) where path="/" AND search.length() > 0').ast.to_a
52
- # ["EPL_EXPR",
53
- # ["SELECTION_EXPR", ["SELECTION_ELEMENT_EXPR", ["count"], ["cnt"]]],
54
- # ["STREAM_EXPR",
55
- # ["EVENT_FILTER_EXPR", ["www"]],
56
- # ["VIEW_EXPR",
57
- # ["win"],
58
- # ["time_batch"],
59
- # ["TIME_PERIOD", ["SECOND_PART", ["10"]]]]],
60
- # ["WHERE_EXPR",
61
- # ["EVAL_AND_EXPR",
62
- # ["EVAL_EQUALS_EXPR",
63
- # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", ["path"]]],
64
- # ["\"/\""]],
65
- # [">",
66
- # ["LIB_FUNC_CHAIN", ["LIB_FUNCTION", ["search"], ["length"], ["("]]],
67
- # ["0"]]]]]
68
-
69
- ast = self.ast
70
- names_simple = ast.listup('EVENT_PROP_SIMPLE').map{|p| p.child.name}
71
- names_chain_root = ast.listup('LIB_FUNC_CHAIN').map{|c| c.child.child.name}.select{|n| not self.class.imported_java_class?(n)}
72
- @fields = (names_simple + names_chain_root).uniq.sort
73
- @fields
46
+ def subqueries
47
+ return @subqueries if @subqueries
48
+ @subqueries = self.ast.listup(:subquery).map{|n| Norikra::SubQuery.new(n)}
49
+ @subqueries
74
50
  end
75
51
 
76
- def self.imported_java_class?(name)
77
- return false unless name =~ /^[A-Z]/
78
- # Esper auto-imports the following Java library packages:
79
- # java.lang.* -> Java::JavaLang::*
80
- # java.math.* -> Java::JavaMath::*
81
- # java.text.* -> Java::JavaText::*
82
- # java.util.* -> Java::JavaUtil::*
83
- java_class('Java::JavaLang::'+name) || java_class('Java::JavaMath::'+name) ||
84
- java_class('Java::JavaText::'+name) || java_class('Java::JavaUtil::'+name) || false
85
- end
86
- def self.java_class(const_name)
87
- begin
88
- c = eval(const_name)
89
- c.class == Kernel ? nil : c
90
- rescue NameError
91
- return nil
92
- end
93
- end
52
+ def explore(outer_targets=[], alias_overridden={})
53
+ fields = {}
54
+ alias_map = {}.merge(alias_overridden)
94
55
 
95
- class ParseRuleSelectorImpl
96
- include com.espertech.esper.epl.parse.ParseRuleSelector
97
- def invokeParseRule(parser)
98
- parser.startEPLExpressionRule().getTree()
56
+ all = []
57
+ unknowns = []
58
+ self.ast.listup(:stream).each do |node|
59
+ #TODO: raise error for same name of target/alias
60
+ if node.alias
61
+ alias_map[node.alias] = node.target
62
+ end
63
+ fields[node.target] = []
99
64
  end
100
- end
101
65
 
102
- class ASTNode
103
- attr_accessor :name, :children
104
- def initialize(name, children)
105
- @name = name
106
- @children = children
107
- end
108
- def to_a
109
- [@name] + @children.map(&:to_a)
66
+ default_target = fields.keys.size == 1 ? fields.keys.first : nil
67
+
68
+ outer_targets.each do |t|
69
+ fields[t] ||= []
110
70
  end
111
- def child
112
- @children.first
71
+
72
+ field_bag = []
73
+ self.subqueries.each do |subquery|
74
+ field_bag.push(subquery.explore(fields.keys, alias_map))
113
75
  end
114
- def find(node_name) # only one, depth-first search
115
- return self if @name == node_name
116
- @children.each do |c|
117
- r = c.find(node_name)
118
- return r if r
76
+
77
+ self.ast.fields(default_target).each do |field_def|
78
+ f = field_def[:f]
79
+ all.push(f)
80
+
81
+ if field_def[:t]
82
+ t = alias_map[field_def[:t]] || field_def[:t]
83
+ unless fields[t]
84
+ raise "unknown target alias name for: #{field_def[:t]}.#{field_def[:f]}"
85
+ end
86
+ fields[t].push(f)
87
+
88
+ else
89
+ unknowns.push(f)
119
90
  end
120
- nil
121
91
  end
122
- def listup(node_name)
123
- result = []
124
- result.push(self) if @name == node_name
125
- @children.each do |c|
126
- result.push(*c.listup(node_name))
92
+
93
+ field_bag.each do |bag|
94
+ all += bag['']
95
+ unknowns += bag[nil]
96
+ bag.keys.each do |t|
97
+ fields[t] ||= []
98
+ fields[t] += bag[t]
127
99
  end
128
- result
100
+ end
101
+
102
+ fields.keys.each do |target|
103
+ fields[target] = fields[target].sort.uniq
104
+ end
105
+ fields[''] = all.sort.uniq
106
+ fields[nil] = unknowns.sort.uniq
107
+
108
+ fields
109
+ end
110
+
111
+ def fields(target='')
112
+ # target '': fields for all targets (without target name)
113
+ # target nil: fields for unknown targets
114
+ return @fields[target] if @fields
115
+
116
+ @fields = explore()
117
+ @fields[target]
118
+ end
119
+
120
+ class ParseRuleSelectorImpl
121
+ include com.espertech.esper.epl.parse.ParseRuleSelector
122
+ def invokeParseRule(parser)
123
+ parser.startEPLExpressionRule().getTree()
129
124
  end
130
125
  end
131
126
 
132
127
  def ast
133
- #TODO: test
134
- #TODO: take care for parse error
128
+ #TODO: take care for parse error(com.espertech.esper.client.EPStatementSyntaxException)
135
129
  return @ast if @ast
136
130
  rule = ParseRuleSelectorImpl.new
137
131
  target = @expression.dup
138
132
  forerrmsg = @expression.dup
139
133
  result = com.espertech.esper.epl.parse.ParseHelper.parse(target, forerrmsg, true, rule, false)
140
134
 
141
- def convSubTree(tree)
142
- ASTNode.new(tree.text, (tree.children ? tree.children.map{|c| convSubTree(c)} : []))
143
- end
144
- @ast = convSubTree(result.getTree)
135
+ @ast = astnode(result.getTree)
145
136
  @ast
146
137
  end
147
138
 
148
- ### select max(price) as maxprice from HogeTable.win:time_batch(10 sec) where cast(amount, double) > 2 and price > 50
149
- # query.ast.to_a
150
- # ["EPL_EXPR",
151
- # ["SELECTION_EXPR",
152
- # ["SELECTION_ELEMENT_EXPR",
153
- # ["LIB_FUNC_CHAIN",
154
- # ["LIB_FUNCTION",
155
- # ["max"],
156
- # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", ["price"]]],
157
- # ["("]]],
158
- # ["maxprice"]]],
159
- # ["STREAM_EXPR",
160
- # ["EVENT_FILTER_EXPR", ["HogeTable"]],
161
- # ["VIEW_EXPR",
162
- # ["win"],
163
- # ["time_batch"],
164
- # ["TIME_PERIOD", ["SECOND_PART", ["10"]]]]],
165
- # ["WHERE_EXPR",
166
- # ["EVAL_AND_EXPR",
167
- # [">",
168
- # ["cast",
169
- # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", ["amount"]]],
170
- # ["double"]],
171
- # ["2"]],
172
- # [">", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", ["price"]]], ["50"]]]]]
139
+ def self.rewrite_event_type_name(statement_model, mapping)
140
+ # mapping: {target_name => query_event_type_name}
141
+
142
+ ### esper-4.9.0/esper/doc/reference/html/epl_clauses.html#epl-subqueries
143
+ # Subqueries can only consist of a select clause, a from clause and a where clause.
144
+ # The group by and having clauses, as well as joins, outer-joins and output rate limiting are not permitted within subqueries.
145
+
146
+ # model.getFromClause.getStreams[0].getFilter.setEventTypeName("hoge")
147
+
148
+ # model.getSelectClause.getSelectList[1].getExpression => #<Java::ComEspertechEsperClientSoda::SubqueryExpression:0x3344c133>
149
+ # model.getSelectClause.getSelectList[1].getExpression.getModel.getFromClause.getStreams[0].getFilter.getEventTypeName
150
+ # model.getWhereClause.getChildren[1] .getModel.getFromClause.getStreams[0].getFilter.getEventTypeName
151
+
152
+ statement_model.getFromClause.getStreams.each do |stream|
153
+ target_name = stream.getFilter.getEventTypeName
154
+ unless mapping[target_name]
155
+ raise RuntimeError, "target missing in mapping, maybe BUG"
156
+ end
157
+ stream.getFilter.setEventTypeName(mapping[target_name])
158
+ end
159
+
160
+ dig = lambda {|node|
161
+ if node.is_a?(Java::ComEspertechEsperClientSoda::SubqueryExpression)
162
+ Norikra::Query.rewrite_event_type_name(node.getModel, mapping)
163
+ elsif node.getChildren.size > 0
164
+ node.getChildren.each do |c|
165
+ dig.call(c)
166
+ end
167
+ end
168
+ }
169
+
170
+ if statement_model.getSelectClause
171
+ statement_model.getSelectClause.getSelectList.each do |item|
172
+ dig.call(item.getExpression)
173
+ end
174
+ end
175
+
176
+ if statement_model.getWhereClause
177
+ statement_model.getWhereClause.getChildren.each do |child|
178
+ dig.call(child)
179
+ end
180
+ end
181
+
182
+ statement_model
183
+ end
184
+ end
185
+
186
+ class SubQuery < Query
187
+ def initialize(ast_nodetree)
188
+ @ast = ast_nodetree
189
+ @targets = nil
190
+ @subqueries = nil
191
+ end
192
+
193
+ def ast; @ast; end
194
+
195
+ def subqueries
196
+ return @subqueries if @subqueries
197
+ @subqueries = @ast.children.map{|c| c.listup(:subquery)}.reduce(&:+).map{|n| Norikra::SubQuery.new(n)}
198
+ @subqueries
199
+ end
200
+
201
+ def name; ''; end
202
+ def expression; ''; end
203
+ def dup; self; end
204
+ def dup_with_stream_name(actual_name); self; end
173
205
  end
174
206
  end
@@ -0,0 +1,237 @@
1
+ module Norikra
2
+ class Query
3
+ ### SELECT MAX(size) AS maxsize, fraud.aaa,bbb
4
+ ### FROM FraudWarningEvent.win:keepall() AS fraud,
5
+ ### PINChangeEvent(size > 10).win:time(20 sec)
6
+ ### WHERE fraud.accountNumber.substr(0,8) = substr(PINChangeEvent.accountNumber, 0, 8)
7
+ ### AND cast(PINChangeEvent.size,double) > 10.5
8
+ #
9
+ # ["EPL_EXPR",
10
+ # ["SELECTION_EXPR",
11
+ # ["SELECTION_ELEMENT_EXPR",
12
+ # ["LIB_FUNC_CHAIN",
13
+ # ["LIB_FUNCTION",
14
+ # "max",
15
+ # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]],
16
+ # "("]],
17
+ # "maxsize"],
18
+ # ["SELECTION_ELEMENT_EXPR",
19
+ # ["EVENT_PROP_EXPR",
20
+ # ["EVENT_PROP_SIMPLE", "fraud"],
21
+ # ["EVENT_PROP_SIMPLE", "aaa"]]],
22
+ # ["SELECTION_ELEMENT_EXPR",
23
+ # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "bbb"]]]],
24
+ # ["STREAM_EXPR",
25
+ # ["EVENT_FILTER_EXPR", "FraudWarningEvent"],
26
+ # ["VIEW_EXPR", "win", "keepall"],
27
+ # "fraud"],
28
+ # ["STREAM_EXPR",
29
+ # ["EVENT_FILTER_EXPR",
30
+ # "PINChangeEvent",
31
+ # [">", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]], "10"]],
32
+ # ["VIEW_EXPR", "win", "time", ["TIME_PERIOD", ["SECOND_PART", "20"]]]],
33
+ # ["WHERE_EXPR",
34
+ # ["EVAL_AND_EXPR",
35
+ # ["EVAL_EQUALS_EXPR",
36
+ # ["LIB_FUNC_CHAIN",
37
+ # ["LIB_FUNCTION", "fraud.accountNumber", "substr", "0", "8", "("]],
38
+ # ["LIB_FUNC_CHAIN",
39
+ # ["LIB_FUNCTION",
40
+ # "substr",
41
+ # ["EVENT_PROP_EXPR",
42
+ # ["EVENT_PROP_SIMPLE", "PINChangeEvent"],
43
+ # ["EVENT_PROP_SIMPLE", "accountNumber"]],
44
+ # "0",
45
+ # "8",
46
+ # "("]]],
47
+ # [">",
48
+ # ["cast",
49
+ # ["EVENT_PROP_EXPR",
50
+ # ["EVENT_PROP_SIMPLE", "PINChangeEvent"],
51
+ # ["EVENT_PROP_SIMPLE", "size"]],
52
+ # "double"],
53
+ # "10.5"]]]]
54
+
55
+ def astnode(tree)
56
+ children = if tree.children
57
+ tree.children.map{|c| astnode(c)}
58
+ else
59
+ []
60
+ end
61
+ case tree.text
62
+ when 'EVENT_PROP_EXPR'
63
+ ASTEventPropNode.new(tree.text, children)
64
+ when 'LIB_FUNCTION'
65
+ ASTLibFunctionNode.new(tree.text, children)
66
+ when 'STREAM_EXPR'
67
+ ASTStreamNode.new(tree.text, children)
68
+ when 'SUBSELECT_EXPR'
69
+ ASTSubSelectNode.new(tree.text, children)
70
+ else
71
+ ASTNode.new(tree.text, children)
72
+ end
73
+ end
74
+
75
+ class ASTNode
76
+ attr_accessor :name, :children
77
+
78
+ def initialize(name, children)
79
+ @name = name
80
+ @children = children
81
+ end
82
+
83
+ def nodetype?(*sym)
84
+ false
85
+ end
86
+
87
+ def to_a
88
+ [@name] + @children.map{|c| c.children.size > 0 ? c.to_a : c.name}
89
+ end
90
+
91
+ def child
92
+ @children.first
93
+ end
94
+
95
+ def find(type) # only one, depth-first search
96
+ return self if type.is_a?(String) && @name == type || nodetype?(type)
97
+
98
+ @children.each do |c|
99
+ next if type != :subquery && c.nodetype?(:subquery)
100
+ r = c.find(type)
101
+ return r if r
102
+ end
103
+ nil
104
+ end
105
+
106
+ def listup(type) # search all nodes that has 'type'
107
+ result = []
108
+ result.push(self) if type.is_a?(String) && @name == type || nodetype?(type)
109
+
110
+ @children.each do |c|
111
+ next if type != :subquery && c.nodetype?(:subquery)
112
+ result.push(*c.listup(type))
113
+ end
114
+ result
115
+ end
116
+
117
+ def fields(default_target=nil)
118
+ @children.map{|c| c.nodetype?(:subquery) ? [] : c.fields(default_target)}.reduce(&:+) || []
119
+ end
120
+ end
121
+
122
+ class ASTEventPropNode < ASTNode # EVENT_PROP_EXPR
123
+ # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "bbb"]]
124
+ # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "fraud"], ["EVENT_PROP_SIMPLE", "aaa"]]
125
+
126
+ def nodetype?(*sym)
127
+ sym.include?(:prop) || sym.include?(:property)
128
+ end
129
+
130
+ def fields(default_target=nil)
131
+ props = self.listup('EVENT_PROP_SIMPLE')
132
+ if props.size > 1 # alias.fieldname
133
+ [ {:f => props[1].child.name, :t => props[0].child.name} ]
134
+ else # fieldname (default target)
135
+ [ {:f => props[0].child.name, :t => default_target } ]
136
+ end
137
+ end
138
+ end
139
+
140
+ class ASTLibFunctionNode < ASTNode # LIB_FUNCTION
141
+ # ["LIB_FUNCTION", "now", "("] #### now()
142
+ # ["LIB_FUNCTION", "hoge", "length", "("] # #### hoge.length()
143
+ # ["LIB_FUNCTION", "hoge", "substr", "0", "("] #### hoge.substr(0)
144
+ # ["LIB_FUNCTION", "substr", "10", "0", "("] #### substr(10,0)
145
+ # ["LIB_FUNCTION", "hoge", "substr", "0", "8", "("] #### hoge.substr(0,8)
146
+ # ["LIB_FUNCTION", "max", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]], "("] #### max(size)
147
+
148
+ def nodetype?(*sym)
149
+ sym.include?(:lib) || sym.include?(:libfunc)
150
+ end
151
+
152
+ def fields(default_target=nil)
153
+ if @children.size <= 2
154
+ # single function like 'now()', function-name and "("
155
+ []
156
+
157
+ elsif @children[1].nodetype?(:prop, :lib, :subquery)
158
+ # first element should be func name if second element is property, library call or subqueries
159
+ self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
160
+
161
+ elsif @children[1].name =~ /^(-)?\d+(\.\d+)$/ || @children[1].name =~ /^'[^']*'$/ || @children[1].name =~ /^"[^"]*"$/
162
+ # first element should be func name if secod element is number/string literal
163
+ self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
164
+
165
+ elsif Norikra::Query.imported_java_class?(@children[0].name)
166
+ # Java imported class name (ex: 'Math.abs(-1)')
167
+ self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
168
+
169
+ else
170
+ # first element may be property (simple 'fieldname.funcname()' or fully qualified 'target.fieldname.funcname()')
171
+ target,fieldname = if @children[0].name.include?('.')
172
+ @children[0].name.split('.', 2)
173
+ else
174
+ [default_target,@children[0].name]
175
+ end
176
+ children_list = self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
177
+ [{:f => fieldname, :t => target}] + children_list
178
+ end
179
+ end
180
+ end
181
+
182
+ class ASTStreamNode < ASTNode # STREAM_EXPR
183
+ # ["STREAM_EXPR",
184
+ # ["EVENT_FILTER_EXPR", "FraudWarningEvent"],
185
+ # ["VIEW_EXPR", "win", "keepall"],
186
+ # "fraud"],
187
+ # ["STREAM_EXPR",
188
+ # ["EVENT_FILTER_EXPR",
189
+ # "PINChangeEvent",
190
+ # [">", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]], "10"]],
191
+ # ["VIEW_EXPR", "win", "time", ["TIME_PERIOD", ["SECOND_PART", "20"]]]],
192
+
193
+ def nodetype?(*sym)
194
+ sym.include?(:stream)
195
+ end
196
+
197
+ def target
198
+ self.find('EVENT_FILTER_EXPR').child.name
199
+ end
200
+
201
+ def alias
202
+ @children.last.children.size < 1 ? @children.last.name : nil
203
+ end
204
+
205
+ def fields(default_target=nil)
206
+ this_target = self.target
207
+ self.listup('EVENT_PROP_EXPR').map{|p| p.fields(this_target)}.reduce(&:+) || []
208
+ end
209
+ end
210
+
211
+ class ASTSubSelectNode < ASTNode # SUBSELECT_EXPR
212
+ def nodetype?(*sym)
213
+ sym.include?(:subquery)
214
+ end
215
+ end
216
+
217
+ def self.imported_java_class?(name)
218
+ return false unless name =~ /^[A-Z]/
219
+ # Esper auto-imports the following Java library packages:
220
+ # java.lang.* -> Java::JavaLang::*
221
+ # java.math.* -> Java::JavaMath::*
222
+ # java.text.* -> Java::JavaText::*
223
+ # java.util.* -> Java::JavaUtil::*
224
+ java_class('Java::JavaLang::'+name) || java_class('Java::JavaMath::'+name) ||
225
+ java_class('Java::JavaText::'+name) || java_class('Java::JavaUtil::'+name) || false
226
+ end
227
+
228
+ def self.java_class(const_name)
229
+ begin
230
+ c = eval(const_name)
231
+ c.class == Kernel ? nil : c
232
+ rescue NameError
233
+ return nil
234
+ end
235
+ end
236
+ end
237
+ end
@@ -41,6 +41,33 @@ module Norikra
41
41
  @typedefs[target].field_defined?(field_name_list)
42
42
  end
43
43
 
44
+ def ready?(query)
45
+ query.targets.all?{|t| @typedefs[t] && ! @typedefs[t].lazy? && @typedefs[t].field_defined?(query.fields(t))} &&
46
+ query.fields(nil).all?{|f| query.targets.any?{|t| @typedefs[t].field_defined?([f])}}
47
+ end
48
+
49
+ def generate_fieldset_mapping(query)
50
+ fields_set = {}
51
+ query.targets.each do |target|
52
+ fields_set[target] = query.fields(target)
53
+ end
54
+ query.fields(nil).each do |field|
55
+ assumed = query.targets.select{|t| @typedefs[t].field_defined?([field])}
56
+ if assumed.size != 1
57
+ #TODO exception class
58
+ raise "cannot determine target for field #{field}"
59
+ end
60
+ fields_set[assumed.first].push(field)
61
+ end
62
+
63
+ mapping = {}
64
+ fields_set.each do |target,fields|
65
+ mapping[target] = generate_query_fieldset(target, fields.sort.uniq)
66
+ end
67
+
68
+ mapping
69
+ end
70
+
44
71
  def bind_fieldset(target, level, fieldset)
45
72
  fieldset.bind(target, level)
46
73
  @typedefs[target].push(level, fieldset)
@@ -1,3 +1,3 @@
1
1
  module Norikra
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -6,26 +6,107 @@ describe Norikra::Query do
6
6
  context 'when instanciate' do
7
7
  describe '#initialize' do
8
8
  context 'with simple query' do
9
- expression = 'SELECT count(*) AS cnt FROM TestTable.win:time_batch(10 sec) WHERE path="/" AND size > 100 and param.length() > 0'
10
- subject { Norikra::Query.new(
9
+ it 'returns query instances collectly parsed' do
10
+ expression = 'SELECT count(*) AS cnt FROM TestTable.win:time_batch(10 sec) WHERE path="/" AND size > 100 and param.length() > 0'
11
+ q = Norikra::Query.new(
11
12
  :name => 'TestTable query1', :expression => expression
12
- ) }
13
- its(:name){ should == 'TestTable query1' }
14
- its(:expression){ should == expression }
15
- its(:targets){ should == ['TestTable'] }
16
- its(:fields){ should == ['param', 'path', 'size'] }
13
+ )
14
+ expect(q.name).to eql('TestTable query1')
15
+ expect(q.expression).to eql(expression)
16
+ expect(q.targets).to eql(['TestTable'])
17
+
18
+ expect(q.fields).to eql(['param', 'path', 'size'].sort)
19
+ expect(q.fields('TestTable')).to eql(['param','path','size'].sort)
20
+ expect(q.fields(nil)).to eql([])
21
+ end
22
+ end
23
+
24
+ context 'with query including Static lib call' do
25
+ it 'returns query instances collectly parsed' do
26
+ expression = 'SELECT count(*) AS cnt FROM TestTable.win:time_batch(10 sec) AS source WHERE source.path="/" AND Math.abs(-1 * source.size) > 3'
27
+ q = Norikra::Query.new(
28
+ :name => 'TestTable query2', :expression => expression
29
+ )
30
+ expect(q.name).to eql('TestTable query2')
31
+ expect(q.expression).to eql(expression)
32
+ expect(q.targets).to eql(['TestTable'])
33
+
34
+ expect(q.fields).to eql(['path', 'size'].sort)
35
+ expect(q.fields('TestTable')).to eql(['path', 'size'].sort)
36
+ expect(q.fields(nil)).to eql([])
37
+ end
38
+ end
39
+
40
+ context 'with query with join' do
41
+ it 'returns query instances collectly parsed' do
42
+ expression = 'select product, max(sta.size) as maxsize from StreamA.win:keepall() as sta, StreamB(size > 10).win:time(20 sec) as stb where sta.data.substr(0,8) = stb.header AND Math.abs(sta.size) > 3'
43
+ q = Norikra::Query.new(
44
+ :name => 'TestTable query3', :expression => expression
45
+ )
46
+ expect(q.name).to eql('TestTable query3')
47
+ expect(q.expression).to eql(expression)
48
+ expect(q.targets).to eql(['StreamA', 'StreamB'])
49
+
50
+ expect(q.fields).to eql(['product', 'size', 'data', 'header'].sort)
51
+ expect(q.fields('StreamA')).to eql(['size','data'].sort)
52
+ expect(q.fields('StreamB')).to eql(['size','header'].sort)
53
+ expect(q.fields(nil)).to eql(['product'])
54
+ end
55
+ end
56
+
57
+ context 'with query with subquery (where clause)' do
58
+ it 'returns query instances collectly parsed' do
59
+ expression = 'select * from RfidEvent as RFID where "Dock 1" = (select name from Zones.std:unique(zoneName) where zoneId = RFID.zoneId)'
60
+ q = Norikra::Query.new(
61
+ :name => 'TestTable query4', :expression => expression
62
+ )
63
+ expect(q.name).to eql('TestTable query4')
64
+ expect(q.expression).to eql(expression)
65
+ expect(q.targets).to eql(['RfidEvent', 'Zones'])
66
+
67
+ expect(q.fields).to eql(['name','zoneName','zoneId'].sort)
68
+ expect(q.fields('RfidEvent')).to eql(['zoneId'])
69
+ expect(q.fields('Zones')).to eql(['name','zoneName','zoneId'].sort)
70
+ expect(q.fields(nil)).to eql([])
71
+ end
72
+ end
73
+
74
+ context 'with query with subquery (select clause)' do
75
+ it 'returns query instances collectly parsed' do
76
+ expression = 'select zoneId, (select name from Zones.std:unique(zoneName) where zoneId = RfidEvent.zoneId) as name from RfidEvent'
77
+ q = Norikra::Query.new(
78
+ :name => 'TestTable query5', :expression => expression
79
+ )
80
+ expect(q.name).to eql('TestTable query5')
81
+ expect(q.expression).to eql(expression)
82
+ expect(q.targets).to eql(['RfidEvent', 'Zones'].sort)
83
+
84
+ expect(q.fields).to eql(['name','zoneName','zoneId'].sort)
85
+ expect(q.fields('RfidEvent')).to eql(['zoneId'])
86
+ expect(q.fields('Zones')).to eql(['name','zoneName','zoneId'].sort)
87
+ expect(q.fields(nil)).to eql([])
88
+ end
89
+ end
90
+
91
+ context 'with query with subquery (from clause)' do
92
+ it 'returns query instances collectly parsed' do
93
+ expression = "select * from BarData(ticker='MSFT', sub(closePrice, (select movAgv from SMA20Stream(ticker='MSFT').std:lastevent())) > 0)"
94
+ q = Norikra::Query.new(
95
+ :name => 'TestTable query6', :expression => expression
96
+ )
97
+ expect(q.name).to eql('TestTable query6')
98
+ expect(q.expression).to eql(expression)
99
+ expect(q.targets).to eql(['BarData', 'SMA20Stream'].sort)
100
+
101
+ expect(q.fields).to eql(['ticker','closePrice','movAgv'].sort)
102
+ expect(q.fields('BarData')).to eql(['ticker','closePrice'].sort)
103
+ expect(q.fields('SMA20Stream')).to eql(['movAgv','ticker'].sort)
104
+ expect(q.fields(nil)).to eql([])
105
+ end
17
106
  end
18
- context 'with query including Static lib call'
19
- expression = 'SELECT count(*) AS cnt FROM TestTable.win:time_batch(10 sec) WHERE path="/" AND Math.abs(-1 * size) > 3'
20
- subject { Norikra::Query.new(
21
- :name => 'TestTable query1', :expression => expression
22
- ) }
23
- its(:name){ should == 'TestTable query1' }
24
- its(:expression){ should == expression }
25
- its(:targets){ should == ['TestTable'] }
26
- its(:fields){ should == ['path', 'size'] }
27
107
  end
28
- describe '#dup_with_stream_name' do
108
+
109
+ describe '#dup_with_stream_name' do
29
110
  context 'with simple query' do
30
111
  expression = 'SELECT count(*) AS cnt FROM TestTable.win:time_batch(10 sec) WHERE path="/" AND size > 100 and param.length() > 0'
31
112
  it 'returns duplicated object, with replaced ' do
@@ -37,6 +118,7 @@ describe Norikra::Query do
37
118
  )
38
119
  end
39
120
  end
121
+
40
122
  context 'with query with newlines' do
41
123
  expression = <<EOQ
42
124
  SELECT
@@ -61,6 +143,7 @@ EOQ
61
143
  end
62
144
  end
63
145
  end
146
+
64
147
  describe '.imported_java_class?' do
65
148
  it 'can do judge passed name exists under java package tree or not' do
66
149
  expect(Norikra::Query.imported_java_class?('String')).to be_true
@@ -41,6 +41,8 @@ describe Norikra::TypedefManager do
41
41
  context 'when instanciated with a target with fields definition' do
42
42
  manager = Norikra::TypedefManager.new
43
43
  manager.add_target('sample', {'a'=>'string','b'=>'string','c'=>'double'})
44
+ manager.reserve('sample', 'z', 'boolean')
45
+ manager.add_target('sample_next', {'a'=>'string','b'=>'string','c'=>'double','d'=>'double'})
44
46
 
45
47
  set_query_base = Norikra::FieldSet.new({'a'=>'string','b'=>'string','c'=>'double'})
46
48
 
@@ -68,9 +70,63 @@ describe Norikra::TypedefManager do
68
70
  describe '#fields_defined?' do
69
71
  it 'does not fail' do
70
72
  expect(manager.fields_defined?('sample', ['a','b','x'])).to be_true
71
- expect(manager.fields_defined?('sample', ['a','b','z'])).to be_false
73
+ expect(manager.fields_defined?('sample', ['a','b','y'])).to be_false
72
74
  end
73
75
  end
76
+
77
+ describe '#ready?' do
78
+ context 'with query with single target' do
79
+ it 'returns boolean which matches target or not' do
80
+ q1 = Norikra::Query.new(:name => 'test', :expression => 'select a from sample.win:time(5 sec) where c > 1.0 and z')
81
+ expect(manager.ready?(q1)).to be_true
82
+ q2 = Norikra::Query.new(:name => 'test', :expression => 'select a from sample.win:time(5 sec) where c > 1.0 and d > 2.0')
83
+ expect(manager.ready?(q2)).to be_false
84
+ q3 = Norikra::Query.new(:name => 'test', :expression => 'select a from sample2.win:time(5 sec) where c > 1.0 and d > 2.0')
85
+ expect(manager.ready?(q3)).to be_false
86
+ end
87
+ end
88
+
89
+ context 'with query with multi targets, including unexisting target' do
90
+ it 'returns false' do
91
+ q = Norikra::Query.new(:name => 'test', :expression => 'select x.a,y.a from sample.win:time(5 sec) as x, sample2.win:time(5 sec) as y where x.c > 1.0 and y.d > 1.0')
92
+ expect(manager.ready?(q)).to be_false
93
+ end
94
+ end
95
+
96
+ context 'with query with multi targets, all of them are exisitng' do
97
+ it 'returns true' do
98
+ q = Norikra::Query.new(:name => 'test', :expression => 'select x.a,d from sample.win:time(5 sec) as x, sample_next.win:time(5 sec) as y where x.c > 1.0 and y.d > 1.0')
99
+ expect(manager.ready?(q)).to be_true
100
+ end
101
+ end
102
+ end
103
+
104
+ describe '#generate_fieldset_mapping' do
105
+ it 'retuns collect mapping for fieldsets' do
106
+ q1 = Norikra::Query.new(:name => 'test', :expression => 'select a from sample.win:time(5 sec) where c > 1.0 and z')
107
+ map1 = manager.generate_fieldset_mapping(q1)
108
+ expect(map1.keys).to eql(['sample'])
109
+ expect(map1.values.size).to eql(1)
110
+ expect(map1.values.first).to be_a(Norikra::FieldSet)
111
+ expect(map1.values.first.fields.size).to eql(4)
112
+ expect(map1.values.first.fields.keys).to eql(['a','b','c','z']) # a,b,c is non-optional fields
113
+
114
+ q2 = Norikra::Query.new(:name => 'test', :expression => 'select a from sample.win:time(5 sec) where c > 1.0')
115
+ map2 = manager.generate_fieldset_mapping(q2)
116
+ expect(map2.keys).to eql(['sample'])
117
+ expect(map2.values.size).to eql(1)
118
+ expect(map2.values.first).to be_a(Norikra::FieldSet)
119
+ expect(map2.values.first.fields.size).to eql(3)
120
+ expect(map2.values.first.fields.keys).to eql(['a','b','c']) # a,b,c is non-optional fields
121
+
122
+ q3 = Norikra::Query.new(:name => 'test', :expression => 'select x.a, z from sample.win:time(5 sec) as x, sample_next.win:time(5 sec) as y where x.c > 1.0 and y.d > 1.0')
123
+ map3 = manager.generate_fieldset_mapping(q3)
124
+ expect(map3.keys).to eql(['sample', 'sample_next'])
125
+ expect(map3['sample'].fields.keys).to eql(['a','b','c','z'])
126
+ expect(map3['sample_next'].fields.keys).to eql(['a','b','c','d'])
127
+ end
128
+ end
129
+
74
130
  describe '#bind_fieldset' do
75
131
  it 'does not fail' do
76
132
  manager.bind_fieldset('sample', :query, set_query_base)
metadata CHANGED
@@ -2,14 +2,14 @@
2
2
  name: norikra
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.5
5
+ version: 0.0.6
6
6
  platform: java
7
7
  authors:
8
8
  - TAGOMORI Satoshi
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-01 00:00:00.000000000 Z
12
+ date: 2013-07-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mizuno
@@ -266,6 +266,7 @@ files:
266
266
  - lib/norikra/logger_mizuno_patch.rb
267
267
  - lib/norikra/output_pool.rb
268
268
  - lib/norikra/query.rb
269
+ - lib/norikra/query/ast.rb
269
270
  - lib/norikra/rpc.rb
270
271
  - lib/norikra/rpc/handler.rb
271
272
  - lib/norikra/rpc/http.rb