plume 0.1.0 → 0.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 +4 -4
- data/README.md +280 -10
- data/lib/plume/analysis/resolver.rb +574 -0
- data/lib/plume/analysis.rb +135 -0
- data/lib/plume/ast/alter_table_statement.rb +29 -0
- data/lib/plume/ast/analyze_statement.rb +15 -0
- data/lib/plume/ast/assignment.rb +18 -0
- data/lib/plume/ast/attach_statement.rb +17 -0
- data/lib/plume/ast/begin_statement.rb +17 -0
- data/lib/plume/ast/binary_expression.rb +71 -0
- data/lib/plume/ast/case_condition.rb +29 -0
- data/lib/plume/ast/case_expression.rb +22 -0
- data/lib/plume/ast/cast_expression.rb +16 -0
- data/lib/plume/ast/check_column_constraint.rb +20 -0
- data/lib/plume/ast/check_table_constraint.rb +19 -0
- data/lib/plume/ast/collate_column_constraint.rb +19 -0
- data/lib/plume/ast/collation_expression.rb +26 -0
- data/lib/plume/ast/column_constraint.rb +7 -0
- data/lib/plume/ast/column_definition.rb +16 -0
- data/lib/plume/ast/column_name.rb +22 -0
- data/lib/plume/ast/column_type.rb +25 -0
- data/lib/plume/ast/commit_statement.rb +13 -0
- data/lib/plume/ast/common_table_expression.rb +31 -0
- data/lib/plume/ast/compound_select.rb +30 -0
- data/lib/plume/ast/conflict_clause.rb +22 -0
- data/lib/plume/ast/conflict_resolution.rb +20 -0
- data/lib/plume/ast/create_index_statement.rb +29 -0
- data/lib/plume/ast/create_table_statement.rb +47 -0
- data/lib/plume/ast/create_trigger_statement.rb +43 -0
- data/lib/plume/ast/create_view_statement.rb +30 -0
- data/lib/plume/ast/create_virtual_table_statement.rb +28 -0
- data/lib/plume/ast/default_column_constraint.rb +29 -0
- data/lib/plume/ast/delete_statement.rb +28 -0
- data/lib/plume/ast/detach_statement.rb +13 -0
- data/lib/plume/ast/drop_statement.rb +21 -0
- data/lib/plume/ast/empty_function_argument.rb +6 -0
- data/lib/plume/ast/exists_expression.rb +15 -0
- data/lib/plume/ast/explain_statement.rb +15 -0
- data/lib/plume/ast/expression.rb +30 -0
- data/lib/plume/ast/foreign_key_action.rb +26 -0
- data/lib/plume/ast/foreign_key_clause.rb +50 -0
- data/lib/plume/ast/foreign_key_column_constraint.rb +16 -0
- data/lib/plume/ast/foreign_key_table_constraint.rb +20 -0
- data/lib/plume/ast/frame_boundary.rb +35 -0
- data/lib/plume/ast/frame_spec.rb +34 -0
- data/lib/plume/ast/function_arguments.rb +17 -0
- data/lib/plume/ast/function_reference.rb +29 -0
- data/lib/plume/ast/generated_as_column_constraint.rb +24 -0
- data/lib/plume/ast/indexed_column.rb +21 -0
- data/lib/plume/ast/insert_statement.rb +35 -0
- data/lib/plume/ast/join.rb +12 -0
- data/lib/plume/ast/join_clause.rb +15 -0
- data/lib/plume/ast/join_on_constraint.rb +12 -0
- data/lib/plume/ast/join_operator.rb +31 -0
- data/lib/plume/ast/join_using_constraint.rb +16 -0
- data/lib/plume/ast/like_expression.rb +19 -0
- data/lib/plume/ast/literal_expression.rb +29 -0
- data/lib/plume/ast/literal_false.rb +7 -0
- data/lib/plume/ast/literal_nil.rb +7 -0
- data/lib/plume/ast/literal_value.rb +18 -0
- data/lib/plume/ast/match_clause.rb +15 -0
- data/lib/plume/ast/node.rb +207 -0
- data/lib/plume/ast/noop_column_constraint.rb +15 -0
- data/lib/plume/ast/noop_table_constraint.rb +15 -0
- data/lib/plume/ast/not_like_expression.rb +7 -0
- data/lib/plume/ast/not_null_column_constraint.rb +17 -0
- data/lib/plume/ast/null_column_constraint.rb +17 -0
- data/lib/plume/ast/ordering_term.rb +32 -0
- data/lib/plume/ast/over_clause.rb +25 -0
- data/lib/plume/ast/parenthetical_expression.rb +37 -0
- data/lib/plume/ast/parenthetical_source.rb +19 -0
- data/lib/plume/ast/pragma_statement.rb +26 -0
- data/lib/plume/ast/primary_key_column_constraint.rb +23 -0
- data/lib/plume/ast/primary_key_table_constraint.rb +23 -0
- data/lib/plume/ast/raise_expression.rb +23 -0
- data/lib/plume/ast/reindex_statement.rb +15 -0
- data/lib/plume/ast/release_statement.rb +16 -0
- data/lib/plume/ast/result_column.rb +18 -0
- data/lib/plume/ast/returning_clause.rb +12 -0
- data/lib/plume/ast/rollback_statement.rb +19 -0
- data/lib/plume/ast/savepoint_statement.rb +16 -0
- data/lib/plume/ast/select_core.rb +27 -0
- data/lib/plume/ast/select_statement.rb +34 -0
- data/lib/plume/ast/star_function_argument.rb +6 -0
- data/lib/plume/ast/star_result_column.rb +16 -0
- data/lib/plume/ast/strict_table_option.rb +6 -0
- data/lib/plume/ast/subquery_expression.rb +14 -0
- data/lib/plume/ast/subquery_source.rb +19 -0
- data/lib/plume/ast/table_constraint.rb +7 -0
- data/lib/plume/ast/table_function.rb +28 -0
- data/lib/plume/ast/table_name.rb +28 -0
- data/lib/plume/ast/table_option.rb +7 -0
- data/lib/plume/ast/ternary_expression.rb +26 -0
- data/lib/plume/ast/token.rb +180 -0
- data/lib/plume/ast/unary_expression.rb +36 -0
- data/lib/plume/ast/unique_column_constraint.rb +17 -0
- data/lib/plume/ast/unique_table_constraint.rb +20 -0
- data/lib/plume/ast/update_statement.rb +35 -0
- data/lib/plume/ast/upsert_clause.rb +26 -0
- data/lib/plume/ast/vacuum_statement.rb +18 -0
- data/lib/plume/ast/values_clause.rb +12 -0
- data/lib/plume/ast/values_row.rb +13 -0
- data/lib/plume/ast/variable.rb +15 -0
- data/lib/plume/ast/window_definition.rb +26 -0
- data/lib/plume/ast/with_clause.rb +15 -0
- data/lib/plume/ast/without_rowid_table_option.rb +6 -0
- data/lib/plume/catalog.rb +180 -0
- data/lib/plume/lexer.rb +639 -0
- data/lib/plume/parser.rb +4015 -0
- data/lib/plume/printer.rb +471 -0
- data/lib/plume/sqlite_grammar.rb +86 -0
- data/lib/plume/union.rb +28 -0
- data/lib/plume/version.rb +1 -1
- data/lib/plume.rb +143 -3
- metadata +167 -12
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class LiteralExpression < Node
|
|
5
|
+
token :value_tk # required
|
|
6
|
+
|
|
7
|
+
attribute :value, _Void # required
|
|
8
|
+
|
|
9
|
+
def self.new(*, value:, **) = super
|
|
10
|
+
def self.concrete(*, **) = super
|
|
11
|
+
|
|
12
|
+
# Parser may pass either tokens or attributes, so
|
|
13
|
+
# require none in `concrete` signature but ensure presence here.
|
|
14
|
+
def after_initialize
|
|
15
|
+
if @value == LiteralNil && @value_tk == LiteralNil
|
|
16
|
+
raise ArgumentError.new("missing either: :value, :value_tk")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def value
|
|
21
|
+
v = (@value == LiteralNil) ? value_tk_val : @value
|
|
22
|
+
case v
|
|
23
|
+
when LiteralNil then nil
|
|
24
|
+
when LiteralFalse then false
|
|
25
|
+
else v
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class MatchClause < Node
|
|
5
|
+
token :match_kw
|
|
6
|
+
token :type_tk
|
|
7
|
+
|
|
8
|
+
attribute :type, Stringy
|
|
9
|
+
|
|
10
|
+
def self.new(*, type:, **) = super
|
|
11
|
+
def self.concrete(*, match_kw:, type_tk:, **) = super
|
|
12
|
+
|
|
13
|
+
def type = (@type == LiteralNil) ? type_tk_src.to_sym.upcase : @type
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class Node < Literal::Struct
|
|
5
|
+
extend Union::Types
|
|
6
|
+
|
|
7
|
+
Stringy = _Union(String, Symbol)
|
|
8
|
+
|
|
9
|
+
prop :full_source, _Nilable(String)
|
|
10
|
+
prop :leading, _Nilable(Token)
|
|
11
|
+
prop :trailing, _Nilable(Token)
|
|
12
|
+
|
|
13
|
+
def self.inspectable(*props)
|
|
14
|
+
@inspectable_props ||= Set.new
|
|
15
|
+
@inspectable_props.merge(props)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.all_inspectable_props
|
|
19
|
+
ancestors.reduce(Set.new) do |all_props, ancestor|
|
|
20
|
+
if (props = ancestor.instance_variable_get(:@inspectable_props))
|
|
21
|
+
all_props.merge(props)
|
|
22
|
+
else
|
|
23
|
+
all_props
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def inspectable_props
|
|
29
|
+
all_inspectable_props = self.class.all_inspectable_props
|
|
30
|
+
all_inspectable_props.map do |prop|
|
|
31
|
+
val = __send__(prop)
|
|
32
|
+
next if val.nil? || (val.respond_to?(:empty?) && val.empty?)
|
|
33
|
+
|
|
34
|
+
[prop, val]
|
|
35
|
+
end.tap(&:compact!)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
alias_method :full_inspect, :inspect
|
|
39
|
+
def inspect
|
|
40
|
+
return "#<#{self.class.name}:#{object_id}>" if inspectable_props.empty?
|
|
41
|
+
|
|
42
|
+
prop_values = inspectable_props.map do |prop, val|
|
|
43
|
+
"#{prop}=#{val.inspect}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
"#<#{self.class.name}:#{object_id} #{prop_values&.join(", ")}>"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def pretty_please(pp)
|
|
50
|
+
pp.push "#{self.class.name}("
|
|
51
|
+
pp.map(inspectable_props) do |prop, val|
|
|
52
|
+
"#{prop} = #{pp.capture { pp.prettify(val) }}"
|
|
53
|
+
end
|
|
54
|
+
pp.push ")"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Direct child nodes in property declaration order. Arrays of nodes are flattened in
|
|
58
|
+
def children
|
|
59
|
+
self.class.literal_properties.each_with_object([]) do |property, kids|
|
|
60
|
+
value = instance_variable_get(:"@#{property.name}")
|
|
61
|
+
case value
|
|
62
|
+
when Node then kids << value
|
|
63
|
+
when Array then value.each { |v| kids << v if Node === v }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Yields every node in the subtree depth-first, parents before their children,
|
|
69
|
+
# along with the parent (nil for the receiver)
|
|
70
|
+
def walk(parent = nil, &block)
|
|
71
|
+
return enum_for(:walk, parent) unless block
|
|
72
|
+
|
|
73
|
+
yield self, parent
|
|
74
|
+
children.each { |child| child.walk(self, &block) }
|
|
75
|
+
self
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def each_node(&block)
|
|
79
|
+
return enum_for(:each_node) unless block
|
|
80
|
+
|
|
81
|
+
walk { |node, _parent| yield node }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Byte range `[beg, fin]` of the source this node was parsed from, spanning every token
|
|
85
|
+
# in its subtree. Separators the node merely carries (`leading`/`trailing`) are left out.
|
|
86
|
+
# Nil for nodes built by hand with `.new`
|
|
87
|
+
def location
|
|
88
|
+
beg = fin = nil
|
|
89
|
+
each_token_span do |_tok, b, f|
|
|
90
|
+
beg = b if beg.nil? || b < beg
|
|
91
|
+
fin = f if fin.nil? || f > fin
|
|
92
|
+
end
|
|
93
|
+
[beg, fin].freeze if beg
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def to_sql = Printer.print(self)
|
|
97
|
+
|
|
98
|
+
def source_text
|
|
99
|
+
return unless @full_source && (loc = location)
|
|
100
|
+
|
|
101
|
+
@full_source.byteslice(loc[0], loc[1] - loc[0])
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def each_token_span(&block)
|
|
105
|
+
instance_variables.each do |ivar|
|
|
106
|
+
next if ivar == :@leading || ivar == :@trailing
|
|
107
|
+
|
|
108
|
+
case (value = instance_variable_get(ivar))
|
|
109
|
+
when Node then value.each_token_span(&block)
|
|
110
|
+
when Array
|
|
111
|
+
if Token === value
|
|
112
|
+
yield value
|
|
113
|
+
else
|
|
114
|
+
value.each do |v|
|
|
115
|
+
if Node === v then v.each_token_span(&block)
|
|
116
|
+
elsif Token === v then yield v
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Uses the public readers, so token-backed values like `TableName#table` come out
|
|
125
|
+
# as their parsed values rather than raw token spans
|
|
126
|
+
def deconstruct_keys(keys)
|
|
127
|
+
names = keys || self.class.literal_properties.map(&:name)
|
|
128
|
+
names.each_with_object({}) do |name, h|
|
|
129
|
+
h[name] = public_send(name) if respond_to?(name)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def self.concrete(*a, full_source:, **kw)
|
|
134
|
+
object = allocate
|
|
135
|
+
object.__send__(:initialize, *a, full_source: full_source, **kw)
|
|
136
|
+
object
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Every prop is type checked as a tree is built, so the check must never hash a node,
|
|
140
|
+
# see Plume::Union
|
|
141
|
+
def self.attribute(name, type, **kw)
|
|
142
|
+
prop name, _Nilable(_Union(type, LiteralNil)), reader: kw.fetch(:reader, false), default: LiteralNil, **kw
|
|
143
|
+
inspectable name
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def self.token_names
|
|
147
|
+
ancestors.each_with_object(Set.new) do |ancestor, names|
|
|
148
|
+
names.merge(ancestor.instance_variable_get(:@token_names) || [])
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def self.token(name, **kw)
|
|
153
|
+
(@token_names ||= Set.new) << name
|
|
154
|
+
prop name, _Nilable(_Union(Token, LiteralNil)), reader: false, default: LiteralNil, **kw
|
|
155
|
+
|
|
156
|
+
# Define methods for accessing token properties, like the original source text, parsed value, and location.
|
|
157
|
+
# The token reader method is simply the parsed value of the token.
|
|
158
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
159
|
+
# frozen_string_literal: true
|
|
160
|
+
|
|
161
|
+
def #{name}_src
|
|
162
|
+
Token.source(@full_source, @#{name})
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def #{name}_val
|
|
166
|
+
Token.value(@full_source, @#{name})
|
|
167
|
+
end
|
|
168
|
+
alias_method :#{name}, :#{name}_val
|
|
169
|
+
|
|
170
|
+
def #{name}_loc
|
|
171
|
+
Token.location(@#{name})
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def #{name}_tok
|
|
175
|
+
Token.token(@#{name})
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def #{name}_spn
|
|
179
|
+
@#{name}
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def #{name}?
|
|
183
|
+
!(@#{name}.nil? || LiteralNil.equal?(@#{name}))
|
|
184
|
+
end
|
|
185
|
+
RUBY
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def self.node(name, type, **kw)
|
|
189
|
+
attribute name, _Nilable(type), **kw
|
|
190
|
+
define_node_reader(name)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def self.nodes(name, type, **kw)
|
|
194
|
+
attribute name, _Nilable(_Array(type)), **kw
|
|
195
|
+
define_node_reader(name)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Absent children read as nil rather than the LiteralNil default
|
|
199
|
+
def self.define_node_reader(name)
|
|
200
|
+
ivar = :"@#{name}"
|
|
201
|
+
define_method(name) do
|
|
202
|
+
value = instance_variable_get(ivar)
|
|
203
|
+
LiteralNil.equal?(value) ? nil : value
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class NoOpColumnConstraint < ColumnConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
|
|
8
|
+
attribute :name, Stringy
|
|
9
|
+
|
|
10
|
+
def self.new(*, **) = super
|
|
11
|
+
def self.concrete(*, **) = super
|
|
12
|
+
|
|
13
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class NoOpTableConstraint < TableConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
|
|
8
|
+
attribute :name, Stringy
|
|
9
|
+
|
|
10
|
+
def self.new(*, **) = super
|
|
11
|
+
def self.concrete(*, **) = super
|
|
12
|
+
|
|
13
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class NotNullColumnConstraint < ColumnConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
token :not_null_kw
|
|
8
|
+
|
|
9
|
+
attribute :name, Stringy
|
|
10
|
+
node :conflict_clause, ConflictClause
|
|
11
|
+
|
|
12
|
+
def self.new(*, **) = super
|
|
13
|
+
def self.concrete(*, not_null_kw:, **) = super
|
|
14
|
+
|
|
15
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class NullColumnConstraint < ColumnConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
token :null_kw
|
|
8
|
+
|
|
9
|
+
attribute :name, Stringy
|
|
10
|
+
node :conflict_clause, ConflictClause
|
|
11
|
+
|
|
12
|
+
def self.new(*, **) = super
|
|
13
|
+
def self.concrete(*, null_kw:, **) = super
|
|
14
|
+
|
|
15
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class OrderingTerm < Node
|
|
5
|
+
Direction = _Union(
|
|
6
|
+
:ASC, "ASC", :asc, "asc", :ASCENDING, "ASCENDING", :ascending, "ascending",
|
|
7
|
+
:DESC, "DESC", :desc, "desc", :DESCENDING, "DESCENDING", :descending, "descending",
|
|
8
|
+
)
|
|
9
|
+
Nulls = _Union(
|
|
10
|
+
:FIRST, "FIRST", :first, "first",
|
|
11
|
+
:LAST, "LAST", :last, "last",
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
token :collate_kw
|
|
15
|
+
token :collation_tk
|
|
16
|
+
token :direction_tk
|
|
17
|
+
token :nulls_kw
|
|
18
|
+
token :nulls_tk
|
|
19
|
+
|
|
20
|
+
node :expression, Expression
|
|
21
|
+
attribute :collation, Stringy
|
|
22
|
+
attribute :direction, Direction
|
|
23
|
+
attribute :nulls, Nulls
|
|
24
|
+
|
|
25
|
+
def self.new(*, **) = super
|
|
26
|
+
def self.concrete(*, **) = super
|
|
27
|
+
|
|
28
|
+
def collation = (@collation == LiteralNil) ? collation_tk_val&.to_sym&.upcase : @collation
|
|
29
|
+
def direction = (@direction == LiteralNil) ? direction_tk_val&.to_sym&.upcase : @direction
|
|
30
|
+
def nulls = (@nulls == LiteralNil) ? nulls_tk_val&.to_sym&.upcase : @nulls
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class OverClause < Node
|
|
5
|
+
token :over_kw
|
|
6
|
+
token :window_lp
|
|
7
|
+
token :base_window_name_tk
|
|
8
|
+
token :partition_by_kw
|
|
9
|
+
token :order_by_kw
|
|
10
|
+
token :window_rp
|
|
11
|
+
token :window_name_tk
|
|
12
|
+
|
|
13
|
+
nodes :partitions, Expression
|
|
14
|
+
nodes :orderings, OrderingTerm
|
|
15
|
+
node :frame, FrameSpec
|
|
16
|
+
attribute :window_name, Stringy
|
|
17
|
+
attribute :base_window_name, Stringy
|
|
18
|
+
|
|
19
|
+
def self.new(*, **) = super
|
|
20
|
+
def self.concrete(*, over_kw:, **) = super
|
|
21
|
+
|
|
22
|
+
def window_name = (@window_name == LiteralNil) ? window_name_tk_val : @window_name
|
|
23
|
+
def base_window_name = (@base_window_name == LiteralNil) ? base_window_name_tk_val : @base_window_name
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class ParentheticalExpression < Node
|
|
5
|
+
token :value_lp
|
|
6
|
+
token :value_tk
|
|
7
|
+
token :value_rp
|
|
8
|
+
|
|
9
|
+
# Hand-built trees may wrap plain Ruby values; a lambda keeps them out of the union's
|
|
10
|
+
# plain-value set, see Node.attribute
|
|
11
|
+
attribute :value, _Union(Expression, _Array(Expression), -> (v) { LiteralValue === v })
|
|
12
|
+
|
|
13
|
+
def self.new(*, value:, **) = super
|
|
14
|
+
def self.concrete(*, **) = super
|
|
15
|
+
|
|
16
|
+
# Parser may pass either tokens or attributes, so
|
|
17
|
+
# require none in `concrete` signature but ensure presence here.
|
|
18
|
+
def after_initialize
|
|
19
|
+
if @value == LiteralNil && @value_tk == LiteralNil
|
|
20
|
+
raise ArgumentError.new("missing either: :value, :value_tk")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# The child as stored - nil only for the empty list of `x IN ()`. Unlike `value`, a NULL
|
|
25
|
+
# literal stays a node, so it cannot be mistaken for that empty list
|
|
26
|
+
def contents = LiteralNil.equal?(@value) ? value_tk_val : @value
|
|
27
|
+
|
|
28
|
+
def value
|
|
29
|
+
case (v = contents)
|
|
30
|
+
when LiteralNil then nil
|
|
31
|
+
when LiteralFalse then false
|
|
32
|
+
when LiteralExpression then v.value
|
|
33
|
+
else v
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# A parenthesized join or source list used as a FROM source, like `(a JOIN b) AS ab`
|
|
5
|
+
class ParentheticalSource < Node
|
|
6
|
+
token :source_lp
|
|
7
|
+
token :source_rp
|
|
8
|
+
token :as_kw
|
|
9
|
+
token :alias_tk
|
|
10
|
+
|
|
11
|
+
node :source, _Any
|
|
12
|
+
attribute :alias, Stringy
|
|
13
|
+
|
|
14
|
+
def self.new(*, source:, **) = super
|
|
15
|
+
def self.concrete(*, source:, **) = super
|
|
16
|
+
|
|
17
|
+
def alias = (@alias == LiteralNil) ? alias_tk_val : @alias
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/pragma.html)**
|
|
5
|
+
class PragmaStatement < Node
|
|
6
|
+
token :pragma_kw
|
|
7
|
+
token :schema_tk
|
|
8
|
+
token :dot_tk
|
|
9
|
+
token :name_tk
|
|
10
|
+
token :eq_tk # `=`, or the opening parenthesis of `PRAGMA name(value)`
|
|
11
|
+
token :value_tk
|
|
12
|
+
token :value_rp
|
|
13
|
+
|
|
14
|
+
attribute :schema, Stringy
|
|
15
|
+
attribute :name, Stringy
|
|
16
|
+
|
|
17
|
+
def self.new(*, name:, **) = super
|
|
18
|
+
def self.concrete(*, pragma_kw:, name_tk:, **) = super
|
|
19
|
+
|
|
20
|
+
def schema = (@schema == LiteralNil) ? schema_tk_val : @schema
|
|
21
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
22
|
+
|
|
23
|
+
# As written: numbers come out as numbers, keywords and names as strings
|
|
24
|
+
inspectable def value = value_tk_val
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class PrimaryKeyColumnConstraint < ColumnConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
token :primary_key_kw
|
|
8
|
+
token :direction_tk
|
|
9
|
+
token :autoincrement_kw
|
|
10
|
+
|
|
11
|
+
attribute :name, Stringy
|
|
12
|
+
attribute :direction, Stringy
|
|
13
|
+
attribute :autoincrement, _Boolean
|
|
14
|
+
node :conflict_clause, ConflictClause
|
|
15
|
+
|
|
16
|
+
def self.new(*, **) = super
|
|
17
|
+
def self.concrete(*, primary_key_kw:, **) = super
|
|
18
|
+
|
|
19
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
20
|
+
def direction = (@direction == LiteralNil) ? direction_tk_val&.to_sym&.upcase : @direction
|
|
21
|
+
def autoincrement = (@autoincrement == LiteralNil) ? autoincrement_kw? : @autoincrement
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class PrimaryKeyTableConstraint < TableConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
token :primary_key_kw
|
|
8
|
+
token :primary_key_lp
|
|
9
|
+
token :autoincrement_kw
|
|
10
|
+
token :primary_key_rp
|
|
11
|
+
|
|
12
|
+
attribute :name, Stringy
|
|
13
|
+
attribute :autoincrement, _Boolean
|
|
14
|
+
nodes :columns, IndexedColumn
|
|
15
|
+
node :conflict_clause, ConflictClause
|
|
16
|
+
|
|
17
|
+
def self.new(*, columns:, **) = super
|
|
18
|
+
def self.concrete(*, primary_key_kw:, columns:, **) = super
|
|
19
|
+
|
|
20
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
21
|
+
def autoincrement = (@autoincrement == LiteralNil) ? autoincrement_kw? : @autoincrement
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class RaiseExpression < Node
|
|
5
|
+
Type = _Union(:IGNORE, :ROLLBACK, :ABORT, :FAIL)
|
|
6
|
+
|
|
7
|
+
token :raise_kw # required
|
|
8
|
+
token :raise_lp # required
|
|
9
|
+
token :type_kw # required
|
|
10
|
+
token :comma # maybe
|
|
11
|
+
token :error_message_tk # maybe
|
|
12
|
+
token :raise_rp # required
|
|
13
|
+
|
|
14
|
+
attribute :type, Type # required
|
|
15
|
+
attribute :error_message, Expression # maybe
|
|
16
|
+
|
|
17
|
+
def self.new(*, type:, **) = super
|
|
18
|
+
def self.concrete(*, raise_kw:, raise_lp:, type_kw:, raise_rp:, **) = super
|
|
19
|
+
|
|
20
|
+
def type = (@type == LiteralNil) ? type_kw_src.to_sym.upcase : @type
|
|
21
|
+
def error_message = (@error_message == LiteralNil) ? error_message_tk_val : @error_message
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_reindex.html)**
|
|
5
|
+
#
|
|
6
|
+
# `target` names a schema, table or index - SQLite works out which at run time
|
|
7
|
+
class ReindexStatement < Node
|
|
8
|
+
token :reindex_kw
|
|
9
|
+
|
|
10
|
+
node :target, TableName
|
|
11
|
+
|
|
12
|
+
def self.new(*, **) = super
|
|
13
|
+
def self.concrete(*, reindex_kw:, **) = super
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_savepoint.html)**
|
|
5
|
+
class ReleaseStatement < Node
|
|
6
|
+
token :release_kw # RELEASE or RELEASE SAVEPOINT
|
|
7
|
+
token :name_tk
|
|
8
|
+
|
|
9
|
+
attribute :name, Stringy
|
|
10
|
+
|
|
11
|
+
def self.new(*, name:, **) = super
|
|
12
|
+
def self.concrete(*, release_kw:, name_tk:, **) = super
|
|
13
|
+
|
|
14
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class ResultColumn < Node
|
|
5
|
+
token :as_kw
|
|
6
|
+
token :alias_tk
|
|
7
|
+
token :result_tk
|
|
8
|
+
|
|
9
|
+
node :result, Expression
|
|
10
|
+
attribute :alias, Stringy
|
|
11
|
+
|
|
12
|
+
def self.new(*, result:, **) = super
|
|
13
|
+
def self.concrete(*, **) = super
|
|
14
|
+
|
|
15
|
+
def result = (@result == LiteralNil) ? result_tk_val : @result
|
|
16
|
+
def alias = (@alias == LiteralNil) ? alias_tk_val : @alias
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class ReturningClause < Node
|
|
5
|
+
token :returning_kw
|
|
6
|
+
|
|
7
|
+
nodes :columns, _Union(ResultColumn, StarResultColumn)
|
|
8
|
+
|
|
9
|
+
def self.new(*, columns:, **) = super
|
|
10
|
+
def self.concrete(*, returning_kw:, columns:, **) = super
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_transaction.html)**
|
|
5
|
+
class RollbackStatement < Node
|
|
6
|
+
token :rollback_kw
|
|
7
|
+
token :transaction_kw
|
|
8
|
+
token :name_tk
|
|
9
|
+
token :to_kw # TO or TO SAVEPOINT
|
|
10
|
+
token :savepoint_tk
|
|
11
|
+
|
|
12
|
+
attribute :savepoint, Stringy
|
|
13
|
+
|
|
14
|
+
def self.new(*, **) = super
|
|
15
|
+
def self.concrete(*, rollback_kw:, **) = super
|
|
16
|
+
|
|
17
|
+
def savepoint = (@savepoint == LiteralNil) ? savepoint_tk_val : @savepoint
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_savepoint.html)**
|
|
5
|
+
class SavepointStatement < Node
|
|
6
|
+
token :savepoint_kw
|
|
7
|
+
token :name_tk
|
|
8
|
+
|
|
9
|
+
attribute :name, Stringy
|
|
10
|
+
|
|
11
|
+
def self.new(*, name:, **) = super
|
|
12
|
+
def self.concrete(*, savepoint_kw:, name_tk:, **) = super
|
|
13
|
+
|
|
14
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
15
|
+
end
|
|
16
|
+
end
|