plume 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +280 -10
- data/lib/plume/analysis/resolver.rb +574 -0
- data/lib/plume/analysis.rb +135 -0
- data/lib/plume/ast/alter_table_statement.rb +29 -0
- data/lib/plume/ast/analyze_statement.rb +15 -0
- data/lib/plume/ast/assignment.rb +18 -0
- data/lib/plume/ast/attach_statement.rb +17 -0
- data/lib/plume/ast/begin_statement.rb +17 -0
- data/lib/plume/ast/binary_expression.rb +71 -0
- data/lib/plume/ast/case_condition.rb +29 -0
- data/lib/plume/ast/case_expression.rb +22 -0
- data/lib/plume/ast/cast_expression.rb +16 -0
- data/lib/plume/ast/check_column_constraint.rb +20 -0
- data/lib/plume/ast/check_table_constraint.rb +19 -0
- data/lib/plume/ast/collate_column_constraint.rb +19 -0
- data/lib/plume/ast/collation_expression.rb +26 -0
- data/lib/plume/ast/column_constraint.rb +7 -0
- data/lib/plume/ast/column_definition.rb +16 -0
- data/lib/plume/ast/column_name.rb +22 -0
- data/lib/plume/ast/column_type.rb +25 -0
- data/lib/plume/ast/commit_statement.rb +13 -0
- data/lib/plume/ast/common_table_expression.rb +31 -0
- data/lib/plume/ast/compound_select.rb +30 -0
- data/lib/plume/ast/conflict_clause.rb +22 -0
- data/lib/plume/ast/conflict_resolution.rb +20 -0
- data/lib/plume/ast/create_index_statement.rb +29 -0
- data/lib/plume/ast/create_table_statement.rb +47 -0
- data/lib/plume/ast/create_trigger_statement.rb +43 -0
- data/lib/plume/ast/create_view_statement.rb +30 -0
- data/lib/plume/ast/create_virtual_table_statement.rb +28 -0
- data/lib/plume/ast/default_column_constraint.rb +29 -0
- data/lib/plume/ast/delete_statement.rb +28 -0
- data/lib/plume/ast/detach_statement.rb +13 -0
- data/lib/plume/ast/drop_statement.rb +21 -0
- data/lib/plume/ast/empty_function_argument.rb +6 -0
- data/lib/plume/ast/exists_expression.rb +15 -0
- data/lib/plume/ast/explain_statement.rb +15 -0
- data/lib/plume/ast/expression.rb +30 -0
- data/lib/plume/ast/foreign_key_action.rb +26 -0
- data/lib/plume/ast/foreign_key_clause.rb +50 -0
- data/lib/plume/ast/foreign_key_column_constraint.rb +16 -0
- data/lib/plume/ast/foreign_key_table_constraint.rb +20 -0
- data/lib/plume/ast/frame_boundary.rb +35 -0
- data/lib/plume/ast/frame_spec.rb +34 -0
- data/lib/plume/ast/function_arguments.rb +17 -0
- data/lib/plume/ast/function_reference.rb +29 -0
- data/lib/plume/ast/generated_as_column_constraint.rb +24 -0
- data/lib/plume/ast/indexed_column.rb +21 -0
- data/lib/plume/ast/insert_statement.rb +35 -0
- data/lib/plume/ast/join.rb +12 -0
- data/lib/plume/ast/join_clause.rb +15 -0
- data/lib/plume/ast/join_on_constraint.rb +12 -0
- data/lib/plume/ast/join_operator.rb +31 -0
- data/lib/plume/ast/join_using_constraint.rb +16 -0
- data/lib/plume/ast/like_expression.rb +19 -0
- data/lib/plume/ast/literal_expression.rb +29 -0
- data/lib/plume/ast/literal_false.rb +7 -0
- data/lib/plume/ast/literal_nil.rb +7 -0
- data/lib/plume/ast/literal_value.rb +18 -0
- data/lib/plume/ast/match_clause.rb +15 -0
- data/lib/plume/ast/node.rb +207 -0
- data/lib/plume/ast/noop_column_constraint.rb +15 -0
- data/lib/plume/ast/noop_table_constraint.rb +15 -0
- data/lib/plume/ast/not_like_expression.rb +7 -0
- data/lib/plume/ast/not_null_column_constraint.rb +17 -0
- data/lib/plume/ast/null_column_constraint.rb +17 -0
- data/lib/plume/ast/ordering_term.rb +32 -0
- data/lib/plume/ast/over_clause.rb +25 -0
- data/lib/plume/ast/parenthetical_expression.rb +37 -0
- data/lib/plume/ast/parenthetical_source.rb +19 -0
- data/lib/plume/ast/pragma_statement.rb +26 -0
- data/lib/plume/ast/primary_key_column_constraint.rb +23 -0
- data/lib/plume/ast/primary_key_table_constraint.rb +23 -0
- data/lib/plume/ast/raise_expression.rb +23 -0
- data/lib/plume/ast/reindex_statement.rb +15 -0
- data/lib/plume/ast/release_statement.rb +16 -0
- data/lib/plume/ast/result_column.rb +18 -0
- data/lib/plume/ast/returning_clause.rb +12 -0
- data/lib/plume/ast/rollback_statement.rb +19 -0
- data/lib/plume/ast/savepoint_statement.rb +16 -0
- data/lib/plume/ast/select_core.rb +27 -0
- data/lib/plume/ast/select_statement.rb +34 -0
- data/lib/plume/ast/star_function_argument.rb +6 -0
- data/lib/plume/ast/star_result_column.rb +16 -0
- data/lib/plume/ast/strict_table_option.rb +6 -0
- data/lib/plume/ast/subquery_expression.rb +14 -0
- data/lib/plume/ast/subquery_source.rb +19 -0
- data/lib/plume/ast/table_constraint.rb +7 -0
- data/lib/plume/ast/table_function.rb +28 -0
- data/lib/plume/ast/table_name.rb +28 -0
- data/lib/plume/ast/table_option.rb +7 -0
- data/lib/plume/ast/ternary_expression.rb +26 -0
- data/lib/plume/ast/token.rb +180 -0
- data/lib/plume/ast/unary_expression.rb +36 -0
- data/lib/plume/ast/unique_column_constraint.rb +17 -0
- data/lib/plume/ast/unique_table_constraint.rb +20 -0
- data/lib/plume/ast/update_statement.rb +35 -0
- data/lib/plume/ast/upsert_clause.rb +26 -0
- data/lib/plume/ast/vacuum_statement.rb +18 -0
- data/lib/plume/ast/values_clause.rb +12 -0
- data/lib/plume/ast/values_row.rb +13 -0
- data/lib/plume/ast/variable.rb +15 -0
- data/lib/plume/ast/window_definition.rb +26 -0
- data/lib/plume/ast/with_clause.rb +15 -0
- data/lib/plume/ast/without_rowid_table_option.rb +6 -0
- data/lib/plume/catalog.rb +180 -0
- data/lib/plume/lexer.rb +639 -0
- data/lib/plume/parser.rb +4015 -0
- data/lib/plume/printer.rb +471 -0
- data/lib/plume/sqlite_grammar.rb +86 -0
- data/lib/plume/union.rb +28 -0
- data/lib/plume/version.rb +1 -1
- data/lib/plume.rb +143 -3
- metadata +167 -12
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# A single `SELECT … FROM … WHERE … GROUP BY … HAVING … WINDOW …`, without the
|
|
5
|
+
# statement-level `WITH`, `ORDER BY` and `LIMIT`
|
|
6
|
+
class SelectCore < Node
|
|
7
|
+
token :select_kw
|
|
8
|
+
token :type_tk
|
|
9
|
+
token :from_kw
|
|
10
|
+
token :where_kw
|
|
11
|
+
token :group_by_kw
|
|
12
|
+
token :having_kw
|
|
13
|
+
token :window_kw
|
|
14
|
+
|
|
15
|
+
nodes :columns, _Union(ResultColumn, StarResultColumn)
|
|
16
|
+
node :source, _Any
|
|
17
|
+
node :condition, Expression
|
|
18
|
+
nodes :groupings, Expression
|
|
19
|
+
node :having, Expression
|
|
20
|
+
nodes :windows, WindowDefinition
|
|
21
|
+
|
|
22
|
+
def self.new(*, columns:, **) = super
|
|
23
|
+
def self.concrete(*, select_kw:, columns:, **) = super
|
|
24
|
+
|
|
25
|
+
inspectable def distinct = type_tk_tok == :DISTINCT
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_select.html)**
|
|
5
|
+
#
|
|
6
|
+
# ```sql
|
|
7
|
+
# with t as (select 1) select * from t union select 2 order by 1 limit 1
|
|
8
|
+
# ```
|
|
9
|
+
#
|
|
10
|
+
# `body` is a `SelectCore`, a `ValuesClause`, or a `CompoundSelect` of those.
|
|
11
|
+
# `ORDER BY` and `LIMIT` belong to the statement as a whole, not to its last core.
|
|
12
|
+
class SelectStatement < Node
|
|
13
|
+
token :order_by_kw
|
|
14
|
+
token :limit_kw
|
|
15
|
+
token :offset_tk # OFFSET keyword, or the comma in `LIMIT offset, limit`
|
|
16
|
+
|
|
17
|
+
node :with_clause, _Any
|
|
18
|
+
node :body, _Any
|
|
19
|
+
nodes :orderings, OrderingTerm
|
|
20
|
+
node :limit, Expression
|
|
21
|
+
node :offset, Expression
|
|
22
|
+
|
|
23
|
+
def self.new(*, body:, **) = super
|
|
24
|
+
def self.concrete(*, body:, **) = super
|
|
25
|
+
|
|
26
|
+
# Every `SelectCore` and `ValuesClause` making up the body, left to right
|
|
27
|
+
def cores
|
|
28
|
+
case body
|
|
29
|
+
when CompoundSelect then body.cores
|
|
30
|
+
else [body]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class StarResultColumn < Node
|
|
5
|
+
token :table_tk
|
|
6
|
+
token :dot_tk
|
|
7
|
+
token :star_tk
|
|
8
|
+
|
|
9
|
+
attribute :table, Stringy
|
|
10
|
+
|
|
11
|
+
def self.new(*, **) = super
|
|
12
|
+
def self.concrete(*, star_tk:, **) = super
|
|
13
|
+
|
|
14
|
+
def table = (@table == LiteralNil) ? table_tk_val : @table
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# A scalar subquery `(SELECT …)`, also the right side of `x IN (SELECT …)`
|
|
5
|
+
class SubqueryExpression < Node
|
|
6
|
+
token :select_lp
|
|
7
|
+
token :select_rp
|
|
8
|
+
|
|
9
|
+
node :select, SelectStatement
|
|
10
|
+
|
|
11
|
+
def self.new(*, select:, **) = super
|
|
12
|
+
def self.concrete(*, select:, **) = super
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# A parenthesized select used as a FROM source, like `(SELECT …) AS s`
|
|
5
|
+
class SubquerySource < Node
|
|
6
|
+
token :select_lp
|
|
7
|
+
token :select_rp
|
|
8
|
+
token :as_kw
|
|
9
|
+
token :alias_tk
|
|
10
|
+
|
|
11
|
+
node :select, SelectStatement
|
|
12
|
+
attribute :alias, Stringy
|
|
13
|
+
|
|
14
|
+
def self.new(*, select:, **) = super
|
|
15
|
+
def self.concrete(*, select:, **) = super
|
|
16
|
+
|
|
17
|
+
def alias = (@alias == LiteralNil) ? alias_tk_val : @alias
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# A table-valued function used as a FROM source, like `json_each(doc) AS j`
|
|
5
|
+
class TableFunction < Node
|
|
6
|
+
token :schema_tk
|
|
7
|
+
token :dot_tk
|
|
8
|
+
token :function_tk
|
|
9
|
+
token :arguments_lp
|
|
10
|
+
token :arguments_rp
|
|
11
|
+
token :as_kw
|
|
12
|
+
token :alias_tk
|
|
13
|
+
|
|
14
|
+
attribute :schema, Stringy
|
|
15
|
+
attribute :function, Stringy
|
|
16
|
+
nodes :arguments, Expression
|
|
17
|
+
attribute :alias, Stringy
|
|
18
|
+
|
|
19
|
+
def self.new(*, function:, **) = super
|
|
20
|
+
def self.concrete(*, function_tk:, **) = super
|
|
21
|
+
|
|
22
|
+
def schema = (@schema == LiteralNil) ? schema_tk_val : @schema
|
|
23
|
+
def function = (@function == LiteralNil) ? function_tk_val : @function
|
|
24
|
+
def alias = (@alias == LiteralNil) ? alias_tk_val : @alias
|
|
25
|
+
|
|
26
|
+
alias_method :name, :function
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class TableName < Node
|
|
5
|
+
token :schema_tk
|
|
6
|
+
token :dot_tk
|
|
7
|
+
token :table_tk
|
|
8
|
+
token :as_kw
|
|
9
|
+
token :alias_tk
|
|
10
|
+
token :indexed_kw # INDEXED BY, or NOT INDEXED
|
|
11
|
+
token :index_tk
|
|
12
|
+
|
|
13
|
+
attribute :schema, Stringy
|
|
14
|
+
attribute :table, Stringy
|
|
15
|
+
attribute :alias, Stringy
|
|
16
|
+
|
|
17
|
+
def self.new(*, table:, **) = super
|
|
18
|
+
def self.concrete(*, table_tk:, **) = super
|
|
19
|
+
|
|
20
|
+
def schema = (@schema == LiteralNil) ? schema_tk_val : @schema
|
|
21
|
+
def table = (@table == LiteralNil) ? table_tk_val : @table
|
|
22
|
+
def alias = (@alias == LiteralNil) ? alias_tk_val : @alias
|
|
23
|
+
def indexed_by = index_tk_val
|
|
24
|
+
def not_indexed = indexed_kw_tok == [:NOT, :INDEXED]
|
|
25
|
+
|
|
26
|
+
alias_method :name, :table
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# `left [NOT] BETWEEN middle AND right`
|
|
5
|
+
class TernaryExpression < Node
|
|
6
|
+
TOKEN_TO_OPERATOR = {
|
|
7
|
+
:BETWEEN => :BETWEEN,
|
|
8
|
+
[:BETWEEN].freeze => :BETWEEN,
|
|
9
|
+
[:NOT, :BETWEEN].freeze => :NOT_BETWEEN,
|
|
10
|
+
}.freeze
|
|
11
|
+
Operator = _Union(*TOKEN_TO_OPERATOR.values)
|
|
12
|
+
|
|
13
|
+
token :operator_tk
|
|
14
|
+
token :and_kw
|
|
15
|
+
|
|
16
|
+
attribute :operator, Operator
|
|
17
|
+
node :left, Expression
|
|
18
|
+
node :middle, Expression
|
|
19
|
+
node :right, Expression
|
|
20
|
+
|
|
21
|
+
def self.new(*, operator:, left:, middle:, right:, **) = super
|
|
22
|
+
def self.concrete(*, operator_tk:, left:, middle:, right:, **) = super
|
|
23
|
+
|
|
24
|
+
def operator = (@operator == LiteralNil) ? TOKEN_TO_OPERATOR[operator_tk_tok] : @operator
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
module Token
|
|
5
|
+
extend Literal::Types
|
|
6
|
+
extend self
|
|
7
|
+
|
|
8
|
+
Type = _Union(
|
|
9
|
+
:keyword,
|
|
10
|
+
:identifier,
|
|
11
|
+
:punctuation,
|
|
12
|
+
:string,
|
|
13
|
+
:numeric,
|
|
14
|
+
:blob,
|
|
15
|
+
:true,
|
|
16
|
+
:false,
|
|
17
|
+
:null,
|
|
18
|
+
:current,
|
|
19
|
+
:variable,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
Tuple = _Tuple(_Union(Symbol, _Array(Symbol)), Integer, Integer, Type)
|
|
23
|
+
|
|
24
|
+
def Keyword(arg)
|
|
25
|
+
[*arg, :keyword] if arg
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def Identifier(arg)
|
|
29
|
+
[*arg, :identifier] if arg
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def Punctuation(arg)
|
|
33
|
+
[*arg, :punctuation] if arg
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def String(arg)
|
|
37
|
+
[*arg, :string] if arg
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def Numeric(arg)
|
|
41
|
+
[*arg, :numeric] if arg
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def Blob(arg)
|
|
45
|
+
[*arg, :blob] if arg
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def True(arg)
|
|
49
|
+
[*arg, :true] if arg
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def False(arg)
|
|
53
|
+
[*arg, :false] if arg
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def Null(arg)
|
|
57
|
+
[*arg, :null] if arg
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def Current(arg)
|
|
61
|
+
[*arg, :current] if arg
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def Variable(arg)
|
|
65
|
+
[*arg, :variable] if arg
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def source(str, tup)
|
|
69
|
+
return unless str && present?(tup)
|
|
70
|
+
|
|
71
|
+
_, beg, fin, = tup
|
|
72
|
+
|
|
73
|
+
str.byteslice(beg, (fin - beg))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def value(str, tup)
|
|
77
|
+
return unless str && present?(tup)
|
|
78
|
+
|
|
79
|
+
*, type = tup
|
|
80
|
+
|
|
81
|
+
case type
|
|
82
|
+
when :null then LiteralNil
|
|
83
|
+
when :true then true
|
|
84
|
+
when :false then LiteralFalse
|
|
85
|
+
when :current then source(str, tup).to_sym.upcase
|
|
86
|
+
when :numeric then numeric_literal(str, tup)
|
|
87
|
+
when :string then string_literal(str, tup)
|
|
88
|
+
when :blob then blob_literal(str, tup)
|
|
89
|
+
when :keyword then source(str, tup)
|
|
90
|
+
when :identifier then identifier(str, tup)
|
|
91
|
+
when :punctuation then source(str, tup)
|
|
92
|
+
when :variable then source(str, tup)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def location(tup)
|
|
97
|
+
return unless present?(tup)
|
|
98
|
+
|
|
99
|
+
_, beg, fin, = tup
|
|
100
|
+
|
|
101
|
+
[beg, fin].freeze
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def token(tup)
|
|
105
|
+
return unless present?(tup)
|
|
106
|
+
|
|
107
|
+
tok, * = tup
|
|
108
|
+
|
|
109
|
+
tok
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def ===(other)
|
|
113
|
+
Tuple === other
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def present?(tup) = !(tup.nil? || LiteralNil.equal?(tup))
|
|
119
|
+
|
|
120
|
+
def numeric_literal(str, tup)
|
|
121
|
+
tok, * = tup
|
|
122
|
+
src = source(str, tup)
|
|
123
|
+
|
|
124
|
+
case tok
|
|
125
|
+
when :INTEGER, [:PLUS, :INTEGER], [:MINUS, :INTEGER]
|
|
126
|
+
integer_literal(src)
|
|
127
|
+
when :FLOAT, [:PLUS, :FLOAT], [:MINUS, :FLOAT]
|
|
128
|
+
src.to_f
|
|
129
|
+
when :QNUMBER, [:PLUS, :QNUMBER], [:MINUS, :QNUMBER]
|
|
130
|
+
(src.match?(/[.eE]/) && !src.match?(/0x/i)) ? src.delete("_").to_f : integer_literal(src)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def integer_literal(src)
|
|
135
|
+
digits = src.delete("_").delete_prefix("+")
|
|
136
|
+
negative = digits.delete_prefix!("-")
|
|
137
|
+
value = digits.match?(/\A0x/i) ? hex_literal(digits) : digits.to_i
|
|
138
|
+
negative ? -value : value
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# SQLite reads a hex literal as a 64-bit two's-complement integer, so 0xFFFFFFFFFFFFFFFF is -1
|
|
142
|
+
def hex_literal(digits)
|
|
143
|
+
value = digits.to_i(16)
|
|
144
|
+
(value >= (2**63)) ? value - (2**64) : value
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def string_literal(str, tup)
|
|
148
|
+
src = source(str, tup)
|
|
149
|
+
bytesize = src.bytesize
|
|
150
|
+
# strip the leading and trailing quotes, a quote inside is written twice
|
|
151
|
+
src.byteslice(1, bytesize - 2).gsub("''", "'")
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def blob_literal(str, tup)
|
|
155
|
+
src = source(str, tup)
|
|
156
|
+
bytesize = src.bytesize
|
|
157
|
+
# trim the leading /x'/i and trailing /'/ then decode the hexadecimal string
|
|
158
|
+
[src.byteslice(2, bytesize - 3)].pack("H*")
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def identifier(str, tup)
|
|
162
|
+
tok, * = tup
|
|
163
|
+
|
|
164
|
+
return string_literal(str, tup) if tok == :STRING
|
|
165
|
+
|
|
166
|
+
src = source(str, tup)
|
|
167
|
+
|
|
168
|
+
return src if tok != :ID
|
|
169
|
+
|
|
170
|
+
# Inside quotes, the quote character itself is written twice. Brackets have no escape
|
|
171
|
+
case src.getbyte(0)
|
|
172
|
+
when 96 then src.byteslice(1, src.bytesize - 2).gsub("``", "`")
|
|
173
|
+
when 39 then src.byteslice(1, src.bytesize - 2).gsub("''", "'")
|
|
174
|
+
when 34 then src.byteslice(1, src.bytesize - 2).gsub('""', '"')
|
|
175
|
+
when 91 then src.byteslice(1, src.bytesize - 2)
|
|
176
|
+
else src
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class UnaryExpression < Node
|
|
5
|
+
TOKEN_TO_OPERATOR = {
|
|
6
|
+
:BITNOT => :INVERT,
|
|
7
|
+
:PLUS => :IDENTITY,
|
|
8
|
+
:MINUS => :NEGATE,
|
|
9
|
+
:NOT => :NOT,
|
|
10
|
+
:ISNULL => :IS_NULL,
|
|
11
|
+
:NOTNULL => :NOT_NULL,
|
|
12
|
+
[:NOT, :NULL].freeze => :NOT_NULL,
|
|
13
|
+
}.freeze
|
|
14
|
+
Operator = _Union(*TOKEN_TO_OPERATOR.values)
|
|
15
|
+
|
|
16
|
+
token :operator_tk # required
|
|
17
|
+
token :operand_tk # required
|
|
18
|
+
|
|
19
|
+
attribute :operator, Operator # required
|
|
20
|
+
attribute :operand, Expression # required
|
|
21
|
+
|
|
22
|
+
def self.new(*, operator:, operand:, **) = super
|
|
23
|
+
def self.concrete(*, operator_tk:, **) = super
|
|
24
|
+
|
|
25
|
+
# Parser may pass either tokens or attributes, so
|
|
26
|
+
# require none in `concrete` signature but ensure presence here.
|
|
27
|
+
def after_initialize
|
|
28
|
+
if @operand.nil? && @operand_tk.nil?
|
|
29
|
+
raise ArgumentError.new("missing either: :operand, :operand_tk")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def operator = (@operator == LiteralNil) ? TOKEN_TO_OPERATOR[operator_tk_tok] : @operator
|
|
34
|
+
def operand = (@operand == LiteralNil) ? operand_tk_val : @operand
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class UniqueColumnConstraint < ColumnConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
token :unique_kw
|
|
8
|
+
|
|
9
|
+
attribute :name, Stringy
|
|
10
|
+
node :conflict_clause, ConflictClause
|
|
11
|
+
|
|
12
|
+
def self.new(*, **) = super
|
|
13
|
+
def self.concrete(*, unique_kw:, **) = super
|
|
14
|
+
|
|
15
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class UniqueTableConstraint < TableConstraint
|
|
5
|
+
token :constraint_kw
|
|
6
|
+
token :name_tk
|
|
7
|
+
token :unique_kw
|
|
8
|
+
token :unique_lp
|
|
9
|
+
token :unique_rp
|
|
10
|
+
|
|
11
|
+
attribute :name, Stringy
|
|
12
|
+
nodes :columns, IndexedColumn
|
|
13
|
+
node :conflict_clause, ConflictClause
|
|
14
|
+
|
|
15
|
+
def self.new(*, columns:, **) = super
|
|
16
|
+
def self.concrete(*, columns:, **) = 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
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_update.html)**
|
|
5
|
+
#
|
|
6
|
+
# ```sql
|
|
7
|
+
# update or ignore t as x set a = u.a from u where u.id = x.id returning x.a
|
|
8
|
+
# ```
|
|
9
|
+
class UpdateStatement < Node
|
|
10
|
+
include ConflictResolution
|
|
11
|
+
|
|
12
|
+
token :update_kw # UPDATE [OR …]
|
|
13
|
+
token :set_kw
|
|
14
|
+
token :from_kw
|
|
15
|
+
token :where_kw
|
|
16
|
+
token :order_by_kw
|
|
17
|
+
token :limit_kw
|
|
18
|
+
token :offset_tk
|
|
19
|
+
|
|
20
|
+
node :with_clause, WithClause
|
|
21
|
+
node :table, TableName
|
|
22
|
+
nodes :assignments, Assignment
|
|
23
|
+
node :source, _Any
|
|
24
|
+
node :condition, Expression
|
|
25
|
+
node :returning, ReturningClause
|
|
26
|
+
nodes :orderings, OrderingTerm
|
|
27
|
+
node :limit, Expression
|
|
28
|
+
node :offset, Expression
|
|
29
|
+
|
|
30
|
+
def self.new(*, table:, assignments:, **) = super
|
|
31
|
+
def self.concrete(*, update_kw:, table:, assignments:, **) = super
|
|
32
|
+
|
|
33
|
+
inspectable def or_action = TOKEN_TO_RESOLUTION[update_kw_tok]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# `ON CONFLICT [(target) [WHERE …]] DO NOTHING | DO UPDATE SET … [WHERE …]`
|
|
5
|
+
class UpsertClause < Node
|
|
6
|
+
token :on_conflict_kw
|
|
7
|
+
token :target_lp
|
|
8
|
+
token :target_rp
|
|
9
|
+
token :target_where_kw
|
|
10
|
+
token :do_kw
|
|
11
|
+
token :action_kw
|
|
12
|
+
token :set_kw
|
|
13
|
+
token :where_kw
|
|
14
|
+
|
|
15
|
+
nodes :targets, OrderingTerm
|
|
16
|
+
node :target_condition, Expression
|
|
17
|
+
nodes :assignments, Assignment
|
|
18
|
+
node :condition, Expression
|
|
19
|
+
|
|
20
|
+
def self.new(*, **) = super
|
|
21
|
+
def self.concrete(*, on_conflict_kw:, action_kw:, **) = super
|
|
22
|
+
|
|
23
|
+
# :NOTHING or :UPDATE
|
|
24
|
+
inspectable def action = action_kw_tok
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# **[SQLite Docs](https://www.sqlite.org/lang_vacuum.html)**
|
|
5
|
+
class VacuumStatement < Node
|
|
6
|
+
token :vacuum_kw
|
|
7
|
+
token :schema_tk
|
|
8
|
+
token :into_kw
|
|
9
|
+
|
|
10
|
+
attribute :schema, Stringy
|
|
11
|
+
node :into, Expression
|
|
12
|
+
|
|
13
|
+
def self.new(*, **) = super
|
|
14
|
+
def self.concrete(*, vacuum_kw:, **) = super
|
|
15
|
+
|
|
16
|
+
def schema = (@schema == LiteralNil) ? schema_tk_val : @schema
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# A bind parameter: `?`, `?1`, `:name`, `@name` or `$name`
|
|
5
|
+
class Variable < Node
|
|
6
|
+
token :name_tk
|
|
7
|
+
|
|
8
|
+
attribute :name, Stringy
|
|
9
|
+
|
|
10
|
+
def self.new(*, name:, **) = super
|
|
11
|
+
def self.concrete(*, name_tk:, **) = super
|
|
12
|
+
|
|
13
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
# A named window from the `WINDOW` clause of a select core
|
|
5
|
+
class WindowDefinition < Node
|
|
6
|
+
token :name_tk
|
|
7
|
+
token :as_kw
|
|
8
|
+
token :window_lp
|
|
9
|
+
token :base_window_name_tk
|
|
10
|
+
token :partition_by_kw
|
|
11
|
+
token :order_by_kw
|
|
12
|
+
token :window_rp
|
|
13
|
+
|
|
14
|
+
attribute :name, Stringy
|
|
15
|
+
attribute :base_window_name, Stringy
|
|
16
|
+
nodes :partitions, Expression
|
|
17
|
+
nodes :orderings, OrderingTerm
|
|
18
|
+
node :frame, FrameSpec
|
|
19
|
+
|
|
20
|
+
def self.new(*, name:, **) = super
|
|
21
|
+
def self.concrete(*, name_tk:, **) = super
|
|
22
|
+
|
|
23
|
+
def name = (@name == LiteralNil) ? name_tk_val : @name
|
|
24
|
+
def base_window_name = (@base_window_name == LiteralNil) ? base_window_name_tk_val : @base_window_name
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Plume
|
|
4
|
+
class WithClause < Node
|
|
5
|
+
token :with_kw
|
|
6
|
+
token :recursive_kw
|
|
7
|
+
|
|
8
|
+
nodes :ctes, CommonTableExpression
|
|
9
|
+
|
|
10
|
+
def self.new(*, ctes:, **) = super
|
|
11
|
+
def self.concrete(*, with_kw:, ctes:, **) = super
|
|
12
|
+
|
|
13
|
+
inspectable def recursive = recursive_kw?
|
|
14
|
+
end
|
|
15
|
+
end
|