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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +280 -10
  3. data/lib/plume/analysis/resolver.rb +574 -0
  4. data/lib/plume/analysis.rb +135 -0
  5. data/lib/plume/ast/alter_table_statement.rb +29 -0
  6. data/lib/plume/ast/analyze_statement.rb +15 -0
  7. data/lib/plume/ast/assignment.rb +18 -0
  8. data/lib/plume/ast/attach_statement.rb +17 -0
  9. data/lib/plume/ast/begin_statement.rb +17 -0
  10. data/lib/plume/ast/binary_expression.rb +71 -0
  11. data/lib/plume/ast/case_condition.rb +29 -0
  12. data/lib/plume/ast/case_expression.rb +22 -0
  13. data/lib/plume/ast/cast_expression.rb +16 -0
  14. data/lib/plume/ast/check_column_constraint.rb +20 -0
  15. data/lib/plume/ast/check_table_constraint.rb +19 -0
  16. data/lib/plume/ast/collate_column_constraint.rb +19 -0
  17. data/lib/plume/ast/collation_expression.rb +26 -0
  18. data/lib/plume/ast/column_constraint.rb +7 -0
  19. data/lib/plume/ast/column_definition.rb +16 -0
  20. data/lib/plume/ast/column_name.rb +22 -0
  21. data/lib/plume/ast/column_type.rb +25 -0
  22. data/lib/plume/ast/commit_statement.rb +13 -0
  23. data/lib/plume/ast/common_table_expression.rb +31 -0
  24. data/lib/plume/ast/compound_select.rb +30 -0
  25. data/lib/plume/ast/conflict_clause.rb +22 -0
  26. data/lib/plume/ast/conflict_resolution.rb +20 -0
  27. data/lib/plume/ast/create_index_statement.rb +29 -0
  28. data/lib/plume/ast/create_table_statement.rb +47 -0
  29. data/lib/plume/ast/create_trigger_statement.rb +43 -0
  30. data/lib/plume/ast/create_view_statement.rb +30 -0
  31. data/lib/plume/ast/create_virtual_table_statement.rb +28 -0
  32. data/lib/plume/ast/default_column_constraint.rb +29 -0
  33. data/lib/plume/ast/delete_statement.rb +28 -0
  34. data/lib/plume/ast/detach_statement.rb +13 -0
  35. data/lib/plume/ast/drop_statement.rb +21 -0
  36. data/lib/plume/ast/empty_function_argument.rb +6 -0
  37. data/lib/plume/ast/exists_expression.rb +15 -0
  38. data/lib/plume/ast/explain_statement.rb +15 -0
  39. data/lib/plume/ast/expression.rb +30 -0
  40. data/lib/plume/ast/foreign_key_action.rb +26 -0
  41. data/lib/plume/ast/foreign_key_clause.rb +50 -0
  42. data/lib/plume/ast/foreign_key_column_constraint.rb +16 -0
  43. data/lib/plume/ast/foreign_key_table_constraint.rb +20 -0
  44. data/lib/plume/ast/frame_boundary.rb +35 -0
  45. data/lib/plume/ast/frame_spec.rb +34 -0
  46. data/lib/plume/ast/function_arguments.rb +17 -0
  47. data/lib/plume/ast/function_reference.rb +29 -0
  48. data/lib/plume/ast/generated_as_column_constraint.rb +24 -0
  49. data/lib/plume/ast/indexed_column.rb +21 -0
  50. data/lib/plume/ast/insert_statement.rb +35 -0
  51. data/lib/plume/ast/join.rb +12 -0
  52. data/lib/plume/ast/join_clause.rb +15 -0
  53. data/lib/plume/ast/join_on_constraint.rb +12 -0
  54. data/lib/plume/ast/join_operator.rb +31 -0
  55. data/lib/plume/ast/join_using_constraint.rb +16 -0
  56. data/lib/plume/ast/like_expression.rb +19 -0
  57. data/lib/plume/ast/literal_expression.rb +29 -0
  58. data/lib/plume/ast/literal_false.rb +7 -0
  59. data/lib/plume/ast/literal_nil.rb +7 -0
  60. data/lib/plume/ast/literal_value.rb +18 -0
  61. data/lib/plume/ast/match_clause.rb +15 -0
  62. data/lib/plume/ast/node.rb +207 -0
  63. data/lib/plume/ast/noop_column_constraint.rb +15 -0
  64. data/lib/plume/ast/noop_table_constraint.rb +15 -0
  65. data/lib/plume/ast/not_like_expression.rb +7 -0
  66. data/lib/plume/ast/not_null_column_constraint.rb +17 -0
  67. data/lib/plume/ast/null_column_constraint.rb +17 -0
  68. data/lib/plume/ast/ordering_term.rb +32 -0
  69. data/lib/plume/ast/over_clause.rb +25 -0
  70. data/lib/plume/ast/parenthetical_expression.rb +37 -0
  71. data/lib/plume/ast/parenthetical_source.rb +19 -0
  72. data/lib/plume/ast/pragma_statement.rb +26 -0
  73. data/lib/plume/ast/primary_key_column_constraint.rb +23 -0
  74. data/lib/plume/ast/primary_key_table_constraint.rb +23 -0
  75. data/lib/plume/ast/raise_expression.rb +23 -0
  76. data/lib/plume/ast/reindex_statement.rb +15 -0
  77. data/lib/plume/ast/release_statement.rb +16 -0
  78. data/lib/plume/ast/result_column.rb +18 -0
  79. data/lib/plume/ast/returning_clause.rb +12 -0
  80. data/lib/plume/ast/rollback_statement.rb +19 -0
  81. data/lib/plume/ast/savepoint_statement.rb +16 -0
  82. data/lib/plume/ast/select_core.rb +27 -0
  83. data/lib/plume/ast/select_statement.rb +34 -0
  84. data/lib/plume/ast/star_function_argument.rb +6 -0
  85. data/lib/plume/ast/star_result_column.rb +16 -0
  86. data/lib/plume/ast/strict_table_option.rb +6 -0
  87. data/lib/plume/ast/subquery_expression.rb +14 -0
  88. data/lib/plume/ast/subquery_source.rb +19 -0
  89. data/lib/plume/ast/table_constraint.rb +7 -0
  90. data/lib/plume/ast/table_function.rb +28 -0
  91. data/lib/plume/ast/table_name.rb +28 -0
  92. data/lib/plume/ast/table_option.rb +7 -0
  93. data/lib/plume/ast/ternary_expression.rb +26 -0
  94. data/lib/plume/ast/token.rb +180 -0
  95. data/lib/plume/ast/unary_expression.rb +36 -0
  96. data/lib/plume/ast/unique_column_constraint.rb +17 -0
  97. data/lib/plume/ast/unique_table_constraint.rb +20 -0
  98. data/lib/plume/ast/update_statement.rb +35 -0
  99. data/lib/plume/ast/upsert_clause.rb +26 -0
  100. data/lib/plume/ast/vacuum_statement.rb +18 -0
  101. data/lib/plume/ast/values_clause.rb +12 -0
  102. data/lib/plume/ast/values_row.rb +13 -0
  103. data/lib/plume/ast/variable.rb +15 -0
  104. data/lib/plume/ast/window_definition.rb +26 -0
  105. data/lib/plume/ast/with_clause.rb +15 -0
  106. data/lib/plume/ast/without_rowid_table_option.rb +6 -0
  107. data/lib/plume/catalog.rb +180 -0
  108. data/lib/plume/lexer.rb +639 -0
  109. data/lib/plume/parser.rb +4015 -0
  110. data/lib/plume/printer.rb +471 -0
  111. data/lib/plume/sqlite_grammar.rb +86 -0
  112. data/lib/plume/union.rb +28 -0
  113. data/lib/plume/version.rb +1 -1
  114. data/lib/plume.rb +143 -3
  115. 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ LiteralFalse = Object.new
5
+ def LiteralFalse.inspect = "<LiteralFalse>"
6
+ LiteralFalse.freeze
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ LiteralNil = Object.new
5
+ def LiteralNil.inspect = "<LiteralNil>"
6
+ LiteralNil.freeze
7
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ LiteralValue = _Union(
5
+ nil,
6
+ true,
7
+ false,
8
+ :CURRENT_TIME,
9
+ :CURRENT_DATE,
10
+ :CURRENT_TIMESTAMP,
11
+ :*,
12
+ Numeric,
13
+ String,
14
+ # Blob,
15
+ LiteralNil,
16
+ LiteralFalse,
17
+ )
18
+ 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class NotLikeExpression < LikeExpression
5
+ def operator = :NOT_LIKE
6
+ end
7
+ 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