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,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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ class ColumnConstraint < Node
5
+ # abstract
6
+ end
7
+ 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