norikra 1.1.2-java → 1.2.0-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -717,6 +717,125 @@ module Norikra
717
717
  # ")"]]]]]]]]],
718
718
  # "<EOF>"]
719
719
 
720
+ #### SELECT a,NULLABLE(b),COUNT(DISTINCT NULLABLE(c)) FROM t GROUP BY a,NULLABLE(b)
721
+
722
+ # ["startEPLExpressionRule",
723
+ # ["eplExpression",
724
+ # ["selectExpr",
725
+ # "SELECT",
726
+ # ["selectClause",
727
+ # ["selectionList",
728
+ # ["selectionListElement",
729
+ # ["selectionListElementExpr",
730
+ # ["expression",
731
+ # ["caseExpression",["evalOrExpression",["evalAndExpression",["bitWiseExpression",["negatedExpression",
732
+ # ["evalEqualsExpression",["evalRelationalExpression",["concatenationExpr",["additiveExpression",
733
+ # ["multiplyExpression",["unaryExpression",
734
+ # ["eventPropertyOrLibFunction",
735
+ # ["eventProperty",
736
+ # ["eventPropertyAtomic", ["eventPropertyIdent", ["keywordAllowedIdent", "a"]]]]]]]]]]]]]]]]]]],
737
+ # ",",
738
+ # ["selectionListElement",
739
+ # ["selectionListElementExpr",
740
+ # ["expression",
741
+ # ["caseExpression",["evalOrExpression",["evalAndExpression",["bitWiseExpression",["negatedExpression",
742
+ # ["evalEqualsExpression",["evalRelationalExpression",["concatenationExpr",["additiveExpression",
743
+ # ["multiplyExpression",["unaryExpression",
744
+ # ["eventPropertyOrLibFunction",
745
+ # ["libFunction",
746
+ # ["libFunctionWithClass",
747
+ # ["funcIdentTop", ["escapableIdent", "NULLABLE"]],
748
+ # "(",
749
+ # ["libFunctionArgs",
750
+ # ["libFunctionArgItem",
751
+ # ["expressionWithTime",
752
+ # ["expressionQualifyable",
753
+ # ["expression",
754
+ # ["caseExpression",["evalOrExpression",["evalAndExpression",["bitWiseExpression",
755
+ # ["negatedExpression",["evalEqualsExpression",["evalRelationalExpression",
756
+ # ["concatenationExpr",["additiveExpression",["multiplyExpression",["unaryExpression",
757
+ # ["eventPropertyOrLibFunction",
758
+ # ["eventProperty",
759
+ # ["eventPropertyAtomic",
760
+ # ["eventPropertyIdent", ["keywordAllowedIdent", "b"]]]]]]]]]]]]]]]]]]]]],
761
+ # ")"]]]]]]]]]]]]]]]]],
762
+ # ",",
763
+ # ["selectionListElement",
764
+ # ["selectionListElementExpr",
765
+ # ["expression",
766
+ # ["caseExpression",["evalOrExpression",["evalAndExpression",["bitWiseExpression",["negatedExpression",
767
+ # ["evalEqualsExpression",["evalRelationalExpression",["concatenationExpr",["additiveExpression",
768
+ # ["multiplyExpression",["unaryExpression",
769
+ # ["builtinFunc",
770
+ # "COUNT",
771
+ # "(",
772
+ # "DISTINCT",
773
+ # ["expression",
774
+ # ["caseExpression",["evalOrExpression",["evalAndExpression",["bitWiseExpression",
775
+ # ["negatedExpression",["evalEqualsExpression",["evalRelationalExpression",
776
+ # ["concatenationExpr",["additiveExpression",["multiplyExpression",["unaryExpression",
777
+ # ["eventPropertyOrLibFunction",
778
+ # ["libFunction",
779
+ # ["libFunctionWithClass",
780
+ # ["funcIdentTop", ["escapableIdent", "NULLABLE"]],
781
+ # "(",
782
+ # ["libFunctionArgs",
783
+ # ["libFunctionArgItem",
784
+ # ["expressionWithTime",
785
+ # ["expressionQualifyable",
786
+ # ["expression",
787
+ # ["caseExpression",["evalOrExpression",["evalAndExpression",["bitWiseExpression",
788
+ # ["negatedExpression",["evalEqualsExpression",["evalRelationalExpression",
789
+ # ["concatenationExpr",["additiveExpression",["multiplyExpression",
790
+ # ["unaryExpression",
791
+ # ["eventPropertyOrLibFunction",
792
+ # ["eventProperty",
793
+ # ["eventPropertyAtomic",
794
+ # ["eventPropertyIdent",
795
+ # ["keywordAllowedIdent", "c"]]]]]]]]]]]]]]]]]]]]],
796
+ # ")"]]]]]]]]]]]]]]],
797
+ # ")"]]]]]]]]]]]]]]]]],
798
+ # "FROM",
799
+ # ["fromClause",
800
+ # ["streamExpression", ["eventFilterExpression", ["classIdentifier", ["escapableStr", "t"]]]],
801
+ # "regularJoin"],
802
+ # "GROUP",
803
+ # "BY",
804
+ # ["groupByListExpr",
805
+ # ["groupByListChoice",
806
+ # ["expression",
807
+ # ["caseExpression",["evalOrExpression",["evalAndExpression",["bitWiseExpression",["negatedExpression",
808
+ # ["evalEqualsExpression",["evalRelationalExpression",["concatenationExpr",["additiveExpression",
809
+ # ["multiplyExpression",["unaryExpression",
810
+ # ["eventPropertyOrLibFunction",
811
+ # ["eventProperty",
812
+ # ["eventPropertyAtomic", ["eventPropertyIdent", ["keywordAllowedIdent", "a"]]]]]]]]]]]]]]]]]],
813
+ # ",",
814
+ # ["groupByListChoice",
815
+ # ["expression",
816
+ # ["caseExpression",["evalOrExpression",["evalAndExpression",["bitWiseExpression",["negatedExpression",
817
+ # ["evalEqualsExpression",["evalRelationalExpression",["concatenationExpr",["additiveExpression",
818
+ # ["multiplyExpression",["unaryExpression",
819
+ # ["eventPropertyOrLibFunction",
820
+ # ["libFunction",
821
+ # ["libFunctionWithClass",
822
+ # ["funcIdentTop", ["escapableIdent", "NULLABLE"]],
823
+ # "(",
824
+ # ["libFunctionArgs",
825
+ # ["libFunctionArgItem",
826
+ # ["expressionWithTime",
827
+ # ["expressionQualifyable",
828
+ # ["expression",
829
+ # ["caseExpression",["evalOrExpression",["evalAndExpression",["bitWiseExpression",
830
+ # ["negatedExpression",["evalEqualsExpression",["evalRelationalExpression",
831
+ # ["concatenationExpr",["additiveExpression",["multiplyExpression",["unaryExpression",
832
+ # ["eventPropertyOrLibFunction",
833
+ # ["eventProperty",
834
+ # ["eventPropertyAtomic",
835
+ # ["eventPropertyIdent", ["keywordAllowedIdent", "b"]]]]]]]]]]]]]]]]]]]]],
836
+ # ")"]]]]]]]]]]]]]]]]]]],
837
+ # "<EOF>"]
838
+
720
839
  def astnode(tree)
721
840
  # com.espertech.esper.epl.generated.EsperEPL2GrammarParser.ruleNames[ast.ruleIndex] #=> "startEPLExpressionRule"
722
841
  # com.espertech.esper.epl.generated.EsperEPL2GrammarParser.ruleNames[ast.getChild(0).ruleIndex] #=> "eplExpression"
@@ -745,9 +864,11 @@ module Norikra
745
864
  end
746
865
 
747
866
  cls = case name
867
+ when 'expression' then ASTExpression
748
868
  when 'eventProperty' then ASTEventPropNode
749
869
  when 'selectionListElementExpr' then ASTSelectionElementNode
750
870
  when 'libFunction' then ASTLibFunctionNode
871
+ when 'libFunctionArgItem' then ASTLibFunctionArgItemNode
751
872
  when 'streamExpression', 'subSelectFilterExpr' then ASTStreamNode
752
873
  when 'patternFilterExpression' then ASTPatternNode
753
874
  when 'subQueryExpr' then ASTSubSelectNode
@@ -769,6 +890,14 @@ module Norikra
769
890
  @tree = tree
770
891
  end
771
892
 
893
+ def has_a?(name)
894
+ @children.size == 1 && (@children.first.name == name || @children.first.nodetype?(name))
895
+ end
896
+
897
+ def chain(*nodes)
898
+ nodes.reduce(self){|n, next_node| n && n.has_a?(next_node) ? n.child : nil }
899
+ end
900
+
772
901
  def nodetype?(*sym)
773
902
  false
774
903
  end
@@ -813,6 +942,28 @@ module Norikra
813
942
  end
814
943
  end
815
944
 
945
+ class ASTExpression < ASTNode
946
+ # ["expression",
947
+ # ["caseExpression", ["evalOrExpression", ["evalAndExpression", ["bitWiseExpression", ["negatedExpression",
948
+ # ["evalEqualsExpression", ["evalRelationalExpression", ["concatenationExpr", ["additiveExpression",
949
+ # ["multiplyExpression", ["unaryExpression", ["eventPropertyOrLibFunction",
950
+ # ["eventProperty",
951
+ # ["eventPropertyAtomic", ["eventPropertyIdent", ["keywordAllowedIdent", "s"]]]]]]]]]]]]]]]]]]]
952
+ def nodetype?(*sym)
953
+ sym.include?(:expression)
954
+ end
955
+
956
+ SIMPLE_PROPERTY_REFERENCE_NODES = [
957
+ "caseExpression", "evalOrExpression", "evalAndExpression", "bitWiseExpression", "negatedExpression",
958
+ "evalEqualsExpression", "evalRelationalExpression", "concatenationExpr", "additiveExpression",
959
+ "multiplyExpression", "unaryExpression", "eventPropertyOrLibFunction", "eventProperty"
960
+ ]
961
+ def propertyReference?
962
+ end_node = self.chain(*SIMPLE_PROPERTY_REFERENCE_NODES)
963
+ end_node && end_node.nodetype?(:property)
964
+ end
965
+ end
966
+
816
967
  class ASTEventPropNode < ASTNode # eventProperty
817
968
  ### "a"
818
969
  # ["eventProperty", ["eventPropertyAtomic", ["eventPropertyIdent", ["keywordAllowedIdent", "a"]]]
@@ -870,13 +1021,13 @@ module Norikra
870
1021
  if props.size > 1 # alias.fieldname or container_fieldname.key.$1 or fieldname.method(...)
871
1022
  non_calls = props.select{|p| p.children.size == 1 }.map{|p| p.find('eventPropertyIdent').find('keywordAllowedIdent').child.name }
872
1023
  if known_targets_aliases.include?(leading_name)
873
- [ {:f => non_calls[1..-1].join("."), :t => leading_name} ]
1024
+ [ {f: non_calls[1..-1].join("."), t: leading_name} ]
874
1025
  else
875
- [ {:f => non_calls.join("."), :t => default_target} ]
1026
+ [ {f: non_calls.join("."), t: default_target} ]
876
1027
  end
877
1028
  else # fieldname (default target)
878
1029
  # ["eventProperty", ["eventPropertyAtomic", ["eventPropertyIdent", ["keywordAllowedIdent", "a"]]]
879
- [ {:f => leading_name, :t => default_target } ]
1030
+ [ {f: leading_name, t: default_target } ]
880
1031
  end
881
1032
  end
882
1033
  end
@@ -892,7 +1043,7 @@ module Norikra
892
1043
  # ["eventProperty",
893
1044
  # ["eventPropertyAtomic", ["eventPropertyIdent", ["keywordAllowedIdent", "s"]]]]]]]]]]]]]]]]]]]
894
1045
 
895
- ### "count(*) AS cnt" => ["SELECTION_ELEMENT_EXPR", "count", "cnt"]
1046
+ ### "count(*) AS cnt"
896
1047
  # ["selectionListElementExpr",
897
1048
  # ["expression",
898
1049
  # ["caseExpression", ["evalOrExpression", ["evalAndExpression", ["bitWiseExpression", ["negatedExpression",
@@ -925,6 +1076,22 @@ module Norikra
925
1076
  end
926
1077
 
927
1078
  class ASTLibFunctionNode < ASTNode # LIB_FUNCTION
1079
+ ### NULLABLE field!
1080
+ # ["libFunction",
1081
+ # ["libFunctionWithClass",
1082
+ # ["funcIdentTop", ["escapableIdent", "NULLABLE"]],
1083
+ # "(",
1084
+ # ["libFunctionArgs",["libFunctionArgItem",["expressionWithTime",["expressionQualifyable",
1085
+ # ["expression",
1086
+ # ["caseExpression",["evalOrExpression",["evalAndExpression",["bitWiseExpression",["negatedExpression",
1087
+ # ["evalEqualsExpression",["evalRelationalExpression",["concatenationExpr",["additiveExpression",
1088
+ # ["multiplyExpression",["unaryExpression",
1089
+ # ["eventPropertyOrLibFunction",
1090
+ # ["eventProperty",
1091
+ # ["eventPropertyAtomic",
1092
+ # ["eventPropertyIdent", ["keywordAllowedIdent", "b"]]]]]]]]]]]]]]]]]]]]],
1093
+ # ")"]]]
1094
+
928
1095
  ### foo is function
929
1096
  # "foo()" => ["libFunction", ["libFunctionWithClass", ["funcIdentTop", ["escapableIdent", "foo"]], "(", ")"]]
930
1097
 
@@ -948,6 +1115,25 @@ module Norikra
948
1115
  # ["libFunctionArgItem", ["expressionWithTime", ["expressionQualifyable", ["expression", EXPRESSION... ]]]]],
949
1116
  # ")"]]
950
1117
 
1118
+ ### foo(value)
1119
+ # ["libFunction",
1120
+ # ["libFunctionWithClass",
1121
+ # ["funcIdentTop", ["escapableIdent", "FOO"]],
1122
+ # "(",
1123
+ # ["libFunctionArgs",
1124
+ # ["libFunctionArgItem",
1125
+ # ["expressionWithTime",
1126
+ # ["expressionQualifyable",
1127
+ # ["expression",
1128
+ # ["caseExpression",["evalOrExpression",["evalAndExpression",["bitWiseExpression",["negatedExpression",
1129
+ # ["evalEqualsExpression",["evalRelationalExpression",["concatenationExpr",["additiveExpression",
1130
+ # ["multiplyExpression",["unaryExpression",
1131
+ # ["eventPropertyOrLibFunction",
1132
+ # ["eventProperty",
1133
+ # ["eventPropertyAtomic",
1134
+ # ["eventPropertyIdent", ["keywordAllowedIdent", "value"]]]]]]]]]]]]]]]]]]]]],
1135
+ # ")"]]
1136
+
951
1137
  ### foo is property
952
1138
  ### "foo.bar()"
953
1139
  # ["libFunction",
@@ -1067,33 +1253,67 @@ module Norikra
1067
1253
  # "(",
1068
1254
  # ")"]]
1069
1255
 
1256
+ def function_name
1257
+ f = self.find("funcIdentTop")
1258
+ if e = f.find("escapableIdent")
1259
+ e.child.name
1260
+ else
1261
+ f.child.name
1262
+ end
1263
+ end
1264
+
1070
1265
  def nodetype?(*sym)
1071
1266
  sym.include?(:lib) || sym.include?(:libfunc)
1072
1267
  end
1073
1268
 
1074
1269
  def fields(default_target=nil, known_targets_aliases=[])
1270
+ # class identifier is receiver: "IDENT.func()"
1075
1271
  identifier = self.find("classIdentifier")
1076
-
1077
- return [] if identifier.nil? # built-in function call (no receivers exists)
1078
-
1079
- if identifier.children.size == 1 && Norikra::Query.imported_java_class?(identifier.find("escapableStr").child.name)
1080
- # Java imported class name (ex: 'Math.abs(-1)')
1081
- self.listup(:prop).map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
1272
+ if identifier
1273
+ if identifier.children.size == 1 && Norikra::Query.imported_java_class?(identifier.find("escapableStr").child.name)
1274
+ # Java imported class name (ex: 'Math.abs(-1)')
1275
+ self.listup(:prop).map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
1276
+ else
1277
+ parts = identifier.listup('escapableStr').map{|node| node.child.name }
1278
+ target, fieldname = if parts.size == 1
1279
+ [ default_target, parts.first ]
1280
+ elsif known_targets_aliases.include?( parts.first )
1281
+ [ parts[0], parts[1..-1].join(".") ]
1282
+ else
1283
+ [ default_target, parts.join(".") ]
1284
+ end
1285
+ children_list = self.listup(:prop).map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
1286
+ [{f: fieldname, t: target}] + children_list
1287
+ end
1082
1288
  else
1083
- parts = identifier.listup('escapableStr').map{|node| node.child.name }
1084
- target, fieldname = if parts.size == 1
1085
- [ default_target, parts.first ]
1086
- elsif known_targets_aliases.include?( parts.first )
1087
- [ parts[0], parts[1..-1].join(".") ]
1088
- else
1089
- [ default_target, parts.join(".") ]
1090
- end
1091
- children_list = self.listup(:prop).map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
1092
- [{:f => fieldname, :t => target}] + children_list
1289
+ if self.function_name.upcase == 'NULLABLE'
1290
+ props = self.listup(:prop).map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
1291
+ props.each do |def_item|
1292
+ def_item[:n] = true # nullable: true
1293
+ end
1294
+ props
1295
+ else
1296
+ self.listup(:prop).map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
1297
+ end
1093
1298
  end
1094
1299
  end
1095
1300
  end
1096
1301
 
1302
+ class ASTLibFunctionArgItemNode < ASTNode
1303
+ # ["libFunctionArgs",
1304
+ # ["libFunctionArgItem",
1305
+ # ["expressionWithTime",
1306
+ # ["expressionQualifyable",
1307
+ # ["expression", ... ]]]]
1308
+ def nodetype?(*sym)
1309
+ sym.include?(:libarg)
1310
+ end
1311
+
1312
+ def expression
1313
+ self.chain("expressionWithTime", "expressionQualifyable", "expression")
1314
+ end
1315
+ end
1316
+
1097
1317
  class ASTStreamNode < ASTNode # streamExpression, subSelectFilterExpr
1098
1318
  def self.generate(name, children, tree)
1099
1319
  if children.first.name == 'eventFilterExpression'
data/lib/norikra/query.rb CHANGED
@@ -27,6 +27,12 @@ module Norikra
27
27
  @aliases = nil
28
28
  @subqueries = nil
29
29
  @fields = nil
30
+ @nullable_fields = nil
31
+ end
32
+
33
+ def inspect
34
+ internal = ['name', 'group', 'expression', 'statement_name', 'targets', 'aliases', 'fields', 'nullable_fields', 'subqueries', 'fieldsets'].map{|x| "@#{x}=" + self.send(x.to_sym).inspect }.join(", ")
35
+ "<#Norikra::Query:#{self.object_id} #{internal}>"
30
36
  end
31
37
 
32
38
  def <=>(other)
@@ -45,16 +51,8 @@ module Norikra
45
51
  end
46
52
  end
47
53
 
48
- def self.loopback(group)
49
- group && group =~ /^LOOPBACK\((.+)\)$/ && $1
50
- end
51
-
52
- def self.stdout?(group)
53
- group && group == "STDOUT()"
54
- end
55
-
56
54
  def dup
57
- self.class.new(:name => @name, :group => @group, :expression => @expression.dup)
55
+ self.class.new(name: @name, group: @group, expression: @expression.dup)
58
56
  end
59
57
 
60
58
  def to_hash
@@ -70,8 +68,26 @@ module Norikra
70
68
  end
71
69
 
72
70
  def invalid?
73
- # check query is invalid as Norikra query or not
74
- self.ast.listup('selectionListElement').any?{|node| node.children.map(&:name).any?{|name| name == '*' } }
71
+ # check query is invalid as Norikra query or not, and return invalid reason (or nil for valid queries)
72
+
73
+ # "SELECT *" is prohibited because each Norikra targets does not have all fields users expect
74
+ if self.ast.listup('selectionListElement').any?{|node| node.children.map(&:name).any?{|name| name == '*' } }
75
+ # '*' is a direct child of selectionListElement, not selectionListElementExpr
76
+ return "'SELECT *' is prohibited in query"
77
+ end
78
+
79
+ # NULLABLE(...) can have "a" single field reference only
80
+ nullables = self.ast.listup(:libfunc).select{|node| node.function_name.upcase == "NULLABLE" }
81
+ if nullables.size > 0
82
+ unless nullables.all?{|node| args = node.find("libFunctionArgs"); args && args.has_a?(:libarg) }
83
+ return "NULLABLE(...) must have a field argument"
84
+ end
85
+ unless nullables.all?{|node| exp = node.find(:libarg).expression; exp && exp.propertyReference? }
86
+ return "NULLABLE(...) can get a field, not expression"
87
+ end
88
+ end
89
+
90
+ nil
75
91
  end
76
92
 
77
93
  def targets
@@ -93,74 +109,85 @@ module Norikra
93
109
  end
94
110
 
95
111
  def explore(outer_targets=[], alias_overridden={})
96
- fields = {}
97
- alias_map = {}.merge(alias_overridden)
112
+ fields = {
113
+ defs: { all: [], unknown: [], target: {} },
114
+ nullables: { all: [], unknown: [], target: {} },
115
+ }
116
+
117
+ alias_map = alias_overridden.dup
98
118
 
99
- all = []
100
- unknowns = []
101
119
  self.ast.listup(:stream).each do |node|
102
120
  node.aliases.each do |alias_name, target|
103
121
  alias_map[alias_name] = target
104
122
  end
105
123
  node.targets.each do |target|
106
- fields[target] ||= []
124
+ fields[:defs][:target][target] = []
125
+ fields[:nullables][:target][target] = []
107
126
  end
108
127
  end
109
128
 
110
- dup_aliases = (alias_map.keys & fields.keys)
111
- unless dup_aliases.empty?
112
- raise Norikra::ClientError, "Invalid alias '#{dup_aliases.join(',')}', same with target name"
113
- end
114
-
115
- default_target = fields.keys.size == 1 ? fields.keys.first : nil
129
+ # default target should be out of effect of outer_targets
130
+ default_target = fields[:defs][:target].keys.size == 1 ? fields[:defs][:target].keys.first : nil
116
131
 
117
132
  outer_targets.each do |t|
118
- fields[t] ||= []
133
+ fields[:defs][:target][t] ||= []
134
+ fields[:nullables][:target][t] ||= []
119
135
  end
120
136
 
121
- field_bag = []
122
- self.subqueries.each do |subquery|
123
- field_bag.push(subquery.explore(fields.keys, alias_map))
137
+ dup_aliases = (alias_map.keys & fields[:defs][:target].keys)
138
+ unless dup_aliases.empty?
139
+ raise Norikra::ClientError, "Invalid alias '#{dup_aliases.join(',')}', same with target name"
124
140
  end
125
141
 
126
142
  # names of 'AS'
127
143
  field_aliases = self.ast.listup(:selection).map(&:alias).compact
128
144
 
129
- known_targets_aliases = fields.keys + alias_map.keys
145
+ known_targets_aliases = fields[:defs][:target].keys + alias_map.keys
130
146
  self.ast.fields(default_target, known_targets_aliases).each do |field_def|
131
147
  f = field_def[:f]
132
148
  next if field_aliases.include?(f)
133
149
 
134
- all.push(f)
150
+ fields[:defs][:all].push(f)
151
+ fields[:nullables][:all].push(f) if field_def[:n]
135
152
 
136
153
  if field_def[:t]
137
154
  t = alias_map[field_def[:t]] || field_def[:t]
138
- unless fields[t]
155
+ unless fields[:defs][:target][t]
139
156
  raise Norikra::ClientError, "unknown target alias name for: #{field_def[:t]}.#{field_def[:f]}"
140
157
  end
141
- fields[t].push(f)
142
-
158
+ fields[:defs][:target][t].push(f)
159
+ fields[:nullables][:target][t].push(f) if field_def[:n]
143
160
  else
144
- unknowns.push(f)
161
+ fields[:defs][:unknown].push(f)
162
+ fields[:nullables][:unknown].push(f) if field_def[:n]
145
163
  end
146
164
  end
147
165
 
148
- field_bag.each do |bag|
149
- all += bag['']
150
- unknowns += bag[nil]
151
- bag.keys.each do |t|
152
- fields[t] ||= []
153
- fields[t] += bag[t]
166
+ self.subqueries.each do |subquery|
167
+ sub = {}
168
+ sub[:defs], sub[:nullables] = subquery.explore(fields[:defs][:target].keys, alias_map)
169
+
170
+ [:defs, :nullables].each do |group|
171
+ fields[group][:all] += sub[group].delete('')
172
+ fields[group][:unknown] += sub[group].delete(nil)
173
+ sub[group].keys.each do |t|
174
+ fields[group][:target][t] ||= []
175
+ fields[group][:target][t] += sub[group][t]
176
+ end
154
177
  end
155
178
  end
156
179
 
157
- fields.keys.each do |target|
158
- fields[target] = fields[target].sort.uniq
159
- end
160
- fields[''] = all.sort.uniq
161
- fields[nil] = unknowns.sort.uniq
180
+ compact = ->(data){
181
+ r = {}
182
+ data[:target].keys.each do |t|
183
+ r[t] = data[:target][t].sort.uniq
184
+ end
185
+ r[''] = data[:all].sort.uniq
186
+ r[nil] = data[:unknown].sort.uniq
187
+ r
188
+ }
162
189
 
163
- fields
190
+ [ compact.(fields[:defs]), compact.(fields[:nullables]) ]
164
191
  end
165
192
 
166
193
  def fields(target='')
@@ -168,10 +195,18 @@ module Norikra
168
195
  # target nil: fields for unknown targets
169
196
  return @fields[target] if @fields
170
197
 
171
- @fields = explore()
198
+ @fields, @nullable_fields = explore()
172
199
  @fields[target]
173
200
  end
174
201
 
202
+ def nullable_fields(target='')
203
+ # argument target is same with #fields
204
+ return @nullable_fields[target] if @nullable_fields
205
+
206
+ @fields, @nullable_fields = explore()
207
+ @nullable_fields[target]
208
+ end
209
+
175
210
  class ParseRuleSelectorImpl
176
211
  include Java::ComEspertechEsperEplParse::ParseRuleSelector
177
212
  def invokeParseRule(parser)
@@ -218,10 +253,32 @@ module Norikra
218
253
  end
219
254
 
220
255
  def self.rewrite_query(statement_model, mapping)
256
+ rewrite_nullable_fields(statement_model)
221
257
  rewrite_event_type_name(statement_model, mapping)
222
258
  rewrite_event_field_name(statement_model, mapping)
223
259
  end
224
260
 
261
+ def self.rewrite_nullable_fields(statement_model)
262
+ # NULLABLE(field) -> field
263
+ ## NOTICE: NULLABLE(...) cannot be used in view parameters. ex: target.std:unique(NULLABLE(a)) -> x
264
+ rewriter = lambda {|node|
265
+ if node.respond_to?(:getExpression)
266
+ exp = node.getExpression
267
+ if exp && exp.is_a?(Java::ComEspertechEsperClientSoda::DotExpression) && exp.getChain.size == 1
268
+ # top-level lib function
269
+ # exp.getChain[0] #=> Java::ComEspertechEsperClientSoda::DotExpressionItem
270
+ if exp.getChain[0].name.upcase == 'NULLABLE'
271
+ node.setExpression(exp.getChain[0].getParameters[0])
272
+ end
273
+ end
274
+ end
275
+ }
276
+ recaller = lambda {|node|
277
+ Norikra::Query.rewrite_nullable_fields(node.getModel)
278
+ }
279
+ traverse_fields(rewriter, recaller, statement_model)
280
+ end
281
+
225
282
  def self.rewrite_event_field_name(statement_model, mapping)
226
283
  # mapping: {target_name => query_event_type_name}
227
284
  # mapping is for target name rewriting of fully qualified field name access
@@ -235,7 +292,7 @@ module Norikra
235
292
  # model.getWhereClause.getChildren[1].getChildren[0].getPropertyName #=> 'field.key1.$1'
236
293
  # model.getWhereClause.getChildren[2].getChildren[0].getChain[0].getName #=> 'opts.num.$0' from opts.num.$0.length()
237
294
 
238
- query = Norikra::Query.new(:name => 'dummy name by .rewrite_event_field_name', :expression => statement_model.toEPL)
295
+ query = Norikra::Query.new(name: 'dummy name by .rewrite_event_field_name', expression: statement_model.toEPL)
239
296
  targets = query.targets
240
297
  fqfs_prefixes = targets + query.aliases
241
298
 
@@ -13,9 +13,9 @@ class Norikra::RPC::Handler
13
13
 
14
14
  def logging(type, handler, args=[])
15
15
  if type == :manage
16
- debug "RPC", :handler => handler.to_s, :args => args
16
+ debug("RPC"){ { handler: handler.to_s, args: args } }
17
17
  else
18
- trace "RPC", :handler => handler.to_s, :args => args
18
+ trace("RPC"){ { handler: handler.to_s, args: args } }
19
19
  end
20
20
 
21
21
  begin
@@ -67,7 +67,7 @@ class Norikra::RPC::Handler
67
67
 
68
68
  def register(query_name, query_group, expression)
69
69
  logging(:manage, :register, [query_name, query_group, expression]){
70
- r = @engine.register(Norikra::Query.new(:name => query_name, :group => query_group, :expression => expression))
70
+ r = @engine.register(Norikra::Query.new(name: query_name, group: query_group, expression: expression))
71
71
  !!r
72
72
  }
73
73
  end
@@ -35,7 +35,7 @@ module Norikra::RPC
35
35
  info "RPC server #{@host}:#{@port}, #{@threads} threads"
36
36
  @thread = Thread.new do
37
37
  @mizuno = Mizuno::Server.new
38
- @mizuno.run(@app, :embedded => true, :threads => @threads, :port => @port, :host => @host)
38
+ @mizuno.run(@app, embedded: true, threads: @threads, port: @port, host: @host)
39
39
  end
40
40
  end
41
41