plume 0.1.0 → 0.3.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 (124) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +506 -11
  3. data/lib/plume/analysis/resolver.rb +797 -0
  4. data/lib/plume/analysis.rb +211 -0
  5. data/lib/plume/ast/alter_table_statement.rb +40 -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 +20 -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 +26 -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/deferrable_column_constraint.rb +20 -0
  34. data/lib/plume/ast/delete_statement.rb +28 -0
  35. data/lib/plume/ast/detach_statement.rb +13 -0
  36. data/lib/plume/ast/drop_statement.rb +21 -0
  37. data/lib/plume/ast/empty_function_argument.rb +6 -0
  38. data/lib/plume/ast/exists_expression.rb +15 -0
  39. data/lib/plume/ast/explain_statement.rb +15 -0
  40. data/lib/plume/ast/expression.rb +30 -0
  41. data/lib/plume/ast/foreign_key_action.rb +26 -0
  42. data/lib/plume/ast/foreign_key_clause.rb +50 -0
  43. data/lib/plume/ast/foreign_key_column_constraint.rb +16 -0
  44. data/lib/plume/ast/foreign_key_table_constraint.rb +20 -0
  45. data/lib/plume/ast/frame_boundary.rb +35 -0
  46. data/lib/plume/ast/frame_spec.rb +34 -0
  47. data/lib/plume/ast/function_arguments.rb +18 -0
  48. data/lib/plume/ast/function_reference.rb +29 -0
  49. data/lib/plume/ast/generated_as_column_constraint.rb +24 -0
  50. data/lib/plume/ast/indexed_column.rb +24 -0
  51. data/lib/plume/ast/insert_statement.rb +35 -0
  52. data/lib/plume/ast/join.rb +12 -0
  53. data/lib/plume/ast/join_clause.rb +15 -0
  54. data/lib/plume/ast/join_on_constraint.rb +12 -0
  55. data/lib/plume/ast/join_operator.rb +31 -0
  56. data/lib/plume/ast/join_using_constraint.rb +16 -0
  57. data/lib/plume/ast/like_expression.rb +19 -0
  58. data/lib/plume/ast/literal_expression.rb +29 -0
  59. data/lib/plume/ast/literal_false.rb +7 -0
  60. data/lib/plume/ast/literal_nil.rb +7 -0
  61. data/lib/plume/ast/literal_value.rb +18 -0
  62. data/lib/plume/ast/match_clause.rb +15 -0
  63. data/lib/plume/ast/node.rb +330 -0
  64. data/lib/plume/ast/noop_column_constraint.rb +15 -0
  65. data/lib/plume/ast/noop_table_constraint.rb +15 -0
  66. data/lib/plume/ast/not_like_expression.rb +7 -0
  67. data/lib/plume/ast/not_null_column_constraint.rb +17 -0
  68. data/lib/plume/ast/null_column_constraint.rb +17 -0
  69. data/lib/plume/ast/ordering_term.rb +32 -0
  70. data/lib/plume/ast/over_clause.rb +25 -0
  71. data/lib/plume/ast/parenthetical_expression.rb +37 -0
  72. data/lib/plume/ast/parenthetical_source.rb +19 -0
  73. data/lib/plume/ast/pragma_statement.rb +26 -0
  74. data/lib/plume/ast/primary_key_column_constraint.rb +23 -0
  75. data/lib/plume/ast/primary_key_table_constraint.rb +23 -0
  76. data/lib/plume/ast/raise_expression.rb +23 -0
  77. data/lib/plume/ast/reindex_statement.rb +15 -0
  78. data/lib/plume/ast/release_statement.rb +16 -0
  79. data/lib/plume/ast/result_column.rb +18 -0
  80. data/lib/plume/ast/returning_clause.rb +12 -0
  81. data/lib/plume/ast/rollback_statement.rb +19 -0
  82. data/lib/plume/ast/savepoint_statement.rb +16 -0
  83. data/lib/plume/ast/select_core.rb +27 -0
  84. data/lib/plume/ast/select_statement.rb +34 -0
  85. data/lib/plume/ast/star_function_argument.rb +6 -0
  86. data/lib/plume/ast/star_result_column.rb +16 -0
  87. data/lib/plume/ast/strict_table_option.rb +6 -0
  88. data/lib/plume/ast/subquery_expression.rb +14 -0
  89. data/lib/plume/ast/subquery_source.rb +19 -0
  90. data/lib/plume/ast/table_constraint.rb +7 -0
  91. data/lib/plume/ast/table_function.rb +28 -0
  92. data/lib/plume/ast/table_name.rb +28 -0
  93. data/lib/plume/ast/table_option.rb +7 -0
  94. data/lib/plume/ast/ternary_expression.rb +26 -0
  95. data/lib/plume/ast/token.rb +183 -0
  96. data/lib/plume/ast/unary_expression.rb +36 -0
  97. data/lib/plume/ast/unique_column_constraint.rb +17 -0
  98. data/lib/plume/ast/unique_table_constraint.rb +20 -0
  99. data/lib/plume/ast/update_statement.rb +35 -0
  100. data/lib/plume/ast/upsert_clause.rb +26 -0
  101. data/lib/plume/ast/vacuum_statement.rb +18 -0
  102. data/lib/plume/ast/values_clause.rb +12 -0
  103. data/lib/plume/ast/values_row.rb +13 -0
  104. data/lib/plume/ast/variable.rb +15 -0
  105. data/lib/plume/ast/window_definition.rb +26 -0
  106. data/lib/plume/ast/with_clause.rb +15 -0
  107. data/lib/plume/ast/without_rowid_table_option.rb +6 -0
  108. data/lib/plume/catalog.rb +276 -0
  109. data/lib/plume/comment.rb +73 -0
  110. data/lib/plume/dump.rb +143 -0
  111. data/lib/plume/fingerprint.rb +86 -0
  112. data/lib/plume/lexer.rb +640 -0
  113. data/lib/plume/literal_patches.rb +76 -0
  114. data/lib/plume/normalize.rb +65 -0
  115. data/lib/plume/parameters.rb +52 -0
  116. data/lib/plume/parser.rb +4064 -0
  117. data/lib/plume/pretty_printer.rb +202 -0
  118. data/lib/plume/printer.rb +525 -0
  119. data/lib/plume/sqlite_grammar.rb +86 -0
  120. data/lib/plume/truncate.rb +67 -0
  121. data/lib/plume/union.rb +28 -0
  122. data/lib/plume/version.rb +1 -1
  123. data/lib/plume.rb +274 -3
  124. metadata +176 -12
@@ -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
@@ -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,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ # `[NOT] DEFERRABLE [INITIALLY DEFERRED | INITIALLY IMMEDIATE]` on its own among a column's
5
+ # constraints, where SQLite's grammar allows it (`ccons ::= defer_subclause`)
6
+ class DeferrableColumnConstraint < ColumnConstraint
7
+ token :constraint_kw
8
+ token :name_tk
9
+ token :deferrable_span
10
+
11
+ attribute :name, Stringy
12
+ attribute :deferred, ForeignKeyClause::Deferrable
13
+
14
+ def self.new(*, **) = super
15
+ def self.concrete(*, deferrable_span:, **) = super
16
+
17
+ def name = (@name == LiteralNil) ? name_tk_val : @name
18
+ def deferred = (@deferred == LiteralNil) ? ForeignKeyClause::TOKEN_TO_DEFERRABLE[deferrable_span_tok] : @deferred
19
+ end
20
+ 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,18 @@
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
+ # ALL is the default, written out - only DISTINCT makes the aggregate distinct
16
+ def distinct = (@distinct == LiteralNil) ? (distinct_kw_tok == :DISTINCT) : @distinct
17
+ end
18
+ 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