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,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_createview.html)**
|
|
5
|
+
#
|
|
6
|
+
# ```sql
|
|
7
|
+
# create temp view if not exists v (a, b) as select 1, 2
|
|
8
|
+
# ```
|
|
9
|
+
class CreateViewStatement < Node
|
|
10
|
+
token :create_kw
|
|
11
|
+
token :temp_kw
|
|
12
|
+
token :view_kw
|
|
13
|
+
token :if_not_exists_kw
|
|
14
|
+
token :columns_lp
|
|
15
|
+
token :columns_rp
|
|
16
|
+
token :as_kw
|
|
17
|
+
|
|
18
|
+
node :view, TableName
|
|
19
|
+
nodes :columns, IndexedColumn
|
|
20
|
+
node :select, SelectStatement
|
|
21
|
+
|
|
22
|
+
def self.new(*, view:, select:, **) = super
|
|
23
|
+
def self.concrete(*, create_kw:, view_kw:, view:, select:, **) = super
|
|
24
|
+
|
|
25
|
+
inspectable def temporary = temp_kw?
|
|
26
|
+
inspectable def if_not_exists = if_not_exists_kw?
|
|
27
|
+
|
|
28
|
+
def column_names = columns&.map(&:name)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_createvtab.html)**
|
|
5
|
+
#
|
|
6
|
+
# Module arguments are free-form - every module has its own syntax for them - so they
|
|
7
|
+
# are kept as the source text between the commas
|
|
8
|
+
class CreateVirtualTableStatement < Node
|
|
9
|
+
token :create_virtual_table_kw
|
|
10
|
+
token :if_not_exists_kw
|
|
11
|
+
token :using_kw
|
|
12
|
+
token :module_tk
|
|
13
|
+
token :arguments_lp
|
|
14
|
+
token :arguments_rp
|
|
15
|
+
|
|
16
|
+
node :table, TableName
|
|
17
|
+
attribute :module_name, Stringy
|
|
18
|
+
attribute :arguments, _Array(String)
|
|
19
|
+
|
|
20
|
+
def self.new(*, table:, module_name:, **) = super
|
|
21
|
+
def self.concrete(*, create_virtual_table_kw:, table:, module_tk:, **) = super
|
|
22
|
+
|
|
23
|
+
def module_name = (@module_name == LiteralNil) ? module_tk_val : @module_name
|
|
24
|
+
def arguments = (@arguments.nil? || LiteralNil.equal?(@arguments)) ? [] : @arguments
|
|
25
|
+
|
|
26
|
+
inspectable def if_not_exists = if_not_exists_kw?
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class DefaultColumnConstraint < ColumnConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
token :default_kw
|
|
8
|
+
token :default_lp
|
|
9
|
+
token :default_rp
|
|
10
|
+
token :value_tk
|
|
11
|
+
|
|
12
|
+
attribute :name, Stringy
|
|
13
|
+
attribute :value, Expression
|
|
14
|
+
|
|
15
|
+
def self.new(*, **) = super
|
|
16
|
+
def self.concrete(*, default_kw:, **) = super
|
|
17
|
+
|
|
18
|
+
# Parser may pass either tokens or attributes, so
|
|
19
|
+
# require none in `concrete` signature but ensure presence here.
|
|
20
|
+
def after_initialize
|
|
21
|
+
if @value.nil? && @value_tk.nil?
|
|
22
|
+
raise ArgumentError.new("missing either: :value, :value_tk")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
27
|
+
def value = (@value == LiteralNil) ? value_tk_val : @value
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_delete.html)**
|
|
5
|
+
#
|
|
6
|
+
# ```sql
|
|
7
|
+
# delete from t where a > 1 returning *
|
|
8
|
+
# ```
|
|
9
|
+
class DeleteStatement < Node
|
|
10
|
+
token :delete_kw
|
|
11
|
+
token :from_kw
|
|
12
|
+
token :where_kw
|
|
13
|
+
token :order_by_kw
|
|
14
|
+
token :limit_kw
|
|
15
|
+
token :offset_tk
|
|
16
|
+
|
|
17
|
+
node :with_clause, WithClause
|
|
18
|
+
node :table, TableName
|
|
19
|
+
node :condition, Expression
|
|
20
|
+
node :returning, ReturningClause
|
|
21
|
+
nodes :orderings, OrderingTerm
|
|
22
|
+
node :limit, Expression
|
|
23
|
+
node :offset, Expression
|
|
24
|
+
|
|
25
|
+
def self.new(*, table:, **) = super
|
|
26
|
+
def self.concrete(*, delete_kw:, from_kw:, table:, **) = super
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_detach.html)**
|
|
5
|
+
class DetachStatement < Node
|
|
6
|
+
token :detach_kw # DETACH or DETACH DATABASE
|
|
7
|
+
|
|
8
|
+
node :schema, Expression
|
|
9
|
+
|
|
10
|
+
def self.new(*, schema:, **) = super
|
|
11
|
+
def self.concrete(*, detach_kw:, schema:, **) = super
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# DROP TABLE, DROP INDEX, DROP VIEW and DROP TRIGGER, told apart by `type`
|
|
5
|
+
#
|
|
6
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_droptable.html)**
|
|
7
|
+
class DropStatement < Node
|
|
8
|
+
token :drop_kw
|
|
9
|
+
token :type_kw
|
|
10
|
+
token :if_exists_kw
|
|
11
|
+
|
|
12
|
+
node :target, TableName
|
|
13
|
+
|
|
14
|
+
def self.new(*, target:, **) = super
|
|
15
|
+
def self.concrete(*, drop_kw:, type_kw:, target:, **) = super
|
|
16
|
+
|
|
17
|
+
# :TABLE, :INDEX, :VIEW or :TRIGGER
|
|
18
|
+
inspectable def type = type_kw_tok
|
|
19
|
+
inspectable def if_exists = if_exists_kw?
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# `EXISTS (SELECT …)`. `NOT EXISTS` is a `UnaryExpression` wrapping this
|
|
5
|
+
class ExistsExpression < Node
|
|
6
|
+
token :exists_kw
|
|
7
|
+
token :select_lp
|
|
8
|
+
token :select_rp
|
|
9
|
+
|
|
10
|
+
node :select, SelectStatement
|
|
11
|
+
|
|
12
|
+
def self.new(*, select:, **) = super
|
|
13
|
+
def self.concrete(*, select:, **) = super
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_explain.html)**
|
|
5
|
+
class ExplainStatement < Node
|
|
6
|
+
token :explain_kw # EXPLAIN or EXPLAIN QUERY PLAN
|
|
7
|
+
|
|
8
|
+
node :statement, _Any
|
|
9
|
+
|
|
10
|
+
def self.new(*, statement:, **) = super
|
|
11
|
+
def self.concrete(*, explain_kw:, statement:, **) = super
|
|
12
|
+
|
|
13
|
+
inspectable def query_plan = explain_kw_tok == [:EXPLAIN, :QUERY, :PLAN]
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# No plain values (nil, true, symbols) in here, see Node.attribute for why
|
|
5
|
+
Expression = _Union(
|
|
6
|
+
Numeric,
|
|
7
|
+
String,
|
|
8
|
+
Token,
|
|
9
|
+
ColumnName,
|
|
10
|
+
TableName,
|
|
11
|
+
_Deferred {
|
|
12
|
+
_Union(
|
|
13
|
+
UnaryExpression,
|
|
14
|
+
BinaryExpression,
|
|
15
|
+
CastExpression,
|
|
16
|
+
TernaryExpression,
|
|
17
|
+
LikeExpression,
|
|
18
|
+
CaseExpression,
|
|
19
|
+
CollationExpression,
|
|
20
|
+
RaiseExpression,
|
|
21
|
+
FunctionReference,
|
|
22
|
+
LiteralExpression,
|
|
23
|
+
ParentheticalExpression,
|
|
24
|
+
SubqueryExpression,
|
|
25
|
+
ExistsExpression,
|
|
26
|
+
Variable,
|
|
27
|
+
)
|
|
28
|
+
},
|
|
29
|
+
)
|
|
30
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class ForeignKeyAction < Node
|
|
5
|
+
TOKEN_TO_ACTION = {
|
|
6
|
+
:CASCADE => :CASCADE,
|
|
7
|
+
:RESTRICT => :RESTRICT,
|
|
8
|
+
[:SET, :NULL].freeze => :SET_NULL,
|
|
9
|
+
[:SET, :DEFAULT].freeze => :SET_DEFAULT,
|
|
10
|
+
[:NO, :ACTION].freeze => :NO_ACTION,
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
token :on_kw
|
|
14
|
+
token :trigger_kw
|
|
15
|
+
token :action_kw
|
|
16
|
+
|
|
17
|
+
attribute :trigger, Stringy
|
|
18
|
+
attribute :action, _Union(*TOKEN_TO_ACTION.values)
|
|
19
|
+
|
|
20
|
+
def self.new(*, trigger:, action:, **) = super
|
|
21
|
+
def self.concrete(*, on_kw:, trigger_kw:, action_kw:, **) = super
|
|
22
|
+
|
|
23
|
+
def trigger = (@trigger == LiteralNil) ? trigger_kw_src.to_sym.upcase : @trigger
|
|
24
|
+
def action = (@action == LiteralNil) ? TOKEN_TO_ACTION[action_kw_tok] : @action
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class ForeignKeyClause < Node
|
|
5
|
+
TOKEN_TO_DEFERRABLE = {
|
|
6
|
+
:DEFERRABLE => false,
|
|
7
|
+
[:DEFERRABLE].freeze => false,
|
|
8
|
+
[:NOT, :DEFERRABLE].freeze => false,
|
|
9
|
+
[:DEFERRABLE, :INITIALLY, :DEFERRED].freeze => true,
|
|
10
|
+
[:DEFERRABLE, :INITIALLY, :IMMEDIATE].freeze => false,
|
|
11
|
+
[:NOT, :DEFERRABLE, :INITIALLY, :DEFERRED].freeze => false,
|
|
12
|
+
[:NOT, :DEFERRABLE, :INITIALLY, :IMMEDIATE].freeze => false,
|
|
13
|
+
}.freeze
|
|
14
|
+
Deferrable = _Union(*TOKEN_TO_DEFERRABLE.values.uniq)
|
|
15
|
+
|
|
16
|
+
token :references_kw
|
|
17
|
+
token :table_tk
|
|
18
|
+
token :columns_lp
|
|
19
|
+
token :columns_rp
|
|
20
|
+
token :deferrable_span
|
|
21
|
+
|
|
22
|
+
attribute :table, Stringy
|
|
23
|
+
attribute :deferred, Deferrable
|
|
24
|
+
nodes :columns, IndexedColumn
|
|
25
|
+
nodes :actions, ForeignKeyAction
|
|
26
|
+
nodes :match_clauses, MatchClause
|
|
27
|
+
|
|
28
|
+
def self.new(*, table:, **) = super
|
|
29
|
+
def self.concrete(*, references_kw:, table_tk:, **) = super
|
|
30
|
+
|
|
31
|
+
def table = (@table == LiteralNil) ? table_tk_val : @table
|
|
32
|
+
def deferred = (@deferred == LiteralNil) ? TOKEN_TO_DEFERRABLE[deferrable_span_tok] : @deferred
|
|
33
|
+
|
|
34
|
+
def on_delete
|
|
35
|
+
actions.reverse_each do |it|
|
|
36
|
+
return it.action if it.trigger == :DELETE
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def on_update
|
|
41
|
+
actions.reverse_each do |it|
|
|
42
|
+
return it.action if it.trigger == :UPDATE
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def match_type
|
|
47
|
+
match_clauses.last.type
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class ForeignKeyColumnConstraint < ColumnConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
|
|
8
|
+
attribute :name, Stringy
|
|
9
|
+
node :foreign_key_clause, ForeignKeyClause
|
|
10
|
+
|
|
11
|
+
def self.new(*, foreign_key_clause:, **) = super
|
|
12
|
+
def self.concrete(*, foreign_key_clause:, **) = super
|
|
13
|
+
|
|
14
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class ForeignKeyTableConstraint < TableConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
token :foreign_key_kw
|
|
8
|
+
token :foreign_key_lp
|
|
9
|
+
token :foreign_key_rp
|
|
10
|
+
|
|
11
|
+
attribute :name, Stringy
|
|
12
|
+
nodes :columns, IndexedColumn
|
|
13
|
+
node :foreign_key_clause, ForeignKeyClause
|
|
14
|
+
|
|
15
|
+
def self.new(*, columns:, foreign_key_clause:, **) = super
|
|
16
|
+
def self.concrete(*, foreign_key_kw:, columns:, foreign_key_clause:, **) = 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
|
+
class FrameBoundary < Node
|
|
5
|
+
Type = _Union(
|
|
6
|
+
:PRECEDING, "PRECEDING", :preceding, "preceding",
|
|
7
|
+
:FOLLOWING, "FOLLOWING", :following, "following",
|
|
8
|
+
:CURRENT_ROW, "CURRENT_ROW", :current_row, "current_row",
|
|
9
|
+
)
|
|
10
|
+
Value = _Union(
|
|
11
|
+
:UNBOUNDED, "UNBOUNDED", :unbounded, "unbounded",
|
|
12
|
+
Expression,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
token :type_tk
|
|
16
|
+
token :value_tk
|
|
17
|
+
|
|
18
|
+
attribute :type, Type
|
|
19
|
+
attribute :value, Value
|
|
20
|
+
|
|
21
|
+
def self.new(*, type:, **) = super
|
|
22
|
+
def self.concrete(*, type_tk:, **) = super
|
|
23
|
+
|
|
24
|
+
# Parser may pass either tokens or attributes, so
|
|
25
|
+
# require none in `concrete` signature but ensure presence here.
|
|
26
|
+
def after_initialize
|
|
27
|
+
if @value.nil? && @value_tk.nil?
|
|
28
|
+
raise ArgumentError.new("missing either: :value, :value_tk")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def type = (@type == LiteralNil) ? type_tk_src.gsub(" ", "_").to_sym.upcase : @type
|
|
33
|
+
def value = (@value == LiteralNil) ? value_tk_src&.to_sym&.upcase : @value
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class FrameSpec < Node
|
|
5
|
+
Type = _Union(
|
|
6
|
+
:RANGE, "RANGE", :range, "range",
|
|
7
|
+
:ROWS, "ROWS", :rows, "rows",
|
|
8
|
+
:GROUPS, "GROUPS", :groups, "groups",
|
|
9
|
+
)
|
|
10
|
+
Exclude = _Union(
|
|
11
|
+
:NO_OTHERS, "NO_OTHERS", :no_others, "no_others",
|
|
12
|
+
:CURRENT_ROW, "CURRENT_ROW", :current_row, "current_row",
|
|
13
|
+
:GROUP, "GROUP", :group, "group",
|
|
14
|
+
:TIES, "TIES", :ties, "ties",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
token :type_tk
|
|
18
|
+
token :between_kw
|
|
19
|
+
token :and_kw
|
|
20
|
+
token :exclude_kw
|
|
21
|
+
token :exclude_tk
|
|
22
|
+
|
|
23
|
+
attribute :type, Type
|
|
24
|
+
attribute :exclude, Exclude
|
|
25
|
+
node :starting_boundary, FrameBoundary
|
|
26
|
+
node :ending_boundary, FrameBoundary
|
|
27
|
+
|
|
28
|
+
def self.new(*, type:, starting_boundary:, **) = super
|
|
29
|
+
def self.concrete(*, type_tk:, starting_boundary:, **) = super
|
|
30
|
+
|
|
31
|
+
def type = (@type == LiteralNil) ? type_tk_src.to_sym.upcase : @type
|
|
32
|
+
def exclude = (@exclude == LiteralNil) ? exclude_tk_src&.gsub(" ", "_")&.to_sym&.upcase : @exclude
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class FunctionArguments < Node
|
|
5
|
+
token :distinct_kw
|
|
6
|
+
token :order_by_kw
|
|
7
|
+
|
|
8
|
+
attribute :distinct, _Boolean
|
|
9
|
+
nodes :expressions, Expression
|
|
10
|
+
nodes :order_by, OrderingTerm
|
|
11
|
+
|
|
12
|
+
def self.new(*, expressions:, **) = super
|
|
13
|
+
def self.concrete(*, expressions:, **) = super
|
|
14
|
+
|
|
15
|
+
def distinct = (@distinct == LiteralNil) ? distinct_kw? : @distinct
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class FunctionReference < Node
|
|
5
|
+
token :schema_tk
|
|
6
|
+
token :dot_tk
|
|
7
|
+
token :function_tk
|
|
8
|
+
token :arguments_lp
|
|
9
|
+
token :arguments_rp
|
|
10
|
+
token :within_group_kw
|
|
11
|
+
token :within_lp
|
|
12
|
+
token :within_rp
|
|
13
|
+
|
|
14
|
+
attribute :schema, Stringy
|
|
15
|
+
attribute :function, Stringy
|
|
16
|
+
node :arguments, _Union(FunctionArguments, StarFunctionArgument, EmptyFunctionArgument)
|
|
17
|
+
node :within_group, Expression # the ORDER BY expression of an ordered-set aggregate
|
|
18
|
+
node :filter_clause, Expression
|
|
19
|
+
node :over_clause, OverClause
|
|
20
|
+
|
|
21
|
+
def self.new(*, function:, **) = super
|
|
22
|
+
def self.concrete(*, function_tk:, **) = super
|
|
23
|
+
|
|
24
|
+
def schema = (@schema == LiteralNil) ? schema_tk_val : @schema
|
|
25
|
+
def function = (@function == LiteralNil) ? function_tk_val : @function
|
|
26
|
+
|
|
27
|
+
alias_method :name, :function
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class GeneratedAsColumnConstraint < ColumnConstraint
|
|
5
|
+
Type = _Union(:STORED, :VIRTUAL)
|
|
6
|
+
|
|
7
|
+
token :constraint_kw
|
|
8
|
+
token :name_tk
|
|
9
|
+
token :as_kw
|
|
10
|
+
token :as_lp
|
|
11
|
+
token :as_rp
|
|
12
|
+
token :type_tk
|
|
13
|
+
|
|
14
|
+
attribute :name, Stringy
|
|
15
|
+
attribute :type, Type
|
|
16
|
+
node :expression, Expression
|
|
17
|
+
|
|
18
|
+
def self.new(*, expression:, **) = super
|
|
19
|
+
def self.concrete(*, as_kw:, as_lp:, expression:, as_rp:, **) = super
|
|
20
|
+
|
|
21
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
22
|
+
def type = (@type == LiteralNil) ? type_tk_val&.to_sym&.upcase : @type
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class IndexedColumn < Node
|
|
5
|
+
token :name_tk
|
|
6
|
+
token :collate_kw
|
|
7
|
+
token :collation_tk
|
|
8
|
+
token :direction_tk
|
|
9
|
+
|
|
10
|
+
attribute :name, Stringy
|
|
11
|
+
attribute :collation, Stringy
|
|
12
|
+
attribute :direction, Stringy
|
|
13
|
+
|
|
14
|
+
def self.new(*, name:, **) = super
|
|
15
|
+
def self.concrete(*, name_tk:, **) = super
|
|
16
|
+
|
|
17
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
18
|
+
def collation = (@collation == LiteralNil) ? collation_tk_val&.to_sym&.upcase : @collation
|
|
19
|
+
def direction = (@direction == LiteralNil) ? direction_tk_val&.to_sym&.upcase : @direction
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_insert.html)**
|
|
5
|
+
#
|
|
6
|
+
# ```sql
|
|
7
|
+
# insert or ignore into t (a, b) select x, y from u on conflict (a) do nothing returning *
|
|
8
|
+
# ```
|
|
9
|
+
#
|
|
10
|
+
# Rows come from `select` (a SelectStatement, VALUES included) or are DEFAULT VALUES.
|
|
11
|
+
class InsertStatement < Node
|
|
12
|
+
include ConflictResolution
|
|
13
|
+
|
|
14
|
+
token :insert_kw # INSERT [OR …], or REPLACE
|
|
15
|
+
token :into_kw
|
|
16
|
+
token :columns_lp
|
|
17
|
+
token :columns_rp
|
|
18
|
+
token :default_values_kw
|
|
19
|
+
|
|
20
|
+
node :with_clause, WithClause
|
|
21
|
+
node :table, TableName
|
|
22
|
+
nodes :columns, ColumnName
|
|
23
|
+
node :select, SelectStatement
|
|
24
|
+
nodes :upserts, UpsertClause
|
|
25
|
+
node :returning, ReturningClause
|
|
26
|
+
|
|
27
|
+
def self.new(*, table:, **) = super
|
|
28
|
+
def self.concrete(*, insert_kw:, into_kw:, table:, **) = super
|
|
29
|
+
|
|
30
|
+
inspectable def or_action = TOKEN_TO_RESOLUTION[insert_kw_tok]
|
|
31
|
+
inspectable def default_values = default_values_kw?
|
|
32
|
+
|
|
33
|
+
def column_names = columns&.map(&:column)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class Join < Node
|
|
5
|
+
node :operator, JoinOperator
|
|
6
|
+
node :source, _Any
|
|
7
|
+
node :constraint, _Union(JoinOnConstraint, JoinUsingConstraint)
|
|
8
|
+
|
|
9
|
+
def self.new(*, operator:, source:, **) = super
|
|
10
|
+
def self.concrete(*, operator:, source:, **) = super
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# `left` followed by one or more joins. Comma-separated sources are joins too,
|
|
5
|
+
# with a `:COMMA` operator
|
|
6
|
+
class JoinClause < Node
|
|
7
|
+
node :left, _Any
|
|
8
|
+
nodes :joins, Join
|
|
9
|
+
|
|
10
|
+
def self.new(*, left:, joins:, **) = super
|
|
11
|
+
def self.concrete(*, left:, joins:, **) = super
|
|
12
|
+
|
|
13
|
+
def sources = [left, *joins.map(&:source)]
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class JoinOperator < Node
|
|
5
|
+
Kind = _Union(:COMMA, :INNER, :LEFT, :RIGHT, :FULL, :CROSS)
|
|
6
|
+
|
|
7
|
+
token :operator_tk
|
|
8
|
+
|
|
9
|
+
attribute :kind, Kind
|
|
10
|
+
attribute :natural, _Boolean
|
|
11
|
+
|
|
12
|
+
def self.new(*, kind:, **) = super
|
|
13
|
+
def self.concrete(*, operator_tk:, **) = super
|
|
14
|
+
|
|
15
|
+
def kind
|
|
16
|
+
return @kind unless @kind == LiteralNil
|
|
17
|
+
|
|
18
|
+
keywords = operator_keywords
|
|
19
|
+
return :COMMA if keywords == [:COMMA]
|
|
20
|
+
|
|
21
|
+
(keywords & [:LEFT, :RIGHT, :FULL, :CROSS]).first || :INNER
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def natural = (@natural == LiteralNil) ? operator_keywords.include?(:NATURAL) : @natural
|
|
25
|
+
def outer? = [:LEFT, :RIGHT, :FULL].include?(kind)
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def operator_keywords = Array(operator_tk_tok)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class JoinUsingConstraint < Node
|
|
5
|
+
token :using_kw
|
|
6
|
+
token :columns_lp
|
|
7
|
+
token :columns_rp
|
|
8
|
+
|
|
9
|
+
nodes :columns, ColumnName
|
|
10
|
+
|
|
11
|
+
def self.new(*, columns:, **) = super
|
|
12
|
+
def self.concrete(*, using_kw:, columns:, **) = super
|
|
13
|
+
|
|
14
|
+
def column_names = columns.map(&:column)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# `left [NOT] LIKE right [ESCAPE escape]`. GLOB, REGEXP and MATCH have no ESCAPE
|
|
5
|
+
# and are plain `BinaryExpression`s
|
|
6
|
+
class LikeExpression < Node
|
|
7
|
+
token :operator_tk
|
|
8
|
+
token :escape_kw
|
|
9
|
+
|
|
10
|
+
node :left, Expression
|
|
11
|
+
node :right, Expression
|
|
12
|
+
node :escape, Expression
|
|
13
|
+
|
|
14
|
+
def self.new(*, left:, right:, **) = super
|
|
15
|
+
def self.concrete(*, operator_tk:, left:, right:, **) = super
|
|
16
|
+
|
|
17
|
+
def operator = :LIKE
|
|
18
|
+
end
|
|
19
|
+
end
|