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,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ # A single `SELECT … FROM … WHERE … GROUP BY … HAVING … WINDOW …`, without the
5
+ # statement-level `WITH`, `ORDER BY` and `LIMIT`
6
+ class SelectCore < Node
7
+ token :select_kw
8
+ token :type_tk
9
+ token :from_kw
10
+ token :where_kw
11
+ token :group_by_kw
12
+ token :having_kw
13
+ token :window_kw
14
+
15
+ nodes :columns, _Union(ResultColumn, StarResultColumn)
16
+ node :source, _Any
17
+ node :condition, Expression
18
+ nodes :groupings, Expression
19
+ node :having, Expression
20
+ nodes :windows, WindowDefinition
21
+
22
+ def self.new(*, columns:, **) = super
23
+ def self.concrete(*, select_kw:, columns:, **) = super
24
+
25
+ inspectable def distinct = type_tk_tok == :DISTINCT
26
+ end
27
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ # **[SQLite Docs](https://www.sqlite.org/lang_select.html)**
5
+ #
6
+ # ```sql
7
+ # with t as (select 1) select * from t union select 2 order by 1 limit 1
8
+ # ```
9
+ #
10
+ # `body` is a `SelectCore`, a `ValuesClause`, or a `CompoundSelect` of those.
11
+ # `ORDER BY` and `LIMIT` belong to the statement as a whole, not to its last core.
12
+ class SelectStatement < Node
13
+ token :order_by_kw
14
+ token :limit_kw
15
+ token :offset_tk # OFFSET keyword, or the comma in `LIMIT offset, limit`
16
+
17
+ node :with_clause, _Any
18
+ node :body, _Any
19
+ nodes :orderings, OrderingTerm
20
+ node :limit, Expression
21
+ node :offset, Expression
22
+
23
+ def self.new(*, body:, **) = super
24
+ def self.concrete(*, body:, **) = super
25
+
26
+ # Every `SelectCore` and `ValuesClause` making up the body, left to right
27
+ def cores
28
+ case body
29
+ when CompoundSelect then body.cores
30
+ else [body]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class StarFunctionArgument < Node
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class StarResultColumn < Node
5
+ token :table_tk
6
+ token :dot_tk
7
+ token :star_tk
8
+
9
+ attribute :table, Stringy
10
+
11
+ def self.new(*, **) = super
12
+ def self.concrete(*, star_tk:, **) = super
13
+
14
+ def table = (@table == LiteralNil) ? table_tk_val : @table
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class StrictTableOption < TableOption
5
+ end
6
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ # A scalar subquery `(SELECT …)`, also the right side of `x IN (SELECT …)`
5
+ class SubqueryExpression < Node
6
+ token :select_lp
7
+ token :select_rp
8
+
9
+ node :select, SelectStatement
10
+
11
+ def self.new(*, select:, **) = super
12
+ def self.concrete(*, select:, **) = super
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ # A parenthesized select used as a FROM source, like `(SELECT …) AS s`
5
+ class SubquerySource < Node
6
+ token :select_lp
7
+ token :select_rp
8
+ token :as_kw
9
+ token :alias_tk
10
+
11
+ node :select, SelectStatement
12
+ attribute :alias, Stringy
13
+
14
+ def self.new(*, select:, **) = super
15
+ def self.concrete(*, select:, **) = super
16
+
17
+ def alias = (@alias == LiteralNil) ? alias_tk_val : @alias
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class TableConstraint < Node
5
+ # abstract
6
+ end
7
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ # A table-valued function used as a FROM source, like `json_each(doc) AS j`
5
+ class TableFunction < Node
6
+ token :schema_tk
7
+ token :dot_tk
8
+ token :function_tk
9
+ token :arguments_lp
10
+ token :arguments_rp
11
+ token :as_kw
12
+ token :alias_tk
13
+
14
+ attribute :schema, Stringy
15
+ attribute :function, Stringy
16
+ nodes :arguments, Expression
17
+ attribute :alias, Stringy
18
+
19
+ def self.new(*, function:, **) = super
20
+ def self.concrete(*, function_tk:, **) = super
21
+
22
+ def schema = (@schema == LiteralNil) ? schema_tk_val : @schema
23
+ def function = (@function == LiteralNil) ? function_tk_val : @function
24
+ def alias = (@alias == LiteralNil) ? alias_tk_val : @alias
25
+
26
+ alias_method :name, :function
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class TableName < Node
5
+ token :schema_tk
6
+ token :dot_tk
7
+ token :table_tk
8
+ token :as_kw
9
+ token :alias_tk
10
+ token :indexed_kw # INDEXED BY, or NOT INDEXED
11
+ token :index_tk
12
+
13
+ attribute :schema, Stringy
14
+ attribute :table, Stringy
15
+ attribute :alias, Stringy
16
+
17
+ def self.new(*, table:, **) = super
18
+ def self.concrete(*, table_tk:, **) = super
19
+
20
+ def schema = (@schema == LiteralNil) ? schema_tk_val : @schema
21
+ def table = (@table == LiteralNil) ? table_tk_val : @table
22
+ def alias = (@alias == LiteralNil) ? alias_tk_val : @alias
23
+ def indexed_by = index_tk_val
24
+ def not_indexed = indexed_kw_tok == [:NOT, :INDEXED]
25
+
26
+ alias_method :name, :table
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class TableOption < Node
5
+ # abstract
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ # `left [NOT] BETWEEN middle AND right`
5
+ class TernaryExpression < Node
6
+ TOKEN_TO_OPERATOR = {
7
+ :BETWEEN => :BETWEEN,
8
+ [:BETWEEN].freeze => :BETWEEN,
9
+ [:NOT, :BETWEEN].freeze => :NOT_BETWEEN,
10
+ }.freeze
11
+ Operator = _Union(*TOKEN_TO_OPERATOR.values)
12
+
13
+ token :operator_tk
14
+ token :and_kw
15
+
16
+ attribute :operator, Operator
17
+ node :left, Expression
18
+ node :middle, Expression
19
+ node :right, Expression
20
+
21
+ def self.new(*, operator:, left:, middle:, right:, **) = super
22
+ def self.concrete(*, operator_tk:, left:, middle:, right:, **) = super
23
+
24
+ def operator = (@operator == LiteralNil) ? TOKEN_TO_OPERATOR[operator_tk_tok] : @operator
25
+ end
26
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ module Token
5
+ extend Literal::Types
6
+ extend self
7
+
8
+ Type = _Union(
9
+ :keyword,
10
+ :identifier,
11
+ :punctuation,
12
+ :string,
13
+ :numeric,
14
+ :blob,
15
+ :true,
16
+ :false,
17
+ :null,
18
+ :current,
19
+ :variable,
20
+ )
21
+
22
+ Tuple = _Tuple(_Union(Symbol, _Array(Symbol)), Integer, Integer, Type)
23
+
24
+ def Keyword(arg)
25
+ [*arg, :keyword] if arg
26
+ end
27
+
28
+ def Identifier(arg)
29
+ [*arg, :identifier] if arg
30
+ end
31
+
32
+ def Punctuation(arg)
33
+ [*arg, :punctuation] if arg
34
+ end
35
+
36
+ def String(arg)
37
+ [*arg, :string] if arg
38
+ end
39
+
40
+ def Numeric(arg)
41
+ [*arg, :numeric] if arg
42
+ end
43
+
44
+ def Blob(arg)
45
+ [*arg, :blob] if arg
46
+ end
47
+
48
+ def True(arg)
49
+ [*arg, :true] if arg
50
+ end
51
+
52
+ def False(arg)
53
+ [*arg, :false] if arg
54
+ end
55
+
56
+ def Null(arg)
57
+ [*arg, :null] if arg
58
+ end
59
+
60
+ def Current(arg)
61
+ [*arg, :current] if arg
62
+ end
63
+
64
+ def Variable(arg)
65
+ [*arg, :variable] if arg
66
+ end
67
+
68
+ def source(str, tup)
69
+ return unless str && present?(tup)
70
+
71
+ _, beg, fin, = tup
72
+
73
+ str.byteslice(beg, (fin - beg))
74
+ end
75
+
76
+ def value(str, tup)
77
+ return unless str && present?(tup)
78
+
79
+ *, type = tup
80
+
81
+ case type
82
+ when :null then LiteralNil
83
+ when :true then true
84
+ when :false then LiteralFalse
85
+ when :current then source(str, tup).to_sym.upcase
86
+ when :numeric then numeric_literal(str, tup)
87
+ when :string then string_literal(str, tup)
88
+ when :blob then blob_literal(str, tup)
89
+ when :keyword then source(str, tup)
90
+ when :identifier then identifier(str, tup)
91
+ when :punctuation then source(str, tup)
92
+ when :variable then source(str, tup)
93
+ end
94
+ end
95
+
96
+ def location(tup)
97
+ return unless present?(tup)
98
+
99
+ _, beg, fin, = tup
100
+
101
+ [beg, fin].freeze
102
+ end
103
+
104
+ def token(tup)
105
+ return unless present?(tup)
106
+
107
+ tok, * = tup
108
+
109
+ tok
110
+ end
111
+
112
+ def ===(other)
113
+ Tuple === other
114
+ end
115
+
116
+ private
117
+
118
+ def present?(tup) = !(tup.nil? || LiteralNil.equal?(tup))
119
+
120
+ def numeric_literal(str, tup)
121
+ tok, * = tup
122
+ src = source(str, tup)
123
+
124
+ case tok
125
+ when :INTEGER, [:PLUS, :INTEGER], [:MINUS, :INTEGER]
126
+ integer_literal(src)
127
+ when :FLOAT, [:PLUS, :FLOAT], [:MINUS, :FLOAT]
128
+ src.to_f
129
+ when :QNUMBER, [:PLUS, :QNUMBER], [:MINUS, :QNUMBER]
130
+ (src.match?(/[.eE]/) && !src.match?(/0x/i)) ? src.delete("_").to_f : integer_literal(src)
131
+ end
132
+ end
133
+
134
+ def integer_literal(src)
135
+ digits = src.delete("_").delete_prefix("+")
136
+ negative = digits.delete_prefix!("-")
137
+ value = digits.match?(/\A0x/i) ? hex_literal(digits) : digits.to_i
138
+ negative ? -value : value
139
+ end
140
+
141
+ # SQLite reads a hex literal as a 64-bit two's-complement integer, so 0xFFFFFFFFFFFFFFFF is -1
142
+ def hex_literal(digits)
143
+ value = digits.to_i(16)
144
+ (value >= (2**63)) ? value - (2**64) : value
145
+ end
146
+
147
+ def string_literal(str, tup)
148
+ src = source(str, tup)
149
+ bytesize = src.bytesize
150
+ # strip the leading and trailing quotes, a quote inside is written twice
151
+ src.byteslice(1, bytesize - 2).gsub("''", "'")
152
+ end
153
+
154
+ def blob_literal(str, tup)
155
+ src = source(str, tup)
156
+ bytesize = src.bytesize
157
+ # trim the leading /x'/i and trailing /'/ then decode the hexadecimal string
158
+ [src.byteslice(2, bytesize - 3)].pack("H*")
159
+ end
160
+
161
+ def identifier(str, tup)
162
+ tok, * = tup
163
+
164
+ return string_literal(str, tup) if tok == :STRING
165
+
166
+ src = source(str, tup)
167
+
168
+ return src if tok != :ID
169
+
170
+ # Inside quotes, the quote character itself is written twice. Brackets have no escape
171
+ case src.getbyte(0)
172
+ when 96 then src.byteslice(1, src.bytesize - 2).gsub("``", "`")
173
+ when 39 then src.byteslice(1, src.bytesize - 2).gsub("''", "'")
174
+ when 34 then src.byteslice(1, src.bytesize - 2).gsub('""', '"')
175
+ when 91 then src.byteslice(1, src.bytesize - 2)
176
+ else src
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class UnaryExpression < Node
5
+ TOKEN_TO_OPERATOR = {
6
+ :BITNOT => :INVERT,
7
+ :PLUS => :IDENTITY,
8
+ :MINUS => :NEGATE,
9
+ :NOT => :NOT,
10
+ :ISNULL => :IS_NULL,
11
+ :NOTNULL => :NOT_NULL,
12
+ [:NOT, :NULL].freeze => :NOT_NULL,
13
+ }.freeze
14
+ Operator = _Union(*TOKEN_TO_OPERATOR.values)
15
+
16
+ token :operator_tk # required
17
+ token :operand_tk # required
18
+
19
+ attribute :operator, Operator # required
20
+ attribute :operand, Expression # required
21
+
22
+ def self.new(*, operator:, operand:, **) = super
23
+ def self.concrete(*, operator_tk:, **) = super
24
+
25
+ # Parser may pass either tokens or attributes, so
26
+ # require none in `concrete` signature but ensure presence here.
27
+ def after_initialize
28
+ if @operand.nil? && @operand_tk.nil?
29
+ raise ArgumentError.new("missing either: :operand, :operand_tk")
30
+ end
31
+ end
32
+
33
+ def operator = (@operator == LiteralNil) ? TOKEN_TO_OPERATOR[operator_tk_tok] : @operator
34
+ def operand = (@operand == LiteralNil) ? operand_tk_val : @operand
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class UniqueColumnConstraint < ColumnConstraint
5
+ token :constraint_kw
6
+ token :name_tk
7
+ token :unique_kw
8
+
9
+ attribute :name, Stringy
10
+ node :conflict_clause, ConflictClause
11
+
12
+ def self.new(*, **) = super
13
+ def self.concrete(*, unique_kw:, **) = super
14
+
15
+ def name = (@name == LiteralNil) ? name_tk_val : @name
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class UniqueTableConstraint < TableConstraint
5
+ token :constraint_kw
6
+ token :name_tk
7
+ token :unique_kw
8
+ token :unique_lp
9
+ token :unique_rp
10
+
11
+ attribute :name, Stringy
12
+ nodes :columns, IndexedColumn
13
+ node :conflict_clause, ConflictClause
14
+
15
+ def self.new(*, columns:, **) = super
16
+ def self.concrete(*, columns:, **) = super
17
+
18
+ def name = (@name == LiteralNil) ? name_tk_val : @name
19
+ end
20
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ # **[SQLite Docs](https://www.sqlite.org/lang_update.html)**
5
+ #
6
+ # ```sql
7
+ # update or ignore t as x set a = u.a from u where u.id = x.id returning x.a
8
+ # ```
9
+ class UpdateStatement < Node
10
+ include ConflictResolution
11
+
12
+ token :update_kw # UPDATE [OR …]
13
+ token :set_kw
14
+ token :from_kw
15
+ token :where_kw
16
+ token :order_by_kw
17
+ token :limit_kw
18
+ token :offset_tk
19
+
20
+ node :with_clause, WithClause
21
+ node :table, TableName
22
+ nodes :assignments, Assignment
23
+ node :source, _Any
24
+ node :condition, Expression
25
+ node :returning, ReturningClause
26
+ nodes :orderings, OrderingTerm
27
+ node :limit, Expression
28
+ node :offset, Expression
29
+
30
+ def self.new(*, table:, assignments:, **) = super
31
+ def self.concrete(*, update_kw:, table:, assignments:, **) = super
32
+
33
+ inspectable def or_action = TOKEN_TO_RESOLUTION[update_kw_tok]
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ # `ON CONFLICT [(target) [WHERE …]] DO NOTHING | DO UPDATE SET … [WHERE …]`
5
+ class UpsertClause < Node
6
+ token :on_conflict_kw
7
+ token :target_lp
8
+ token :target_rp
9
+ token :target_where_kw
10
+ token :do_kw
11
+ token :action_kw
12
+ token :set_kw
13
+ token :where_kw
14
+
15
+ nodes :targets, OrderingTerm
16
+ node :target_condition, Expression
17
+ nodes :assignments, Assignment
18
+ node :condition, Expression
19
+
20
+ def self.new(*, **) = super
21
+ def self.concrete(*, on_conflict_kw:, action_kw:, **) = super
22
+
23
+ # :NOTHING or :UPDATE
24
+ inspectable def action = action_kw_tok
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ # **[SQLite Docs](https://www.sqlite.org/lang_vacuum.html)**
5
+ class VacuumStatement < Node
6
+ token :vacuum_kw
7
+ token :schema_tk
8
+ token :into_kw
9
+
10
+ attribute :schema, Stringy
11
+ node :into, Expression
12
+
13
+ def self.new(*, **) = super
14
+ def self.concrete(*, vacuum_kw:, **) = super
15
+
16
+ def schema = (@schema == LiteralNil) ? schema_tk_val : @schema
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class ValuesClause < Node
5
+ token :values_kw
6
+
7
+ nodes :rows, ValuesRow
8
+
9
+ def self.new(*, rows:, **) = super
10
+ def self.concrete(*, values_kw:, rows:, **) = super
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class ValuesRow < Node
5
+ token :row_lp
6
+ token :row_rp
7
+
8
+ nodes :values, Expression
9
+
10
+ def self.new(*, values:, **) = super
11
+ def self.concrete(*, values:, **) = super
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ # A bind parameter: `?`, `?1`, `:name`, `@name` or `$name`
5
+ class Variable < Node
6
+ token :name_tk
7
+
8
+ attribute :name, Stringy
9
+
10
+ def self.new(*, name:, **) = super
11
+ def self.concrete(*, name_tk:, **) = super
12
+
13
+ def name = (@name == LiteralNil) ? name_tk_val : @name
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ # A named window from the `WINDOW` clause of a select core
5
+ class WindowDefinition < Node
6
+ token :name_tk
7
+ token :as_kw
8
+ token :window_lp
9
+ token :base_window_name_tk
10
+ token :partition_by_kw
11
+ token :order_by_kw
12
+ token :window_rp
13
+
14
+ attribute :name, Stringy
15
+ attribute :base_window_name, Stringy
16
+ nodes :partitions, Expression
17
+ nodes :orderings, OrderingTerm
18
+ node :frame, FrameSpec
19
+
20
+ def self.new(*, name:, **) = super
21
+ def self.concrete(*, name_tk:, **) = super
22
+
23
+ def name = (@name == LiteralNil) ? name_tk_val : @name
24
+ def base_window_name = (@base_window_name == LiteralNil) ? base_window_name_tk_val : @base_window_name
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class WithClause < Node
5
+ token :with_kw
6
+ token :recursive_kw
7
+
8
+ nodes :ctes, CommonTableExpression
9
+
10
+ def self.new(*, ctes:, **) = super
11
+ def self.concrete(*, with_kw:, ctes:, **) = super
12
+
13
+ inspectable def recursive = recursive_kw?
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class WithoutRowidTableOption < TableOption
5
+ end
6
+ end