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.
- checksums.yaml +4 -4
- data/README.md +506 -11
- data/lib/plume/analysis/resolver.rb +797 -0
- data/lib/plume/analysis.rb +211 -0
- data/lib/plume/ast/alter_table_statement.rb +40 -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 +20 -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 +26 -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/deferrable_column_constraint.rb +20 -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 +18 -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 +24 -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 +330 -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 +183 -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 +276 -0
- data/lib/plume/comment.rb +73 -0
- data/lib/plume/dump.rb +143 -0
- data/lib/plume/fingerprint.rb +86 -0
- data/lib/plume/lexer.rb +640 -0
- data/lib/plume/literal_patches.rb +76 -0
- data/lib/plume/normalize.rb +65 -0
- data/lib/plume/parameters.rb +52 -0
- data/lib/plume/parser.rb +4064 -0
- data/lib/plume/pretty_printer.rb +202 -0
- data/lib/plume/printer.rb +525 -0
- data/lib/plume/sqlite_grammar.rb +86 -0
- data/lib/plume/truncate.rb +67 -0
- data/lib/plume/union.rb +28 -0
- data/lib/plume/version.rb +1 -1
- data/lib/plume.rb +274 -3
- 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,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
|