rdl 2.1.0 → 2.2.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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +7 -6
- data/CHANGES.md +29 -0
- data/README.md +94 -26
- data/lib/rdl/boot.rb +82 -41
- data/lib/rdl/boot_rails.rb +5 -0
- data/lib/rdl/config.rb +9 -1
- data/lib/rdl/query.rb +2 -2
- data/lib/rdl/typecheck.rb +972 -225
- data/lib/rdl/types/annotated_arg.rb +8 -0
- data/lib/rdl/types/ast_node.rb +73 -0
- data/lib/rdl/types/bot.rb +8 -0
- data/lib/rdl/types/bound_arg.rb +63 -0
- data/lib/rdl/types/computed.rb +48 -0
- data/lib/rdl/types/dependent_arg.rb +9 -0
- data/lib/rdl/types/dynamic.rb +61 -0
- data/lib/rdl/types/finite_hash.rb +54 -9
- data/lib/rdl/types/generic.rb +33 -0
- data/lib/rdl/types/intersection.rb +8 -0
- data/lib/rdl/types/lexer.rex +6 -1
- data/lib/rdl/types/lexer.rex.rb +13 -1
- data/lib/rdl/types/method.rb +14 -0
- data/lib/rdl/types/nominal.rb +8 -0
- data/lib/rdl/types/non_null.rb +8 -0
- data/lib/rdl/types/optional.rb +8 -0
- data/lib/rdl/types/parser.racc +31 -5
- data/lib/rdl/types/parser.tab.rb +540 -302
- data/lib/rdl/types/rdl_types.rb +45 -0
- data/lib/rdl/types/singleton.rb +14 -1
- data/lib/rdl/types/string.rb +104 -0
- data/lib/rdl/types/structural.rb +8 -0
- data/lib/rdl/types/top.rb +8 -0
- data/lib/rdl/types/tuple.rb +32 -8
- data/lib/rdl/types/type.rb +54 -11
- data/lib/rdl/types/union.rb +41 -2
- data/lib/rdl/types/var.rb +10 -0
- data/lib/rdl/types/vararg.rb +8 -0
- data/lib/rdl/util.rb +13 -10
- data/lib/rdl/wrap.rb +271 -27
- data/lib/rdl_disable.rb +16 -2
- data/lib/types/active_record.rb +1 -0
- data/lib/types/core/array.rb +442 -23
- data/lib/types/core/basic_object.rb +3 -3
- data/lib/types/core/bigdecimal.rb +5 -0
- data/lib/types/core/class.rb +2 -0
- data/lib/types/core/dir.rb +3 -3
- data/lib/types/core/enumerable.rb +4 -4
- data/lib/types/core/enumerator.rb +1 -1
- data/lib/types/core/file.rb +4 -4
- data/lib/types/core/float.rb +203 -0
- data/lib/types/core/hash.rb +390 -15
- data/lib/types/core/integer.rb +223 -10
- data/lib/types/core/io.rb +2 -2
- data/lib/types/core/kernel.rb +8 -5
- data/lib/types/core/marshal.rb +3 -0
- data/lib/types/core/module.rb +3 -3
- data/lib/types/core/numeric.rb +0 -2
- data/lib/types/core/object.rb +5 -5
- data/lib/types/core/pathname.rb +2 -2
- data/lib/types/core/process.rb +1 -3
- data/lib/types/core/range.rb +1 -1
- data/lib/types/core/regexp.rb +2 -2
- data/lib/types/core/set.rb +1 -1
- data/lib/types/core/string.rb +408 -16
- data/lib/types/core/symbol.rb +3 -3
- data/lib/types/core/time.rb +1 -1
- data/lib/types/core/uri.rb +13 -13
- data/lib/types/rails/_helpers.rb +7 -1
- data/lib/types/rails/action_controller/mime_responds.rb +2 -0
- data/lib/types/rails/active_record/associations.rb +42 -30
- data/lib/types/rails/active_record/comp_types.rb +637 -0
- data/lib/types/rails/active_record/finder_methods.rb +1 -1
- data/lib/types/rails/active_record/model_schema.rb +28 -16
- data/lib/types/rails/active_record/relation.rb +5 -3
- data/lib/types/rails/active_record/sql-strings.rb +166 -0
- data/lib/types/rails/string.rb +1 -1
- data/lib/types/sequel.rb +1 -0
- data/lib/types/sequel/comp_types.rb +581 -0
- data/rdl.gemspec +5 -4
- data/test/test_alias.rb +4 -0
- data/test/test_array_types.rb +244 -0
- data/test/test_bound_types.rb +80 -0
- data/test/test_contract.rb +4 -0
- data/test/test_dsl.rb +5 -0
- data/test/test_dyn_comptype_checks.rb +206 -0
- data/test/test_generic.rb +21 -20
- data/test/test_hash_types.rb +322 -0
- data/test/test_intersection.rb +1 -0
- data/test/test_le.rb +29 -4
- data/test/test_member.rb +3 -1
- data/test/test_parser.rb +5 -0
- data/test/test_query.rb +1 -0
- data/test/test_rdl.rb +63 -28
- data/test/test_rdl_type.rb +4 -0
- data/test/test_string_types.rb +102 -0
- data/test/test_type_contract.rb +59 -37
- data/test/test_typecheck.rb +480 -75
- data/test/test_types.rb +17 -0
- data/test/test_wrap.rb +5 -0
- metadata +35 -5
- data/lib/types/rails/active_record/schema_types.rb +0 -51
@@ -1,2 +1,2 @@
|
|
1
1
|
RDL.nowrap :'ActiveRecord::FinderMethods'
|
2
|
-
RDL.type :'ActiveRecord::FinderMethods', :exists?, '(?String or
|
2
|
+
#RDL.type :'ActiveRecord::FinderMethods', :exists?, '(?String or Integer or Array<String> or Hash<String or Symbol, %any>) -> %bool'
|
@@ -1,37 +1,49 @@
|
|
1
|
+
class ActiveRecord::Base
|
2
|
+
extend RDL::RDLAnnotate
|
3
|
+
end
|
4
|
+
|
1
5
|
module ActiveRecord::ModelSchema::ClassMethods
|
2
6
|
extend RDL::RDLAnnotate
|
3
7
|
|
4
8
|
rdl_post(:load_schema!) { |ret| # load_schema! doesn't return anything interesting
|
9
|
+
=begin
|
5
10
|
columns_hash.each { |name, col|
|
6
11
|
t = RDL::Rails.column_to_rdl(col.type)
|
7
12
|
if col.null
|
8
13
|
# may be null; show nullability in return type
|
9
14
|
rdl_type name, "() -> #{t} or nil" # getter
|
10
|
-
rdl_type "#{name}=", "(#{t}) -> #{t} or nil" # setter
|
11
|
-
rdl_type
|
12
|
-
rdl_type
|
13
|
-
rdl_type
|
15
|
+
rdl_type :"#{name}=", "(#{t}) -> #{t} or nil" # setter
|
16
|
+
rdl_type :write_attribute, "(:#{name}, #{t}) -> %bool"
|
17
|
+
rdl_type :update_attribute, "(:#{name}, #{t}) -> %bool"
|
18
|
+
rdl_type :update_column, "(:#{name}, #{t}) -> %bool"
|
14
19
|
else
|
15
20
|
# not null; can't truly check in type system but hint via the name
|
16
21
|
rdl_type name, "() -> !#{t}" # getter
|
17
|
-
rdl_type "#{name}=", "(!#{t}) -> !#{t}" # setter
|
18
|
-
rdl_type
|
19
|
-
rdl_type
|
20
|
-
rdl_type
|
22
|
+
rdl_type :"#{name}=", "(!#{t}) -> !#{t}" # setter
|
23
|
+
rdl_type :write_attribute, "(:#{name}, !#{t}) -> %bool"
|
24
|
+
rdl_type :update_attribute, "(:#{name}, #{t}) -> %bool"
|
25
|
+
rdl_type :update_column, "(:#{name}, #{t}) -> %bool"
|
21
26
|
end
|
22
27
|
}
|
23
28
|
|
24
29
|
attribute_types = RDL::Rails.attribute_types(self)
|
25
|
-
rdl_type 'self.find_by', '(' + attribute_types + ") -> #{self} or nil"
|
26
|
-
rdl_type 'self.find_by!', '(' + attribute_types + ") -> #{self}"
|
27
|
-
rdl_type
|
28
|
-
rdl_type
|
29
|
-
rdl_type 'attributes=', '(' + attribute_types + ') -> %bool'
|
30
|
+
rdl_type :'self.find_by', '(' + attribute_types + ") -> #{self} or nil"
|
31
|
+
rdl_type :'self.find_by!', '(' + attribute_types + ") -> #{self}"
|
32
|
+
rdl_type :update, '(' + attribute_types + ') -> %bool'
|
33
|
+
rdl_type :update_columns, '(' + attribute_types + ') -> %bool'
|
34
|
+
rdl_type :'attributes=', '(' + attribute_types + ') -> %bool'
|
30
35
|
|
31
36
|
# If called with String arguments, can't check types as precisely
|
32
|
-
rdl_type
|
33
|
-
rdl_type
|
34
|
-
rdl_type
|
37
|
+
rdl_type :write_attribute, '(String, %any) -> %bool'
|
38
|
+
rdl_type :update_attribute, '(String, %any) -> %bool'
|
39
|
+
rdl_type :update_column, '(String, %any) -> %bool'
|
40
|
+
|
41
|
+
rdl_type :'self.joins', "(Symbol or String) -> ActiveRecord::Associations::CollectionProxy<#{self.to_s}>"
|
42
|
+
rdl_type :'self.none', "() -> ActiveRecord::Associations::CollectionProxy<#{self.to_s}>"
|
43
|
+
rdl_type :'self.where', '(String, *%any) -> ActiveRecord::Associations::CollectionProxy<t>'
|
44
|
+
rdl_type :'self.where', '(**%any) -> ActiveRecord::Associations::CollectionProxy<t>'
|
45
|
+
=end
|
35
46
|
true
|
36
47
|
}
|
48
|
+
|
37
49
|
end
|
@@ -2,10 +2,12 @@ RDL.nowrap :'ActiveRecord::Relation'
|
|
2
2
|
|
3
3
|
RDL.type_params :'ActiveRecord::Relation', [:t], :all?
|
4
4
|
|
5
|
-
|
5
|
+
=begin
|
6
|
+
RDL.type :'ActiveRecord::Relation', :[], '(Integer) -> t'
|
6
7
|
RDL.type :'ActiveRecord::Relation', :empty?, '() -> %bool'
|
7
8
|
RDL.type :'ActiveRecord::Relation', :first, '() -> t'
|
8
|
-
RDL.type :'ActiveRecord::Relation', :length, '() ->
|
9
|
-
RDL.type :'ActiveRecord::Relation', :sort, '() {(t, t) ->
|
9
|
+
RDL.type :'ActiveRecord::Relation', :length, '() -> Integer'
|
10
|
+
RDL.type :'ActiveRecord::Relation', :sort, '() {(t, t) -> Integer} -> Array<t>'
|
10
11
|
RDL.type :'ActiveRecord::Relation', :each, '() -> Enumerator<t>'
|
11
12
|
RDL.type :'ActiveRecord::Relation', :each, '() { (t) -> %any } -> Array<t>'
|
13
|
+
=end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'sql-parser'
|
2
|
+
|
3
|
+
# Mocking the SQLVistor internal class behavior to do our own stuff
|
4
|
+
class ASTVisitor
|
5
|
+
def initialize(table, targs)
|
6
|
+
@table = table # default table name, if we don't have a qualified column name
|
7
|
+
@targs = targs
|
8
|
+
end
|
9
|
+
|
10
|
+
def visit(node)
|
11
|
+
node.accept(self)
|
12
|
+
end
|
13
|
+
|
14
|
+
def binary_op(o)
|
15
|
+
table, column = visit(o.left)
|
16
|
+
ident = visit(o.right)
|
17
|
+
query_type = @targs[ident] || @targs.last
|
18
|
+
schema_type = RDL::Globals.ar_db_schema[table.classify.to_sym].params[0].elts[column.to_sym]
|
19
|
+
query_type = query_type.elts[column.to_sym] if query_type.is_a? RDL::Type::FiniteHashType
|
20
|
+
# puts query_type, schema_type
|
21
|
+
raise RDL::Typecheck::StaticTypeError, "type error" unless query_type <= schema_type
|
22
|
+
end
|
23
|
+
|
24
|
+
alias_method :visit_Greater, :binary_op
|
25
|
+
alias_method :visit_Equals, :binary_op
|
26
|
+
alias_method :visit_Less, :binary_op
|
27
|
+
alias_method :visit_GreaterOrEquals, :binary_op
|
28
|
+
|
29
|
+
def visit_And(o)
|
30
|
+
visit(o.left)
|
31
|
+
visit(o.right)
|
32
|
+
end
|
33
|
+
|
34
|
+
def visit_Subquery(o)
|
35
|
+
raise RDL::Typecheck::StaticTypeError, "only works with SELECT queries now" unless o.query_specification.is_a? SQLParser::Statement::Select
|
36
|
+
select_query = o.query_specification
|
37
|
+
raise RDL::Typecheck::StaticTypeError, "expected only 1 column in SELECT sub queries" unless select_query.list.is_a? SQLParser::Statement::SelectList and select_query.list.columns.length == 1
|
38
|
+
column = select_query.list.columns[0].name
|
39
|
+
table = select_query.table_expression.from_clause.tables[0].name
|
40
|
+
visitor = ASTVisitor.new table, @targs
|
41
|
+
search_cond = select_query.table_expression.where_clause.search_condition
|
42
|
+
visitor.visit(search_cond)
|
43
|
+
RDL::Type::GenericType.new(RDL::Type::NominalType.new(Array), RDL::Globals.ar_db_schema[table.classify.to_sym].params[0].elts[column.to_sym])
|
44
|
+
end
|
45
|
+
|
46
|
+
def visit_In(o)
|
47
|
+
table, column = visit(o.left)
|
48
|
+
ident = visit(o.right)
|
49
|
+
# TODO: add a case where ident is an integer and targs doesn't have named params, but just ?-ed params
|
50
|
+
if ident.is_a? Integer and @targs.last.is_a? RDL::Type::FiniteHashType
|
51
|
+
# query_params is a finite hash
|
52
|
+
query_params = @targs.last.elts
|
53
|
+
# this is a hack, assumes keys are in order, which isn't necessarily true
|
54
|
+
query_type = query_params[query_params.keys[ident - 1]]
|
55
|
+
elsif ident.is_a? RDL::Type::GenericType
|
56
|
+
query_type = ident
|
57
|
+
else
|
58
|
+
# puts "(TODO) Unexpected"
|
59
|
+
end
|
60
|
+
|
61
|
+
schema_type = RDL::Globals.ar_db_schema[table.classify.to_sym].params[0].elts[column.to_sym]
|
62
|
+
# IN works with arrays
|
63
|
+
promoted = if query_type.is_a?(RDL::Type::TupleType) then query_type.promote else query_type end
|
64
|
+
if promoted.is_a? RDL::Type::GenericType
|
65
|
+
# base type is Array, maybe add check?
|
66
|
+
raise RDL::Typecheck::StaticTypeError, "type error" unless promoted.params[0] <= schema_type
|
67
|
+
else
|
68
|
+
raise RDL::Typecheck::StaticTypeError, "some other type after promotion"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def visit_Not(o)
|
73
|
+
visit(o.value)
|
74
|
+
end
|
75
|
+
|
76
|
+
def visit_Integer(o)
|
77
|
+
return o.value
|
78
|
+
end
|
79
|
+
|
80
|
+
def visit_QualifiedColumn(o)
|
81
|
+
[o.table.name, o.column.name]
|
82
|
+
end
|
83
|
+
|
84
|
+
def visit_Column(o)
|
85
|
+
[@table, o.name]
|
86
|
+
end
|
87
|
+
|
88
|
+
def visit_InValueList(o)
|
89
|
+
o.values.value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def handle_sql_strings(trec, targs)
|
94
|
+
parser = SQLParser::Parser.new
|
95
|
+
|
96
|
+
case trec
|
97
|
+
when RDL::Type::GenericType
|
98
|
+
if trec.base.klass == ActiveRecord_Relation
|
99
|
+
handle_sql_strings trec.params[0], targs
|
100
|
+
elsif trec.base.klass == JoinTable
|
101
|
+
# works only for the base class right now, need to extend for the params as well
|
102
|
+
base_klass = trec.params[0]
|
103
|
+
joined_with = trec.params[1]
|
104
|
+
case joined_with
|
105
|
+
when RDL::Type::UnionType
|
106
|
+
joined_with.types.each do |klass|
|
107
|
+
# add the joining association column on this
|
108
|
+
sql_query = "SELECT * FROM `#{base_klass.name.tableize}` INNER JOIN `#{klass.name.tableize}` ON a.id = b.a_id WHERE #{build_string_from_precise_string(targs)}"
|
109
|
+
# puts sql_query
|
110
|
+
begin
|
111
|
+
ast = parser.scan_str(sql_query)
|
112
|
+
rescue Racc::ParseError => e
|
113
|
+
# puts "There was a parse error with above query, moving on"
|
114
|
+
return
|
115
|
+
end
|
116
|
+
search_cond = ast.query_expression.table_expression.where_clause.search_condition
|
117
|
+
visitor = ASTVisitor.new base_klass.name.tableize, targs
|
118
|
+
visitor.visit(search_cond)
|
119
|
+
end
|
120
|
+
else
|
121
|
+
# TODO
|
122
|
+
# puts "== TODO =="
|
123
|
+
end
|
124
|
+
else
|
125
|
+
# puts "UNEXPECTED #{trec}, #{targs}"
|
126
|
+
end
|
127
|
+
when RDL::Type::NominalType
|
128
|
+
base_klass = trec
|
129
|
+
sql_query = "SELECT * FROM `#{base_klass.name.tableize}` WHERE #{build_string_from_precise_string(targs)}"
|
130
|
+
# puts sql_query
|
131
|
+
ast = parser.scan_str(sql_query)
|
132
|
+
search_cond = ast.query_expression.table_expression.where_clause.search_condition
|
133
|
+
visitor = ASTVisitor.new base_klass.name.tableize, targs
|
134
|
+
visitor.visit(search_cond)
|
135
|
+
when RDL::Type::SingletonType
|
136
|
+
base_klass = trec
|
137
|
+
sql_query = "SELECT * FROM `#{base_klass.val.to_s.tableize}` WHERE #{build_string_from_precise_string(targs)}"
|
138
|
+
# puts sql_query
|
139
|
+
ast = parser.scan_str(sql_query)
|
140
|
+
search_cond = ast.query_expression.table_expression.where_clause.search_condition
|
141
|
+
visitor = ASTVisitor.new base_klass.val.to_s.tableize, targs
|
142
|
+
visitor.visit(search_cond)
|
143
|
+
else
|
144
|
+
# puts "UNEXPECTED #{trec}, #{targs}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def build_string_from_precise_string(args)
|
149
|
+
str = args[0]
|
150
|
+
raise "Bad type!" unless str.is_a? RDL::Type::PreciseStringType
|
151
|
+
# TODO: handles only non-interpolated strings for now
|
152
|
+
base_query = str.vals[0]
|
153
|
+
|
154
|
+
# Get rid of SQL functions here, that just ends up confusing the parser anyway
|
155
|
+
base_query.gsub!('LOWER(', '(')
|
156
|
+
|
157
|
+
counter = 1
|
158
|
+
if args[1].is_a? RDL::Type::FiniteHashType
|
159
|
+
# the query has named params
|
160
|
+
args[1].elts.keys.each { |k| base_query.gsub!(":#{k}", counter.to_s); counter += 1 }
|
161
|
+
else
|
162
|
+
# the query has ? symbols
|
163
|
+
args[1..-1].each { |t| base_query.sub!('?', counter.to_s); counter += 1}
|
164
|
+
end
|
165
|
+
base_query
|
166
|
+
end
|
data/lib/types/rails/string.rb
CHANGED
data/lib/types/sequel.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.dirname(__FILE__) + "/sequel/*.rb"].each { |f| require f }
|
@@ -0,0 +1,581 @@
|
|
1
|
+
class SequelDB
|
2
|
+
extend RDL::Annotate
|
3
|
+
|
4
|
+
type 'self.[]', '(Symbol) -> ``gen_output_type(targs[0])``', wrap: false
|
5
|
+
type '[]', '(Symbol) -> ``gen_output_type(targs[0])``', wrap: false
|
6
|
+
type :transaction, "() { () -> %any } -> self", wrap: false
|
7
|
+
|
8
|
+
|
9
|
+
type RDL::Globals, 'self.seq_db_schema', "()-> Hash<Symbol, RDL::Type::FiniteHashType>", wrap: false, effect: [:+, :+]
|
10
|
+
|
11
|
+
def self.gen_output_type(targ)
|
12
|
+
case targ
|
13
|
+
when RDL::Type::SingletonType
|
14
|
+
t = RDL::Globals.seq_db_schema[RDL.type_cast(targ.val, "Symbol", force: true)]
|
15
|
+
raise "no schema for table #{targ}" if t.nil?
|
16
|
+
new_t = t.elts.clone.merge({__selected: RDL::Globals.types[:nil], __last_joined: targ, __all_joined: targ, __orm: RDL::Globals.types[:false] })
|
17
|
+
new_fht = RDL::Type::FiniteHashType.new(new_t, nil)
|
18
|
+
return RDL::Type::GenericType.new(RDL::Type::NominalType.new(Table), new_fht)
|
19
|
+
else
|
20
|
+
raise "unexpected type"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
RDL.type SequelDB, 'self.gen_output_type', "(RDL::Type::Type) -> RDL::Type::GenericType", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
25
|
+
end
|
26
|
+
|
27
|
+
module Sequel::Mysql2; end
|
28
|
+
|
29
|
+
class Sequel::Mysql2::Database
|
30
|
+
extend RDL::Annotate
|
31
|
+
## This class is identical to SequelDB (above), except its name.
|
32
|
+
## Necessary to support different apps.
|
33
|
+
|
34
|
+
type 'self.[]', '(Symbol) -> ``gen_output_type(targs[0])``', wrap: false
|
35
|
+
type '[]', '(Symbol) -> ``gen_output_type(targs[0])``', wrap: false
|
36
|
+
type :transaction, "() { () -> %any } -> self", wrap: false
|
37
|
+
|
38
|
+
|
39
|
+
type RDL::Globals, 'self.seq_db_schema', "()-> Hash<Symbol, RDL::Type::FiniteHashType>", wrap: false, effect: [:+, :+]
|
40
|
+
|
41
|
+
def self.gen_output_type(targ)
|
42
|
+
case targ
|
43
|
+
when RDL::Type::SingletonType
|
44
|
+
t = RDL::Globals.seq_db_schema[RDL.type_cast(targ.val, "Symbol", force: true)]
|
45
|
+
raise "no schema for table #{targ}" if t.nil?
|
46
|
+
new_t = t.elts.clone.merge({__selected: RDL::Globals.types[:nil], __last_joined: targ, __all_joined: targ, __orm: RDL::Globals.types[:false] })
|
47
|
+
new_fht = RDL::Type::FiniteHashType.new(new_t, nil)
|
48
|
+
return RDL::Type::GenericType.new(RDL::Type::NominalType.new(Table), new_fht)
|
49
|
+
else
|
50
|
+
raise "unexpected type #{targ.class}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
RDL.type Sequel::Mysql2::Database, 'self.gen_output_type', "(RDL::Type::Type) -> RDL::Type::GenericType", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
module Sequel
|
59
|
+
extend RDL::Annotate
|
60
|
+
|
61
|
+
type 'self.sqlite', '() -> DBVal', wrap: false
|
62
|
+
|
63
|
+
type 'self.[]', '(Symbol) -> ``gen_output_type(targs[0])``', wrap: false
|
64
|
+
type 'self.qualify', '(Symbol, Symbol) -> ``qualify_output_type(targs)``', wrap: false
|
65
|
+
|
66
|
+
def self.gen_output_type(targ)
|
67
|
+
case targ
|
68
|
+
when RDL::Type::SingletonType
|
69
|
+
RDL::Type::GenericType.new(RDL::Type::NominalType.new(SeqIdent), targ)
|
70
|
+
else
|
71
|
+
raise "unexpected type"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
RDL.type Sequel, 'self.gen_output_type', "(RDL::Type::Type) -> RDL::Type::GenericType", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
75
|
+
|
76
|
+
def self.qualify_output_type(targs)
|
77
|
+
raise "unexpected types" unless targs.all? { |a| a.is_a?(RDL::Type::SingletonType) }
|
78
|
+
RDL::Type::GenericType.new(RDL::Type::NominalType.new(SeqQualIdent), targs[0], targs[1])
|
79
|
+
end
|
80
|
+
RDL.type Sequel, 'self.qualify_output_type', "(Array<RDL::Type::Type>) -> RDL::Type::GenericType", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
81
|
+
end
|
82
|
+
class SeqIdent
|
83
|
+
extend RDL::Annotate
|
84
|
+
type_params [:t], :all?
|
85
|
+
type :[], '(Symbol) -> ``gen_output_type(trec, targs[0])``', wrap: false
|
86
|
+
|
87
|
+
def self.gen_output_type(trec, targ)
|
88
|
+
case trec
|
89
|
+
when RDL::Type::GenericType
|
90
|
+
param = trec.params[0]
|
91
|
+
case targ
|
92
|
+
when RDL::Type::SingletonType
|
93
|
+
return RDL::Type::GenericType.new(RDL::Type::NominalType.new(SeqQualIdent), param, targ)
|
94
|
+
else
|
95
|
+
raise "expected singleton"
|
96
|
+
end
|
97
|
+
else
|
98
|
+
raise "unexpected trec type"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
RDL.type SeqIdent, 'self.gen_output_type', "(RDL::Type::Type, RDL::Type::Type) -> RDL::Type::GenericType", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
class SeqQualIdent
|
106
|
+
extend RDL::Annotate
|
107
|
+
type_params [:table, :column], :all?
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
class Table
|
112
|
+
extend RDL::Annotate
|
113
|
+
type_params [:t], :all?
|
114
|
+
|
115
|
+
type :join, "(Symbol, %any) -> ``join_ret_type(trec, targs)``", wrap: false
|
116
|
+
type :join, "(Symbol, %any, %any) -> ``join_ret_type(trec, targs)``", wrap: false
|
117
|
+
|
118
|
+
RDL.rdl_alias :Table, :inner_join, :join
|
119
|
+
RDL.rdl_alias :Table, :left_join, :join
|
120
|
+
RDL.rdl_alias :Table, :left_outer_join, :join
|
121
|
+
|
122
|
+
def self.get_schema(hash)
|
123
|
+
hash.select { |key, val| ![:__last_joined, :__all_joined, :__selected, :__orm].member?(key) }
|
124
|
+
end
|
125
|
+
RDL.type Table, 'self.get_schema', "(Hash<%any, RDL::Type::Type>) -> Hash<%any, RDL::Type::Type>", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
126
|
+
|
127
|
+
def self.get_all_joined(t)
|
128
|
+
case t
|
129
|
+
when RDL::Type::SingletonType
|
130
|
+
sing = RDL.type_cast(t, "RDL::Type::SingletonType<Symbol>", force: true)
|
131
|
+
raise "unexpected type #{t} in __all_joined clause" unless sing.val.is_a?(Symbol)
|
132
|
+
return [sing.val]
|
133
|
+
when RDL::Type::UnionType
|
134
|
+
all = t.types.map { |subt|
|
135
|
+
raise "unexpected type #{subt} in union type within __all_joined clause" unless subt.is_a?(RDL::Type::SingletonType) && RDL.type_cast(subt, "RDL::Type::SingletonType<Symbol>", force: true).val.is_a?(Symbol)
|
136
|
+
RDL.type_cast(subt, "RDL::Type::SingletonType<Symbol>", force: true).val
|
137
|
+
}
|
138
|
+
return all
|
139
|
+
when nil
|
140
|
+
return RDL.type_cast([], "Array<Symbol>", force: true)
|
141
|
+
else
|
142
|
+
raise "unexpected type #{t} in __all_joined clause"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
RDL.type Table, 'self.get_all_joined', "(RDL::Type::Type) -> Array<Symbol>", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
146
|
+
|
147
|
+
def self.join_ret_type(trec, targs)
|
148
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected number of arguments to `join`." unless targs.size == 2
|
149
|
+
targ1, targ2 = *targs
|
150
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected second argument type #{targ2} to `join`." unless targ2.is_a?(RDL::Type::FiniteHashType)
|
151
|
+
arg_join_column = RDL.type_cast(RDL.type_cast(targ2, "RDL::Type::FiniteHashType").elts.keys[0], "Symbol", force: true) ## column name of arg table which is joined on
|
152
|
+
rec_join_column = RDL.type_cast(RDL.type_cast(targ2, "RDL::Type::FiniteHashType").elts[arg_join_column], "Symbol", force: true) ## column name of receiver table which is joined on
|
153
|
+
case trec
|
154
|
+
when RDL::Type::GenericType
|
155
|
+
raise RDL::Typecheck::StaticTypeError, "unexpceted generic type in call to join" unless trec.base.name == "Table"
|
156
|
+
receiver_param = RDL.type_cast(trec.params[0], "RDL::Type::FiniteHashType", force: true).elts
|
157
|
+
receiver_schema = get_schema(receiver_param)
|
158
|
+
join_source_schema = get_schema(RDL::Globals.seq_db_schema[RDL.type_cast(receiver_param[:__last_joined], "RDL::Type::SingletonType<Symbol>", force: true).val].elts)
|
159
|
+
rec_all_joined = get_all_joined(receiver_param[:__all_joined])
|
160
|
+
|
161
|
+
case rec_join_column
|
162
|
+
when RDL::Type::SingletonType
|
163
|
+
## given symbol for second column to join on
|
164
|
+
if rec_join_column.to_s.include?("__")
|
165
|
+
## qualified column name in old versions of sequel.
|
166
|
+
check_qual_column(rec_join_column, rec_all_joined)
|
167
|
+
else
|
168
|
+
raise RDL::Typecheck::StaticTypeError, "No column #{rec_join_column} for receiver in call to `join`." if join_source_schema[rec_join_column.val].nil?
|
169
|
+
end
|
170
|
+
when RDL::Type::GenericType
|
171
|
+
## given qualified column, e.g. Sequel[:people][:name]
|
172
|
+
raise RDL::Typecheck::StaticTypeError, "unexpected generic type #{rec_join_column}" unless rec_join_column.base.name == "SeqQualIdent"
|
173
|
+
qual_table, qual_column = rec_join_column.params.map { |t| t.val }
|
174
|
+
raise RDL::Typecheck::StaticTypeError, "qualified table #{qual_table} is not joined in receiver table, and so its columns cannot be joined on" unless rec_all_joined.include?(qual_table)
|
175
|
+
qual_table_schema = get_schema(RDL::Globals.seq_db_schema[qual_table].elts)
|
176
|
+
raise RDL::Typecheck::StaticTypeError, "No column #{qual_column} in table #{qual_table}." if qual_table_schema[qual_column].nil?
|
177
|
+
else
|
178
|
+
raise "Unexpected column #{rec_join_column} to join on"
|
179
|
+
end
|
180
|
+
case targ1
|
181
|
+
when RDL::Type::SingletonType
|
182
|
+
val = RDL.type_cast(targ1.val, "Symbol", force: true)
|
183
|
+
raise RDL::Typecheck::StaticTypeError, "Expected Symbol for first argument to `join`." unless val.is_a?(Symbol)
|
184
|
+
table_name = val
|
185
|
+
table_schema = RDL::Globals.seq_db_schema[table_name]
|
186
|
+
raise "No schema found for table #{table_name}." unless table_schema
|
187
|
+
arg_schema = get_schema(table_schema.elts) ## look up table schema for argument
|
188
|
+
raise RDL::Typecheck::StaticTypeError, "No column #{arg_join_column} for arg in call to `join`." if arg_schema[arg_join_column].nil?
|
189
|
+
result_schema = receiver_schema.merge(arg_schema).merge({ __all_joined: RDL::Type::UnionType.new(*[receiver_param[:__all_joined], targ1]), __last_joined: targ1, __selected: receiver_param[:__selected], __orm: receiver_param[:__orm] }) ## resulting schema as hash
|
190
|
+
result_fht = RDL::Type::FiniteHashType.new(result_schema, nil) ## resulting schema as FiniteHashType
|
191
|
+
|
192
|
+
return RDL::Type::GenericType.new(trec.base, result_fht)
|
193
|
+
when RDL::Type::NominalType
|
194
|
+
## TODO: this will catch case that first argument is a non-singleton Symbol
|
195
|
+
raise "not implemented, likely not needed in practice"
|
196
|
+
else
|
197
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected type of first argument to `join`."
|
198
|
+
end
|
199
|
+
when RDL::Type::NominalType
|
200
|
+
raise RDL::Typecheck::StaticTypeError unless trec.name == "Table"
|
201
|
+
else
|
202
|
+
raise RDL::Typecheck::StaticTypeError, "Unexpected receiver type in call to `join`."
|
203
|
+
end
|
204
|
+
end
|
205
|
+
RDL.type Table, 'self.join_ret_type', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::Type", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
206
|
+
|
207
|
+
|
208
|
+
type :insert, "(``insert_arg_type(trec, targs)``) -> Integer", wrap: false
|
209
|
+
type :insert, "(``insert_arg_type(trec, targs, true)``, %any) -> Integer", wrap: false
|
210
|
+
type :where, "(``where_arg_type(trec, targs)``) -> self", wrap: false
|
211
|
+
type :where, "(``where_arg_type(trec, targs, true)``, %any) -> self", wrap: false
|
212
|
+
type :exclude, "(``where_arg_type(trec, targs)``) -> self", wrap: false
|
213
|
+
type :exclude, "(``where_arg_type(trec, targs, true)``, %any) -> self", wrap: false
|
214
|
+
type :[], "(``where_arg_type(trec, targs)``) -> ``first_output(trec)``", wrap: false
|
215
|
+
type :first, "() -> ``first_output(trec)``", wrap: false
|
216
|
+
type :first, "(``if targs[0] then where_arg_type(trec, targs) else RDL::Globals.types[:bot] end``) -> ``first_output(trec)``", wrap: false
|
217
|
+
type :get, '(``get_input(trec)``) -> ``get_output(trec, targs)``', wrap: false
|
218
|
+
type :order, '(``order_input(trec, targs)``) -> self', wrap: false
|
219
|
+
type Sequel, 'self.desc', '(%any) -> ``targs[0]``', wrap: false ## args will ultimately be checked by `order`
|
220
|
+
type :select_map, '(Symbol) -> ``select_map_output(trec, targs, :select_map)``', wrap: false
|
221
|
+
type :pluck, '(Symbol) -> ``select_map_output(trec, targs, :select_map)``', wrap: false
|
222
|
+
type :any?, "() -> %bool", wrap: false
|
223
|
+
type :select, "(*%any) -> ``select_map_output(trec, targs, :select)``", wrap: false
|
224
|
+
type :all, "() -> ``all_output(trec)``", wrap: false
|
225
|
+
type Sequel, 'self.lit', "(%any) -> String", wrap: false
|
226
|
+
type :server, "(Symbol) -> self", wrap: false
|
227
|
+
type :empty?, '() -> %bool', wrap: false
|
228
|
+
type :update, "(``insert_arg_type(trec, targs)``) -> Integer", wrap: false
|
229
|
+
type :count, "() -> Integer", wrap: false
|
230
|
+
type :map, "() { (``map_block_input(trec)``) -> x } -> Array<x>", wrap: false
|
231
|
+
type :each, "() { (``map_block_input(trec)``) -> x } -> self", wrap: false
|
232
|
+
type :import, "(``import_arg_type(trec, targs)``, Array<u>) -> Array<String>", wrap: false
|
233
|
+
|
234
|
+
def self.order_input(trec, targs)
|
235
|
+
case trec
|
236
|
+
when RDL::Type::GenericType
|
237
|
+
trp0 = RDL.type_cast(trec.params[0], "RDL::Type::FiniteHashType", force: true)
|
238
|
+
sym_keys = get_schema(trp0.elts).keys
|
239
|
+
all_joined = get_all_joined(trp0.elts[:__all_joined])
|
240
|
+
targs.each { |a|
|
241
|
+
case a
|
242
|
+
when RDL::Type::SingletonType
|
243
|
+
return RDL::Globals.types[:bot] unless sym_keys.include?(a.val)
|
244
|
+
when RDL::Type::GenericType
|
245
|
+
return RDL::Globals.types[:bot] unless a.base.name == "SeqQualIdent"
|
246
|
+
check_qual_column(a, all_joined)
|
247
|
+
end
|
248
|
+
}
|
249
|
+
return RDL::Type::VarargType.new(RDL::Type::UnionType.new(*targs))
|
250
|
+
else
|
251
|
+
raise "unexpected type #{trec}"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
RDL.type Table, 'self.order_input', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::Type", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
255
|
+
|
256
|
+
def self.map_block_input(trec)
|
257
|
+
schema = get_schema(RDL.type_cast(trec.params[0], "RDL::Type::FiniteHashType", force: true).elts)
|
258
|
+
RDL::Type::FiniteHashType.new(schema, nil)
|
259
|
+
end
|
260
|
+
RDL.type Table, 'self.map_block_input', "(RDL::Type::GenericType) -> RDL::Type::FiniteHashType", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
261
|
+
|
262
|
+
def self.all_output(trec)
|
263
|
+
f = first_output(trec)
|
264
|
+
if f.is_a?(RDL::Type::FiniteHashType)
|
265
|
+
trp0 = RDL.type_cast(RDL.type_cast(trec, "RDL::Type::GenericType").params[0], "RDL::Type::FiniteHashType", force: true)
|
266
|
+
selected = trp0.elts[:__selected]
|
267
|
+
all_joined = get_all_joined(trp0.elts[:__all_joined])
|
268
|
+
if !(selected == RDL::Globals.types[:nil])
|
269
|
+
## something is selected
|
270
|
+
sel_arr = RDL.type_cast(selected.is_a?(RDL::Type::UnionType) ? RDL.type_cast(selected, "RDL::Type::UnionType").types : [selected], "Array<RDL::Type::SingletonType<Symbol>>", force: true)
|
271
|
+
new_hash = Hash[sel_arr.map { |sel|
|
272
|
+
if sel.val.to_s.include?("__")
|
273
|
+
t = check_qual_column(sel.val, all_joined)
|
274
|
+
_, col_name = sel.val.to_s.split "__"
|
275
|
+
col_name = col_name.to_sym
|
276
|
+
[col_name, t]
|
277
|
+
else
|
278
|
+
raise "no selected column found" unless (t = RDL.type_cast(f, "RDL::Type::FiniteHashType", force: true).elts[sel.val])
|
279
|
+
[sel.val, t]
|
280
|
+
end
|
281
|
+
}]
|
282
|
+
new_hash_type = RDL::Type::FiniteHashType.new(RDL.type_cast(new_hash, "Hash<%any, RDL::Type::Type>", force: true), nil)
|
283
|
+
return RDL::Type::GenericType.new(RDL::Globals.types[:array], new_hash_type)
|
284
|
+
else
|
285
|
+
return RDL::Type::GenericType.new(RDL::Globals.types[:array], f)
|
286
|
+
end
|
287
|
+
else
|
288
|
+
return RDL::Type::GenericType.new(RDL::Globals.types[:array], f)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
RDL.type Table, 'self.all_output', "(RDL::Type::Type) -> RDL::Type::GenericType", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
292
|
+
|
293
|
+
def self.select_map_output(trec, targs, meth)
|
294
|
+
case trec
|
295
|
+
when RDL::Type::GenericType
|
296
|
+
raise RDL::Typecheck::StaticTypeError, 'unexpected type' unless trec.base.name == "Table"
|
297
|
+
receiver_param = RDL.type_cast(trec.params[0], "RDL::Type::FiniteHashType", force: true).elts
|
298
|
+
all_joined = get_all_joined(receiver_param[:__all_joined])
|
299
|
+
|
300
|
+
map_types = targs.map { |arg|
|
301
|
+
case arg
|
302
|
+
when RDL::Type::SingletonType
|
303
|
+
column = RDL.type_cast(arg.val, "Symbol", force: true)
|
304
|
+
raise "unexpected arg type #{arg}" unless column.is_a?(Symbol)
|
305
|
+
raise "Ambiguous column identifier #{arg}." unless unique_ids?([column], receiver_param[:__all_joined])
|
306
|
+
if column.to_s.include?("__")
|
307
|
+
check_qual_column(column, all_joined)
|
308
|
+
else
|
309
|
+
raise "No column #{column} in receiver table." unless receiver_param[column]
|
310
|
+
receiver_param[column]
|
311
|
+
end
|
312
|
+
when RDL::Type::GenericType
|
313
|
+
raise "unexpected arg type #{arg}" unless arg.base.name == "SeqQualIdent"
|
314
|
+
check_qual_column(arg, all_joined)
|
315
|
+
else
|
316
|
+
raise "unexpected arg type #{arg}"
|
317
|
+
end
|
318
|
+
}
|
319
|
+
|
320
|
+
targs.each { |arg|
|
321
|
+
case arg
|
322
|
+
when RDL::Type::SingletonType
|
323
|
+
column = RDL.type_cast(arg.val, "Symbol", force: true)
|
324
|
+
raise "unexpected arg type #{arg}" unless column.is_a?(Symbol)
|
325
|
+
raise "Ambiguous column identifier #{arg}." unless unique_ids?([column], receiver_param[:__all_joined])
|
326
|
+
if column.to_s.include?("__")
|
327
|
+
map_types = map_types + [check_qual_column(column, all_joined)]
|
328
|
+
else
|
329
|
+
raise "No column #{column} in receiver table." unless receiver_param[column]
|
330
|
+
map_types = map_types + [receiver_param[column]]
|
331
|
+
end
|
332
|
+
when RDL::Type::GenericType
|
333
|
+
raise "unexpected arg type #{arg}" unless arg.base.name == "SeqQualIdent"
|
334
|
+
map_types = map_types + [check_qual_column(arg, all_joined)]
|
335
|
+
else
|
336
|
+
raise "unexpected arg type #{arg}"
|
337
|
+
end
|
338
|
+
}
|
339
|
+
if meth == :select
|
340
|
+
result_schema = receiver_param.clone.merge({ __selected: RDL::Type::UnionType.new(*targs).canonical })
|
341
|
+
return RDL::Type::GenericType.new(trec.base, RDL::Type::FiniteHashType.new(result_schema, nil))
|
342
|
+
elsif meth == :select_map
|
343
|
+
if targs.size >1
|
344
|
+
return RDL::Type::GenericType.new(RDL::Globals.types[:array], RDL::Type::TupleType.new(*map_types))
|
345
|
+
else
|
346
|
+
return RDL::Type::GenericType.new(RDL::Globals.types[:array], map_types[0])
|
347
|
+
end
|
348
|
+
else
|
349
|
+
raise 'unexpected'
|
350
|
+
end
|
351
|
+
else
|
352
|
+
raise 'unexpected type #{trec}'
|
353
|
+
end
|
354
|
+
end
|
355
|
+
RDL.type Table, 'self.select_map_output', "(RDL::Type::Type, Array<RDL::Type::Type>, Symbol) -> RDL::Type::GenericType", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
356
|
+
|
357
|
+
def self.get_input(trec)
|
358
|
+
case trec
|
359
|
+
when RDL::Type::GenericType
|
360
|
+
sym_keys = get_schema(RDL.type_cast(trec.params[0], "RDL::Type::FiniteHashType", force: true).elts).keys
|
361
|
+
RDL::Type::UnionType.new(*RDL.type_cast(sym_keys.map { |k| RDL::Type::SingletonType.new(k) }, "Array<RDL::Type::Type>"))
|
362
|
+
else
|
363
|
+
raise 'unexpected type #{trec}'
|
364
|
+
end
|
365
|
+
end
|
366
|
+
RDL.type Table, 'self.get_input', "(RDL::Type::Type) -> RDL::Type::UnionType", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
367
|
+
|
368
|
+
def self.get_output(trec, targs)
|
369
|
+
RDL.type_cast(RDL.type_cast(trec, "RDL::Type::GenericType").params[0], "RDL::Type::FiniteHashType", force: true).elts[RDL.type_cast(targs[0], "RDL::Type::SingletonType<Symbol>", force: true).val]
|
370
|
+
end
|
371
|
+
RDL.type Table, 'self.get_output', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::Type", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
372
|
+
|
373
|
+
def self.first_output(trec)
|
374
|
+
case trec
|
375
|
+
when RDL::Type::GenericType
|
376
|
+
raise RDL::Typecheck::StaticTypeError, 'unexpected type' unless trec.base.name == "Table"
|
377
|
+
receiver_param = RDL.type_cast(trec.params[0], "RDL::Type::FiniteHashType", force: true).elts
|
378
|
+
if !(receiver_param[:__orm] == RDL::Globals.types[:false])
|
379
|
+
receiver_param[:__orm]
|
380
|
+
else
|
381
|
+
RDL::Type::FiniteHashType.new(get_schema(receiver_param), nil)
|
382
|
+
end
|
383
|
+
else
|
384
|
+
raise 'unexpected type #{trec}'
|
385
|
+
end
|
386
|
+
end
|
387
|
+
RDL.type Table, 'self.first_output', "(RDL::Type::Type) -> RDL::Type::Type", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
388
|
+
|
389
|
+
def self.insert_arg_type(trec, targs, tuple=false)
|
390
|
+
raise "Cannot insert/update for joined table." if RDL.type_cast(RDL.type_cast(trec, "RDL::Type::GenericType").params[0], "RDL::Type::FiniteHashType", force: true).elts[:__all_joined].is_a?(RDL::Type::UnionType)
|
391
|
+
if tuple
|
392
|
+
schema_arg_tuple_type(trec, targs, :insert)
|
393
|
+
else
|
394
|
+
schema_arg_type(trec, targs, :insert)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
RDL.type Table, 'self.insert_arg_type', "(RDL::Type::Type, Array<RDL::Type::Type>, ?%bool) -> RDL::Type::Type", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
398
|
+
|
399
|
+
def self.import_arg_type(trec, targs)
|
400
|
+
raise "Cannot import for joined table." if RDL.type_cast(RDL.type_cast(trec, "RDL::Type::GenericType").params[0], "RDL::Type::FiniteHashType", force: true).elts[:__all_joined].is_a?(RDL::Type::UnionType)
|
401
|
+
raise "Expected tuple for first arg to `import`, got #{targs[0]} instead." unless targs[0].is_a?(RDL::Type::TupleType)
|
402
|
+
arg1 = targs[1]
|
403
|
+
case arg1
|
404
|
+
when RDL::Type::TupleType
|
405
|
+
arg1.params.each { |t| schema_arg_tuple_type(trec, [targs[0], t], :import) } ## check each individual tuple inside second arg tuple
|
406
|
+
when RDL::Type::GenericType
|
407
|
+
raise "expected Array, got #{arg1}" unless (arg1.base == RDL::Globals.types[:array])
|
408
|
+
raise "`import` type not yet implemented for type #{arg1}" unless arg1.params[0].is_a?(RDL::Type::TupleType)
|
409
|
+
schema_arg_tuple_type(trec, [targs[0], arg1.params[0]], :import)
|
410
|
+
else
|
411
|
+
raise "Not yet implemented for type #{arg1}."
|
412
|
+
end
|
413
|
+
return targs[0]
|
414
|
+
end
|
415
|
+
RDL.type Table, 'self.import_arg_type', "(RDL::Type::Type, Array<RDL::Type::Type>) -> RDL::Type::Type", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
416
|
+
|
417
|
+
def self.get_nominal_where_type(type)
|
418
|
+
## `where` can accept arrays/tuples and tables with a single column selected
|
419
|
+
## this method just extracts the parameter type
|
420
|
+
case type
|
421
|
+
when RDL::Type::GenericType
|
422
|
+
if type.base == RDL::Globals.types[:array]
|
423
|
+
type.params[0]
|
424
|
+
elsif type.base == RDL::Type::NominalType.new(Table)
|
425
|
+
schema = RDL.type_cast(type.params[0], "RDL::Type::FiniteHashType", force: true).elts
|
426
|
+
sel = schema[:__selected]
|
427
|
+
raise "Call to where expects table with a single column selected, got #{type}" unless sel.is_a?(RDL::Type::SingletonType)
|
428
|
+
nominal = schema[RDL.type_cast(sel, "RDL::Type::SingletonType", force: true).val]
|
429
|
+
raise "No type found for column #{sel} in call to `where`." unless nominal
|
430
|
+
nominal
|
431
|
+
else
|
432
|
+
type
|
433
|
+
end
|
434
|
+
when RDL::Type::TupleType
|
435
|
+
type = type.promote.params[0]
|
436
|
+
raise "`where` passed tuple containing different types." if type.is_a?(RDL::Type::UnionType)
|
437
|
+
type
|
438
|
+
else
|
439
|
+
type
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
RDL.type Table, 'self.get_nominal_where_type', "(RDL::Type::Type) -> RDL::Type::Type", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
444
|
+
|
445
|
+
def self.schema_arg_type(trec, targs, meth)
|
446
|
+
return RDL::Type::NominalType.new(Hash) if targs.size != 1
|
447
|
+
case trec
|
448
|
+
when RDL::Type::GenericType
|
449
|
+
raise RDL::Typecheck::StaticTypeError, 'unexpected type' unless trec.base.name == "Table"
|
450
|
+
receiver_param = RDL.type_cast(trec.params[0], "RDL::Type::FiniteHashType", force: true).elts
|
451
|
+
receiver_schema = get_schema(receiver_param)
|
452
|
+
all_joined = get_all_joined(receiver_param[:__all_joined])
|
453
|
+
arg0 = targs[0]
|
454
|
+
case arg0
|
455
|
+
when RDL::Type::FiniteHashType
|
456
|
+
insert_hash = arg0.elts
|
457
|
+
insert_hash.each { |column_name, type|
|
458
|
+
cn = RDL.type_cast(column_name, "Symbol", force: true)
|
459
|
+
if cn.to_s.include?("__") && (meth == :where)
|
460
|
+
check_qual_column(cn, all_joined, type)
|
461
|
+
else
|
462
|
+
raise RDL::Typecheck::StaticTypeError, "No column #{column_name} for receiver #{trec}." unless receiver_schema.has_key?(cn)
|
463
|
+
type = get_nominal_where_type(type) if (meth == :where)
|
464
|
+
raise RDL::Typecheck::StaticTypeError, "Incompatible column types #{type} and #{receiver_schema[column_name]} for column #{cn} in call to #{meth}." unless RDL::Type::Type.leq(type, receiver_schema[cn])
|
465
|
+
end
|
466
|
+
}
|
467
|
+
return arg0
|
468
|
+
else
|
469
|
+
return arg0 if (meth==:where) && targs[0] <= RDL::Globals.types[:string]
|
470
|
+
raise "TODO WITH #{trec} AND #{targs} AND #{meth}"
|
471
|
+
end
|
472
|
+
when RDL::Type::NominalType
|
473
|
+
##TODO
|
474
|
+
else
|
475
|
+
|
476
|
+
end
|
477
|
+
end
|
478
|
+
RDL.type Table, 'self.schema_arg_type', "(RDL::Type::Type, Array<RDL::Type::Type>, Symbol) -> RDL::Type::Type", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
479
|
+
|
480
|
+
## [+ column_name +] if a symbol or SeqQualIdent Generic type of the qualified column name, e.g. :person__age
|
481
|
+
## [+ all_joined +] is an array of symbols of joined tables (must check if qualifying table is a member)
|
482
|
+
## [+ type +] is optional RDL type. If given, we check that it matches the type of the column in the schema.
|
483
|
+
## returns type of given column
|
484
|
+
def self.check_qual_column(column_name, all_joined, type=nil)
|
485
|
+
case column_name
|
486
|
+
when RDL::Type::GenericType
|
487
|
+
cn = RDL.type_cast(column_name, "RDL::Type::GenericType", force: true)
|
488
|
+
raise "Expected qualified column type." unless cn.base.name == "SeqQualIdent"
|
489
|
+
qual_table, qual_column = cn.params.map { |t| RDL.type_cast(t, "RDL::Type::SingletonType<Symbol>", force: true).val }
|
490
|
+
else
|
491
|
+
## symbol with name including underscores
|
492
|
+
qual_table, qual_column = RDL.type_cast(column_name, "Symbol", force: true).to_s.split "__"
|
493
|
+
qual_table = if qual_table.start_with?(":") then qual_table[1..-1].to_sym else qual_table.to_sym end
|
494
|
+
qual_column = qual_column.to_sym
|
495
|
+
end
|
496
|
+
raise RDL::Typecheck::StaticTypeError, "qualified table #{qual_table} is not joined in receiver table, cannot reference its columns" unless all_joined.include?(qual_table)
|
497
|
+
qual_table_schema = get_schema(RDL::Globals.seq_db_schema[qual_table].elts)
|
498
|
+
raise RDL::Typecheck::StaticTypeError, "No column #{qual_column} in table #{qual_table}." if qual_table_schema[qual_column].nil?
|
499
|
+
if type
|
500
|
+
types = (if type.is_a?(RDL::Type::UnionType) then RDL.type_cast(type, "RDL::Type::UnionType", force: true).types else [type] end)
|
501
|
+
types.each { |t|
|
502
|
+
t = RDL.type_cast(t, "RDL::Type::GenericType", force: true).params[0] if t.is_a?(RDL::Type::GenericType) && (RDL.type_cast(t, "RDL::Type::GenericType", force: true).base == RDL::Globals.types[:array]) ## only happens if meth is where, don't need to check
|
503
|
+
raise RDL::Typecheck::StaticTypeError, "Incompatible column types. Given #{t} but expected #{qual_table_schema[qual_column]} for column #{column_name}." unless RDL::Type::Type.leq(t, qual_table_schema[qual_column])
|
504
|
+
}
|
505
|
+
end
|
506
|
+
return qual_table_schema[qual_column]
|
507
|
+
end
|
508
|
+
RDL.type Table, 'self.check_qual_column', "(Symbol or RDL::Type::GenericType, Array<Symbol>, ?RDL::Type::Type) -> RDL::Type::Type", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
509
|
+
|
510
|
+
def self.schema_arg_tuple_type(trec, targs, meth)
|
511
|
+
return RDL::Type::NominalType.new(Array) if targs.size != 2
|
512
|
+
case trec
|
513
|
+
when RDL::Type::GenericType
|
514
|
+
raise RDL::Typecheck::StaticTypeError, 'unexpected type' unless trec.base.name == "Table"
|
515
|
+
receiver_param = RDL.type_cast(trec.params[0], "RDL::Type::FiniteHashType", force: true).elts
|
516
|
+
all_joined = get_all_joined(receiver_param[:__all_joined])
|
517
|
+
receiver_schema = get_schema(receiver_param)
|
518
|
+
if targs[0].is_a?(RDL::Type::TupleType) && targs[1].is_a?(RDL::Type::TupleType)
|
519
|
+
RDL.type_cast(targs[0], "RDL::Type::TupleType", force: true).params.each_with_index { |column_name, i|
|
520
|
+
cn = RDL.type_cast(column_name, "RDL::Type::SingletonType<Symbol>", force: true)
|
521
|
+
raise "Expected singleton symbol in call to insert, got #{column_name}" unless cn.is_a?(RDL::Type::SingletonType) && cn.val.is_a?(Symbol)
|
522
|
+
type = RDL.type_cast(targs[1], "RDL::Type::TupleType", force: true).params[i]
|
523
|
+
if cn.val.to_s.include?("__") && (meth == :where)
|
524
|
+
check_qual_column(cn.val, all_joined, type)
|
525
|
+
else
|
526
|
+
raise RDL::Typecheck::StaticTypeError, "No column #{column_name} for receiver in call to `insert`." unless receiver_schema.has_key?(cn.val)
|
527
|
+
type = get_nominal_where_type(type) if (meth == :where)
|
528
|
+
raise RDL::Typecheck::StaticTypeError, "Incompatible column types." unless RDL::Type::Type.leq(type, receiver_schema[cn.val])
|
529
|
+
end
|
530
|
+
}
|
531
|
+
return targs[0]
|
532
|
+
else
|
533
|
+
raise "not yet implemented for types #{targs[0]} and #{targs[1]}"
|
534
|
+
end
|
535
|
+
else
|
536
|
+
raise 'not yet implemented'
|
537
|
+
end
|
538
|
+
end
|
539
|
+
RDL.type Table, 'self.schema_arg_tuple_type', "(RDL::Type::Type, Array<RDL::Type::Type>, Symbol) -> RDL::Type::Type", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
540
|
+
|
541
|
+
def self.where_arg_type(trec, targs, tuple=false)
|
542
|
+
trp0 = RDL.type_cast(RDL.type_cast(trec, "RDL::Type::GenericType").params[0], "RDL::Type::FiniteHashType", force: true)
|
543
|
+
if trp0.elts[:__all_joined].is_a?(RDL::Type::UnionType)
|
544
|
+
arg0 = targs[0]
|
545
|
+
case arg0
|
546
|
+
when RDL::Type::TupleType
|
547
|
+
raise "Unexpected column type." unless arg0.params.all? { |t| t.is_a?(RDL::Type::SingletonType) && RDL.type_cast(t, "RDL::Type::SingletonType<Object>", force: true).val.is_a?(Symbol) }
|
548
|
+
raise "Ambigious identifier in call to where." unless unique_ids?(arg0.params.map { |t| RDL.type_cast(t, "RDL::Type::SingletonType<Symbol>", force: true).val }, trp0.elts[:__all_joined])
|
549
|
+
when RDL::Type::FiniteHashType
|
550
|
+
raise "Ambigious identifier in call to where." unless unique_ids?(RDL.type_cast(arg0.elts.keys, "Array<Symbol>", force: true), trp0.elts[:__all_joined])
|
551
|
+
else
|
552
|
+
raise "unexpected arg type #{arg0}"
|
553
|
+
end
|
554
|
+
end
|
555
|
+
if tuple
|
556
|
+
schema_arg_tuple_type(trec, targs, :where)
|
557
|
+
else
|
558
|
+
schema_arg_type(trec, targs, :where)
|
559
|
+
end
|
560
|
+
end
|
561
|
+
RDL.type Table, 'self.where_arg_type', "(RDL::Type::Type, Array<RDL::Type::Type>, ?%bool) -> RDL::Type::Type", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
562
|
+
|
563
|
+
def self.unique_ids?(ids, joined)
|
564
|
+
j = get_all_joined(joined)
|
565
|
+
count = Hash[ids.map { |id| [id, 0] }]
|
566
|
+
|
567
|
+
j.each { |t1|
|
568
|
+
schema1 = RDL::Globals.seq_db_schema[t1]
|
569
|
+
raise "schema not found" unless schema1
|
570
|
+
j.each { |t2|
|
571
|
+
schema2 = RDL::Globals.seq_db_schema[t2]
|
572
|
+
ids.each { |id|
|
573
|
+
return false if schema1.elts.has_key?(id) && schema2.elts.has_key?(id) && (t1 != t2)
|
574
|
+
}
|
575
|
+
}
|
576
|
+
}
|
577
|
+
return true
|
578
|
+
end
|
579
|
+
RDL.type Table, 'self.unique_ids?', "(Array<Symbol>, RDL::Type::Type) -> %bool", typecheck: :type_code, wrap: false, effect: [:+, :+]
|
580
|
+
|
581
|
+
end
|