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
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_altertable.html)**
|
|
5
|
+
#
|
|
6
|
+
# `action` is :RENAME_TABLE, :RENAME_COLUMN, :ADD_COLUMN or :DROP_COLUMN
|
|
7
|
+
class AlterTableStatement < Node
|
|
8
|
+
Action = _Union(:RENAME_TABLE, :RENAME_COLUMN, :ADD_COLUMN, :DROP_COLUMN)
|
|
9
|
+
|
|
10
|
+
token :alter_table_kw
|
|
11
|
+
token :action_kw # RENAME [COLUMN], ADD [COLUMN] or DROP [COLUMN]
|
|
12
|
+
token :column_tk
|
|
13
|
+
token :to_kw
|
|
14
|
+
token :new_name_tk
|
|
15
|
+
|
|
16
|
+
node :table, TableName
|
|
17
|
+
attribute :action, Action
|
|
18
|
+
node :column_definition, ColumnDefinition
|
|
19
|
+
attribute :column, Stringy
|
|
20
|
+
attribute :new_name, Stringy
|
|
21
|
+
|
|
22
|
+
def self.new(*, table:, action:, **) = super
|
|
23
|
+
def self.concrete(*, alter_table_kw:, table:, action:, **) = super
|
|
24
|
+
|
|
25
|
+
def action = @action
|
|
26
|
+
def column = (@column == LiteralNil) ? column_tk_val : @column
|
|
27
|
+
def new_name = (@new_name == LiteralNil) ? new_name_tk_val : @new_name
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_analyze.html)**
|
|
5
|
+
#
|
|
6
|
+
# `target` names a schema, table or index - SQLite works out which at run time
|
|
7
|
+
class AnalyzeStatement < Node
|
|
8
|
+
token :analyze_kw
|
|
9
|
+
|
|
10
|
+
node :target, TableName
|
|
11
|
+
|
|
12
|
+
def self.new(*, **) = super
|
|
13
|
+
def self.concrete(*, analyze_kw:, **) = super
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# One `col = expr` or `(a, b) = expr` from a SET list
|
|
5
|
+
class Assignment < Node
|
|
6
|
+
token :columns_lp
|
|
7
|
+
token :columns_rp
|
|
8
|
+
token :eq_tk
|
|
9
|
+
|
|
10
|
+
nodes :columns, ColumnName
|
|
11
|
+
node :value, Expression
|
|
12
|
+
|
|
13
|
+
def self.new(*, columns:, value:, **) = super
|
|
14
|
+
def self.concrete(*, columns:, value:, **) = super
|
|
15
|
+
|
|
16
|
+
def column_names = columns.map(&:column)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_attach.html)**
|
|
5
|
+
class AttachStatement < Node
|
|
6
|
+
token :attach_kw # ATTACH or ATTACH DATABASE
|
|
7
|
+
token :as_kw
|
|
8
|
+
token :key_kw
|
|
9
|
+
|
|
10
|
+
node :file, Expression
|
|
11
|
+
node :schema, Expression
|
|
12
|
+
node :key, Expression
|
|
13
|
+
|
|
14
|
+
def self.new(*, file:, schema:, **) = super
|
|
15
|
+
def self.concrete(*, attach_kw:, file:, schema:, **) = super
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_transaction.html)**
|
|
5
|
+
class BeginStatement < Node
|
|
6
|
+
token :begin_kw
|
|
7
|
+
token :type_kw
|
|
8
|
+
token :transaction_kw
|
|
9
|
+
token :name_tk
|
|
10
|
+
|
|
11
|
+
def self.new(*, **) = super
|
|
12
|
+
def self.concrete(*, begin_kw:, **) = super
|
|
13
|
+
|
|
14
|
+
# :DEFERRED, :IMMEDIATE, :EXCLUSIVE, or nil for SQLite's default (deferred)
|
|
15
|
+
inspectable def type = type_kw_tok
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class BinaryExpression < Node
|
|
5
|
+
TOKEN_TO_OPERATOR = {
|
|
6
|
+
:AND => :ALL,
|
|
7
|
+
:OR => :ANY,
|
|
8
|
+
:CONCAT => :CONCAT,
|
|
9
|
+
:PTR1 => :EXTRACT,
|
|
10
|
+
:PTR2 => :RETRIEVE,
|
|
11
|
+
:STAR => :MULTIPLY,
|
|
12
|
+
:SLASH => :DIVIDE,
|
|
13
|
+
:REM => :MODULO,
|
|
14
|
+
:PLUS => :ADD,
|
|
15
|
+
:MINUS => :SUB,
|
|
16
|
+
:BITAND => :BIT_AND,
|
|
17
|
+
:BITOR => :BIT_OR,
|
|
18
|
+
:LSHIFT => :BIT_LSHIFT,
|
|
19
|
+
:RSHIFT => :BIT_RSHIFT,
|
|
20
|
+
:LT => :BELOW,
|
|
21
|
+
:GT => :ABOVE,
|
|
22
|
+
:LE => :ATMOST,
|
|
23
|
+
:GE => :ATLEAST,
|
|
24
|
+
:EQ => :EQUALS,
|
|
25
|
+
:NE => :NOT_EQUALS,
|
|
26
|
+
:IS => :IS,
|
|
27
|
+
:GLOB => :GLOB,
|
|
28
|
+
:REGEXP => :REGEXP,
|
|
29
|
+
:MATCH => :MATCH,
|
|
30
|
+
:IN => :IN,
|
|
31
|
+
[:IS, :NOT, :DISTINCT, :FROM].freeze => :IS,
|
|
32
|
+
[:IS, :DISTINCT, :FROM].freeze => :IS_NOT,
|
|
33
|
+
[:IS, :NOT].freeze => :IS_NOT,
|
|
34
|
+
[:IS].freeze => :IS,
|
|
35
|
+
[:GLOB].freeze => :GLOB,
|
|
36
|
+
[:REGEXP].freeze => :REGEXP,
|
|
37
|
+
[:MATCH].freeze => :MATCH,
|
|
38
|
+
[:IN].freeze => :IN,
|
|
39
|
+
[:NOT, :GLOB].freeze => :NOT_GLOB,
|
|
40
|
+
[:NOT, :REGEXP].freeze => :NOT_REGEXP,
|
|
41
|
+
[:NOT, :MATCH].freeze => :NOT_MATCH,
|
|
42
|
+
[:NOT, :IN].freeze => :NOT_IN,
|
|
43
|
+
}.freeze
|
|
44
|
+
Operator = _Union(*TOKEN_TO_OPERATOR.values)
|
|
45
|
+
|
|
46
|
+
token :operator_tk # required
|
|
47
|
+
token :left_tk # required
|
|
48
|
+
token :right_tk # required
|
|
49
|
+
|
|
50
|
+
attribute :operator, Operator # required
|
|
51
|
+
attribute :left, Expression # required
|
|
52
|
+
attribute :right, Expression # required
|
|
53
|
+
|
|
54
|
+
def self.new(*, operator:, left:, right:, **) = super
|
|
55
|
+
def self.concrete(*, operator_tk:, **) = super
|
|
56
|
+
|
|
57
|
+
# Parser may pass either tokens or attributes, so
|
|
58
|
+
# require none in `concrete` signature but ensure presence here.
|
|
59
|
+
def after_initialize
|
|
60
|
+
if @left.nil? && @left_tk.nil?
|
|
61
|
+
raise ArgumentError.new("missing either: :left, :left_tk")
|
|
62
|
+
elsif @right.nil? && @right_tk.nil?
|
|
63
|
+
raise ArgumentError.new("missing either: :right, :right_tk")
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def operator = (@operator == LiteralNil) ? TOKEN_TO_OPERATOR[operator_tk_tok] : @operator
|
|
68
|
+
def left = (@left == LiteralNil) ? left_tk_val : @left
|
|
69
|
+
def right = (@right == LiteralNil) ? right_tk_val : @right
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class CaseCondition < Node
|
|
5
|
+
token :when_kw
|
|
6
|
+
token :predicate_tk
|
|
7
|
+
token :then_kw
|
|
8
|
+
token :consequence_tk
|
|
9
|
+
|
|
10
|
+
attribute :predicate, Expression
|
|
11
|
+
attribute :consequence, Expression
|
|
12
|
+
|
|
13
|
+
def self.new(*, predicate:, consequence:, **) = super
|
|
14
|
+
def self.concrete(*, when_kw:, then_kw:, **) = 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 @predicate.nil? && @predicate_tk.nil?
|
|
20
|
+
raise ArgumentError.new("missing either: :predicate, :predicate_tk")
|
|
21
|
+
elsif @consequence.nil? && @consequence_tk.nil?
|
|
22
|
+
raise ArgumentError.new("missing either: :consequence, :consequence_tk")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def predicate = (@predicate == LiteralNil) ? predicate_tk_val : @predicate
|
|
27
|
+
def consequence = (@consequence == LiteralNil) ? consequence_tk_val : @consequence
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class CaseExpression < Node
|
|
5
|
+
token :case_kw
|
|
6
|
+
token :predicate_tk
|
|
7
|
+
token :else_kw
|
|
8
|
+
token :else_clause_tk
|
|
9
|
+
token :end_kw
|
|
10
|
+
|
|
11
|
+
attribute :predicate, Expression
|
|
12
|
+
attribute :else_clause, Expression
|
|
13
|
+
|
|
14
|
+
nodes :conditions, CaseCondition
|
|
15
|
+
|
|
16
|
+
def self.new(*, conditions:, **) = super
|
|
17
|
+
def self.concrete(*, case_kw:, conditions:, end_kw:, **) = super
|
|
18
|
+
|
|
19
|
+
def predicate = (@predicate == LiteralNil) ? predicate_tk_val : @predicate
|
|
20
|
+
def else_clause = (@else_clause == LiteralNil) ? else_clause_tk_val : @else_clause
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class CastExpression < Node
|
|
5
|
+
token :cast_kw
|
|
6
|
+
token :expression_lp
|
|
7
|
+
token :as_kw
|
|
8
|
+
token :expression_rp
|
|
9
|
+
|
|
10
|
+
node :expression, Expression
|
|
11
|
+
node :as, ColumnType
|
|
12
|
+
|
|
13
|
+
def self.new(*, expression:, as:, **) = super
|
|
14
|
+
def self.concrete(*, cast_kw:, expression_lp:, expression:, as_kw:, as:, expression_rp:, **) = super
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class CheckColumnConstraint < ColumnConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
token :check_kw
|
|
8
|
+
token :check_lp
|
|
9
|
+
token :check_rp
|
|
10
|
+
|
|
11
|
+
attribute :name, Stringy
|
|
12
|
+
node :expression, Expression
|
|
13
|
+
node :conflict_clause, ConflictClause
|
|
14
|
+
|
|
15
|
+
def self.new(*, expression:, **) = super
|
|
16
|
+
def self.concrete(*, check_kw:, check_lp:, expression:, check_rp:, **) = super
|
|
17
|
+
|
|
18
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class CheckTableConstraint < TableConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
token :check_kw
|
|
8
|
+
token :check_lp
|
|
9
|
+
token :check_rp
|
|
10
|
+
|
|
11
|
+
attribute :name, Stringy
|
|
12
|
+
node :expression, Expression
|
|
13
|
+
|
|
14
|
+
def self.new(*, expression:, **) = super
|
|
15
|
+
def self.concrete(*, expression:, **) = super
|
|
16
|
+
|
|
17
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class CollateColumnConstraint < ColumnConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
token :collate_kw
|
|
8
|
+
token :collation_tk
|
|
9
|
+
|
|
10
|
+
attribute :name, Stringy
|
|
11
|
+
attribute :collation, Stringy
|
|
12
|
+
|
|
13
|
+
def self.new(*, collation:, **) = super
|
|
14
|
+
def self.concrete(*, collate_kw:, collation_tk:, **) = super
|
|
15
|
+
|
|
16
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
17
|
+
def collation = (@collation == LiteralNil) ? collation_tk_val&.to_sym&.upcase : @collation
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class CollationExpression < Node
|
|
5
|
+
token :collate_kw
|
|
6
|
+
token :expression_tk
|
|
7
|
+
token :sequence_tk
|
|
8
|
+
|
|
9
|
+
attribute :expression, Expression
|
|
10
|
+
attribute :sequence, Stringy
|
|
11
|
+
|
|
12
|
+
def self.new(*, sequence:, **) = super
|
|
13
|
+
def self.concrete(*, sequence_tk:, **) = super
|
|
14
|
+
|
|
15
|
+
# Parser may pass either tokens or attributes, so
|
|
16
|
+
# require none in `concrete` signature but ensure presence here.
|
|
17
|
+
def after_initialize
|
|
18
|
+
if @expression.nil? && @expression_tk.nil?
|
|
19
|
+
raise ArgumentError.new("missing either: :expression, :expression_tk")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def expression = (@expression == LiteralNil) ? expression_tk_val : @expression
|
|
24
|
+
def sequence = (@sequence == LiteralNil) ? sequence_tk_val.to_sym.upcase : @sequence
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class ColumnDefinition < Node
|
|
5
|
+
token :name_tk
|
|
6
|
+
|
|
7
|
+
attribute :name, Stringy
|
|
8
|
+
node :type, ColumnType
|
|
9
|
+
nodes :constraints, ColumnConstraint
|
|
10
|
+
|
|
11
|
+
def self.new(*, name:, **) = super
|
|
12
|
+
def self.concrete(*, name_tk:, **) = super
|
|
13
|
+
|
|
14
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class ColumnName < Node
|
|
5
|
+
token :schema_tk # maybe
|
|
6
|
+
token :dot1 # maybe
|
|
7
|
+
token :table_tk # maybe
|
|
8
|
+
token :dot2 # maybe
|
|
9
|
+
token :column_tk # required
|
|
10
|
+
|
|
11
|
+
attribute :schema, Stringy # maybe
|
|
12
|
+
attribute :table, Stringy # maybe
|
|
13
|
+
attribute :column, Stringy # required
|
|
14
|
+
|
|
15
|
+
def self.new(*, column:, **) = super
|
|
16
|
+
def self.concrete(*, column_tk:, **) = super
|
|
17
|
+
|
|
18
|
+
def schema = (@schema == LiteralNil) ? schema_tk_val : @schema
|
|
19
|
+
def table = (@table == LiteralNil) ? table_tk_val : @table
|
|
20
|
+
def column = (@column == LiteralNil) ? column_tk_val : @column
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class ColumnType < Node
|
|
5
|
+
token :text_span
|
|
6
|
+
|
|
7
|
+
attribute :text, Stringy
|
|
8
|
+
|
|
9
|
+
def self.new(*, text:, **) = super
|
|
10
|
+
def self.concrete(*, text_span:, **) = super
|
|
11
|
+
|
|
12
|
+
def text = (@text == LiteralNil) ? text_span_val : @text
|
|
13
|
+
|
|
14
|
+
inspectable def affinity
|
|
15
|
+
# rules found at: https://www.sqlite.org/datatype3.html#determination_of_column_affinity
|
|
16
|
+
case text
|
|
17
|
+
when /INT/i then :INTEGER
|
|
18
|
+
when /CHAR|CLOB|TEXT/i then :TEXT
|
|
19
|
+
when /BLOB/i then :BLOB
|
|
20
|
+
when /REAL|FLOA|DOUB/i then :REAL
|
|
21
|
+
else :ANY
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# COMMIT or END. **[SQLite Docs](https://www.sqlite.org/lang_transaction.html)**
|
|
5
|
+
class CommitStatement < Node
|
|
6
|
+
token :commit_kw
|
|
7
|
+
token :transaction_kw
|
|
8
|
+
token :name_tk
|
|
9
|
+
|
|
10
|
+
def self.new(*, **) = super
|
|
11
|
+
def self.concrete(*, commit_kw:, **) = super
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class CommonTableExpression < Node
|
|
5
|
+
token :name_tk
|
|
6
|
+
token :columns_lp
|
|
7
|
+
token :columns_rp
|
|
8
|
+
token :as_kw
|
|
9
|
+
token :materialized_tk
|
|
10
|
+
token :select_lp
|
|
11
|
+
token :select_rp
|
|
12
|
+
|
|
13
|
+
attribute :name, Stringy
|
|
14
|
+
nodes :columns, IndexedColumn
|
|
15
|
+
node :select, SelectStatement
|
|
16
|
+
|
|
17
|
+
def self.new(*, name:, select:, **) = super
|
|
18
|
+
def self.concrete(*, name_tk:, select:, **) = super
|
|
19
|
+
|
|
20
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
21
|
+
def column_names = columns&.map(&:name)
|
|
22
|
+
|
|
23
|
+
# true for MATERIALIZED, false for NOT MATERIALIZED, nil when left to the planner
|
|
24
|
+
inspectable def materialized
|
|
25
|
+
case materialized_tk_tok
|
|
26
|
+
when :MATERIALIZED then true
|
|
27
|
+
when [:NOT, :MATERIALIZED] then false
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# Compound operators all have the same precedence and associate to the left, so
|
|
5
|
+
# `a UNION b EXCEPT c` is `(a UNION b) EXCEPT c` and `right` is never a compound
|
|
6
|
+
class CompoundSelect < Node
|
|
7
|
+
TOKEN_TO_OPERATOR = {
|
|
8
|
+
:UNION => :UNION,
|
|
9
|
+
[:UNION, :ALL].freeze => :UNION_ALL,
|
|
10
|
+
:INTERSECT => :INTERSECT,
|
|
11
|
+
:EXCEPT => :EXCEPT,
|
|
12
|
+
}.freeze
|
|
13
|
+
Operator = _Union(*TOKEN_TO_OPERATOR.values)
|
|
14
|
+
|
|
15
|
+
token :operator_tk
|
|
16
|
+
|
|
17
|
+
attribute :operator, Operator
|
|
18
|
+
node :left, _Any
|
|
19
|
+
node :right, _Any
|
|
20
|
+
|
|
21
|
+
def self.new(*, operator:, left:, right:, **) = super
|
|
22
|
+
def self.concrete(*, operator_tk:, left:, right:, **) = super
|
|
23
|
+
|
|
24
|
+
def operator = (@operator == LiteralNil) ? TOKEN_TO_OPERATOR[operator_tk_tok] : @operator
|
|
25
|
+
|
|
26
|
+
def cores
|
|
27
|
+
[*((CompoundSelect === left) ? left.cores : [left]), right]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class ConflictClause < Node
|
|
5
|
+
RAISE_KWS = Set[:ROLLBACK, :ABORT, :FAIL].freeze
|
|
6
|
+
RESOLVE_KWS = Set[:IGNORE, :REPLACE].freeze
|
|
7
|
+
RESOLUTIONS = (RAISE_KWS + RESOLVE_KWS).freeze
|
|
8
|
+
|
|
9
|
+
token :on_conflict_kw
|
|
10
|
+
token :resolution_kw
|
|
11
|
+
|
|
12
|
+
attribute :resolution, RESOLUTIONS
|
|
13
|
+
|
|
14
|
+
def self.new(*, resolution:, **) = super
|
|
15
|
+
def self.concrete(*, on_conflict_kw:, resolution_kw:, **) = super
|
|
16
|
+
|
|
17
|
+
def resolution = (@resolution == LiteralNil) ? resolution_kw_src.to_sym.upcase : @resolution
|
|
18
|
+
|
|
19
|
+
def raise? = RAISE_KWS.include?(resolution)
|
|
20
|
+
def resolve? = RESOLVE_KWS.include?(resolution)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# Shared by INSERT and UPDATE: the `OR …` part of `INSERT OR REPLACE`, and REPLACE on its own
|
|
5
|
+
module ConflictResolution
|
|
6
|
+
TOKEN_TO_RESOLUTION = {
|
|
7
|
+
[:INSERT, :OR, :ROLLBACK].freeze => :ROLLBACK,
|
|
8
|
+
[:INSERT, :OR, :ABORT].freeze => :ABORT,
|
|
9
|
+
[:INSERT, :OR, :FAIL].freeze => :FAIL,
|
|
10
|
+
[:INSERT, :OR, :IGNORE].freeze => :IGNORE,
|
|
11
|
+
[:INSERT, :OR, :REPLACE].freeze => :REPLACE,
|
|
12
|
+
[:UPDATE, :OR, :ROLLBACK].freeze => :ROLLBACK,
|
|
13
|
+
[:UPDATE, :OR, :ABORT].freeze => :ABORT,
|
|
14
|
+
[:UPDATE, :OR, :FAIL].freeze => :FAIL,
|
|
15
|
+
[:UPDATE, :OR, :IGNORE].freeze => :IGNORE,
|
|
16
|
+
[:UPDATE, :OR, :REPLACE].freeze => :REPLACE,
|
|
17
|
+
:REPLACE => :REPLACE,
|
|
18
|
+
}.freeze
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_createindex.html)**
|
|
5
|
+
class CreateIndexStatement < Node
|
|
6
|
+
token :create_kw
|
|
7
|
+
token :unique_kw
|
|
8
|
+
token :index_kw
|
|
9
|
+
token :if_not_exists_kw
|
|
10
|
+
token :on_kw
|
|
11
|
+
token :table_tk
|
|
12
|
+
token :columns_lp
|
|
13
|
+
token :columns_rp
|
|
14
|
+
token :where_kw
|
|
15
|
+
|
|
16
|
+
node :index, TableName
|
|
17
|
+
attribute :table, Stringy
|
|
18
|
+
nodes :columns, OrderingTerm
|
|
19
|
+
node :condition, Expression
|
|
20
|
+
|
|
21
|
+
def self.new(*, index:, table:, columns:, **) = super
|
|
22
|
+
def self.concrete(*, create_kw:, index_kw:, index:, table_tk:, columns:, **) = super
|
|
23
|
+
|
|
24
|
+
def table = (@table == LiteralNil) ? table_tk_val : @table
|
|
25
|
+
|
|
26
|
+
inspectable def unique = unique_kw?
|
|
27
|
+
inspectable def if_not_exists = if_not_exists_kw?
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# - `schema_name` → `_Nilable(String)`
|
|
5
|
+
# - `table_name` → `String`
|
|
6
|
+
# - `select_statement` → `_Nilable(SelectStatement)`
|
|
7
|
+
# - `columns` → `_Array(ColumnDefinition)`
|
|
8
|
+
# - `strict` → `_Nilable(_Boolean)`
|
|
9
|
+
# - `temporary` → `_Nilable(_Boolean)`
|
|
10
|
+
# - `if_not_exists` → `_Nilable(_Boolean)`
|
|
11
|
+
# - `without_row_id` → `_Nilable(_Boolean)`
|
|
12
|
+
# - `constraints` → `_Array(TableConstraint)`
|
|
13
|
+
#
|
|
14
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_createtable.html)**
|
|
15
|
+
#
|
|
16
|
+
# ```sql
|
|
17
|
+
# create table tb0 (c0 primary key desc on conflict abort autoincrement)
|
|
18
|
+
# ```
|
|
19
|
+
class CreateTableStatement < Node
|
|
20
|
+
token :create_kw
|
|
21
|
+
token :temp_kw
|
|
22
|
+
token :table_kw
|
|
23
|
+
token :if_not_exists_kw
|
|
24
|
+
token :columns_lp
|
|
25
|
+
token :columns_rp
|
|
26
|
+
token :as_kw
|
|
27
|
+
|
|
28
|
+
attribute :temporary, _Boolean
|
|
29
|
+
attribute :if_not_exists, _Boolean
|
|
30
|
+
node :table, TableName
|
|
31
|
+
nodes :columns, ColumnDefinition
|
|
32
|
+
nodes :constraints, TableConstraint
|
|
33
|
+
nodes :options, TableOption
|
|
34
|
+
|
|
35
|
+
node :select_statement, _Any
|
|
36
|
+
|
|
37
|
+
def self.new(*, table:, **) = super
|
|
38
|
+
def self.concrete(*, create_kw:, table_kw:, **) = super
|
|
39
|
+
|
|
40
|
+
def table = (@table == LiteralNil) ? table_tk_val : @table
|
|
41
|
+
def temporary = (@temporary == LiteralNil) ? temp_kw? : @temporary
|
|
42
|
+
def if_not_exists = (@if_not_exists == LiteralNil) ? if_not_exists_kw? : @if_not_exists
|
|
43
|
+
|
|
44
|
+
inspectable def strict = (@options != LiteralNil) ? @options&.any?(StrictTableOption) : false
|
|
45
|
+
inspectable def without_row_id = (@options != LiteralNil) ? @options&.any?(WithoutRowidTableOption) : false
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_createtrigger.html)**
|
|
5
|
+
class CreateTriggerStatement < Node
|
|
6
|
+
token :create_kw
|
|
7
|
+
token :temp_kw
|
|
8
|
+
token :trigger_kw
|
|
9
|
+
token :if_not_exists_kw
|
|
10
|
+
token :time_kw # BEFORE, AFTER or INSTEAD OF
|
|
11
|
+
token :event_kw # DELETE, INSERT, UPDATE or UPDATE OF
|
|
12
|
+
token :on_kw
|
|
13
|
+
token :for_each_row_kw
|
|
14
|
+
token :when_kw
|
|
15
|
+
token :begin_kw
|
|
16
|
+
token :end_kw
|
|
17
|
+
|
|
18
|
+
node :trigger, TableName
|
|
19
|
+
nodes :columns, ColumnName
|
|
20
|
+
node :table, TableName
|
|
21
|
+
node :condition, Expression
|
|
22
|
+
nodes :statements, _Any
|
|
23
|
+
|
|
24
|
+
def self.new(*, trigger:, table:, statements:, **) = super
|
|
25
|
+
def self.concrete(*, create_kw:, trigger_kw:, trigger:, table:, statements:, **) = super
|
|
26
|
+
|
|
27
|
+
inspectable def temporary = temp_kw?
|
|
28
|
+
inspectable def if_not_exists = if_not_exists_kw?
|
|
29
|
+
|
|
30
|
+
# :BEFORE, :AFTER, :INSTEAD_OF, or nil (which SQLite treats as BEFORE)
|
|
31
|
+
inspectable def time
|
|
32
|
+
case time_kw_tok
|
|
33
|
+
when :BEFORE then :BEFORE
|
|
34
|
+
when :AFTER then :AFTER
|
|
35
|
+
when [:INSTEAD, :OF] then :INSTEAD_OF
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# :DELETE, :INSERT or :UPDATE
|
|
40
|
+
inspectable def event = Array(event_kw_tok).first
|
|
41
|
+
def column_names = columns&.map(&:column)
|
|
42
|
+
end
|
|
43
|
+
end
|