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,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,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class EmptyFunctionArgument < Node
5
+ end
6
+ 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,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class JoinOnConstraint < Node
5
+ token :on_kw
6
+
7
+ node :expression, Expression
8
+
9
+ def self.new(*, expression:, **) = super
10
+ def self.concrete(*, on_kw:, expression:, **) = super
11
+ end
12
+ 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