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,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plume
4
+ # Literal's union looks every value up in a Set of its plain values before trying its types,
5
+ # and from Ruby 4 on Set#include? hashes the value even when the set is empty. Hashing a node
6
+ # (or an array of them) hashes the whole subtree, so type-checking a tree as it is built would
7
+ # be quadratic. This union only looks up values that could be one of the plain ones
8
+ class Union
9
+ PLAIN = [String, Symbol, Integer, Float, Rational, Complex, TrueClass, FalseClass, NilClass].freeze
10
+
11
+ def initialize(*members)
12
+ plain, types = members.partition { |member| PLAIN.include?(member.class) }
13
+ @plain = plain.to_set.freeze
14
+ @types = types.freeze
15
+ freeze
16
+ end
17
+
18
+ def ===(value)
19
+ (PLAIN.include?(value.class) && @plain.include?(value)) || @types.any? { |type| type === value }
20
+ end
21
+
22
+ def inspect = "_Union(#{[*@plain, *@types].map(&:inspect).join(", ")})"
23
+
24
+ module Types
25
+ def _Union(*members) = Union.new(*members)
26
+ end
27
+ end
28
+ end
data/lib/plume/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Plume
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/plume.rb CHANGED
@@ -1,8 +1,279 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "plume/version"
3
+ require "plume/version"
4
+ require "literal"
5
+ require "plume/union"
6
+ require "plume/literal_patches"
4
7
 
5
8
  module Plume
6
- class Error < StandardError; end
7
- # Your code goes here...
9
+ extend Literal::Types
10
+ extend Union::Types
11
+ extend LiteralPatches::Types if LiteralPatches::DEFERRED_WRITES_ON_CHECK
12
+
13
+ # JRuby and TruffleRuby have no Ractors, so there is nothing to make shareable for
14
+ def self.make_shareable(obj) = defined?(Ractor) ? Ractor.make_shareable(obj) : obj
15
+
16
+ autoload :Lexer, "plume/lexer"
17
+ autoload :Parser, "plume/parser"
18
+ autoload :Token, "plume/ast/token"
19
+ autoload :SQLiteGrammar, "plume/sqlite_grammar"
20
+ autoload :Catalog, "plume/catalog"
21
+ autoload :Printer, "plume/printer"
22
+ autoload :PrettyPrinter, "plume/pretty_printer"
23
+ autoload :Analysis, "plume/analysis"
24
+ autoload :Parameters, "plume/parameters"
25
+ autoload :Dump, "plume/dump"
26
+ autoload :Truncate, "plume/truncate"
27
+ autoload :Comment, "plume/comment"
28
+ autoload :Comments, "plume/comment"
29
+ autoload :Normalize, "plume/normalize"
30
+ autoload :Fingerprint, "plume/fingerprint"
31
+
32
+ autoload :BinaryExpression, "plume/ast/binary_expression"
33
+ autoload :ParentheticalExpression, "plume/ast/parenthetical_expression"
34
+ autoload :CaseCondition, "plume/ast/case_condition"
35
+ autoload :CaseExpression, "plume/ast/case_expression"
36
+ autoload :CastExpression, "plume/ast/cast_expression"
37
+ autoload :CheckColumnConstraint, "plume/ast/check_column_constraint"
38
+ autoload :CheckTableConstraint, "plume/ast/check_table_constraint"
39
+ autoload :CollateColumnConstraint, "plume/ast/collate_column_constraint"
40
+ autoload :CollationExpression, "plume/ast/collation_expression"
41
+ autoload :ColumnConstraint, "plume/ast/column_constraint"
42
+ autoload :ColumnDefinition, "plume/ast/column_definition"
43
+ autoload :ColumnName, "plume/ast/column_name"
44
+ autoload :ColumnType, "plume/ast/column_type"
45
+ autoload :ConflictClause, "plume/ast/conflict_clause"
46
+ autoload :CreateTableStatement, "plume/ast/create_table_statement"
47
+ autoload :CreateViewStatement, "plume/ast/create_view_statement"
48
+ autoload :DefaultColumnConstraint, "plume/ast/default_column_constraint"
49
+ autoload :DeferrableColumnConstraint, "plume/ast/deferrable_column_constraint"
50
+ autoload :EmptyFunctionArgument, "plume/ast/empty_function_argument"
51
+ autoload :Expression, "plume/ast/expression"
52
+ autoload :ForeignKeyAction, "plume/ast/foreign_key_action"
53
+ autoload :ForeignKeyClause, "plume/ast/foreign_key_clause"
54
+ autoload :ForeignKeyColumnConstraint, "plume/ast/foreign_key_column_constraint"
55
+ autoload :ForeignKeyTableConstraint, "plume/ast/foreign_key_table_constraint"
56
+ autoload :FrameBoundary, "plume/ast/frame_boundary"
57
+ autoload :FrameSpec, "plume/ast/frame_spec"
58
+ autoload :FunctionArguments, "plume/ast/function_arguments"
59
+ autoload :FunctionReference, "plume/ast/function_reference"
60
+ autoload :GeneratedAsColumnConstraint, "plume/ast/generated_as_column_constraint"
61
+ autoload :IndexedColumn, "plume/ast/indexed_column"
62
+ autoload :JoinOnConstraint, "plume/ast/join_on_constraint"
63
+ autoload :JoinUsingConstraint, "plume/ast/join_using_constraint"
64
+ autoload :LikeExpression, "plume/ast/like_expression"
65
+ autoload :LiteralFalse, "plume/ast/literal_false"
66
+ autoload :LiteralNil, "plume/ast/literal_nil"
67
+ autoload :LiteralValue, "plume/ast/literal_value"
68
+ autoload :MatchClause, "plume/ast/match_clause"
69
+ autoload :NoOpColumnConstraint, "plume/ast/noop_column_constraint"
70
+ autoload :NoOpTableConstraint, "plume/ast/noop_table_constraint"
71
+ autoload :Node, "plume/ast/node"
72
+ autoload :NotLikeExpression, "plume/ast/not_like_expression"
73
+ autoload :NotNullColumnConstraint, "plume/ast/not_null_column_constraint"
74
+ autoload :NullColumnConstraint, "plume/ast/null_column_constraint"
75
+ autoload :OrderingTerm, "plume/ast/ordering_term"
76
+ autoload :OverClause, "plume/ast/over_clause"
77
+ autoload :PrimaryKeyColumnConstraint, "plume/ast/primary_key_column_constraint"
78
+ autoload :PrimaryKeyTableConstraint, "plume/ast/primary_key_table_constraint"
79
+ autoload :RaiseExpression, "plume/ast/raise_expression"
80
+ autoload :ResultColumn, "plume/ast/result_column"
81
+ autoload :SelectStatement, "plume/ast/select_statement"
82
+ autoload :StarFunctionArgument, "plume/ast/star_function_argument"
83
+ autoload :StarResultColumn, "plume/ast/star_result_column"
84
+ autoload :StrictTableOption, "plume/ast/strict_table_option"
85
+ autoload :TableConstraint, "plume/ast/table_constraint"
86
+ autoload :TableName, "plume/ast/table_name"
87
+ autoload :TableOption, "plume/ast/table_option"
88
+ autoload :TernaryExpression, "plume/ast/ternary_expression"
89
+ autoload :UnaryExpression, "plume/ast/unary_expression"
90
+ autoload :UniqueColumnConstraint, "plume/ast/unique_column_constraint"
91
+ autoload :UniqueTableConstraint, "plume/ast/unique_table_constraint"
92
+ autoload :LiteralExpression, "plume/ast/literal_expression"
93
+ autoload :Variable, "plume/ast/variable"
94
+ autoload :WithoutRowidTableOption, "plume/ast/without_rowid_table_option"
95
+ autoload :AlterTableStatement, "plume/ast/alter_table_statement"
96
+ autoload :AnalyzeStatement, "plume/ast/analyze_statement"
97
+ autoload :AttachStatement, "plume/ast/attach_statement"
98
+ autoload :BeginStatement, "plume/ast/begin_statement"
99
+ autoload :CommitStatement, "plume/ast/commit_statement"
100
+ autoload :CreateIndexStatement, "plume/ast/create_index_statement"
101
+ autoload :CreateTriggerStatement, "plume/ast/create_trigger_statement"
102
+ autoload :CreateVirtualTableStatement, "plume/ast/create_virtual_table_statement"
103
+ autoload :DetachStatement, "plume/ast/detach_statement"
104
+ autoload :DropStatement, "plume/ast/drop_statement"
105
+ autoload :ExplainStatement, "plume/ast/explain_statement"
106
+ autoload :PragmaStatement, "plume/ast/pragma_statement"
107
+ autoload :ReindexStatement, "plume/ast/reindex_statement"
108
+ autoload :ReleaseStatement, "plume/ast/release_statement"
109
+ autoload :RollbackStatement, "plume/ast/rollback_statement"
110
+ autoload :SavepointStatement, "plume/ast/savepoint_statement"
111
+ autoload :VacuumStatement, "plume/ast/vacuum_statement"
112
+ autoload :Assignment, "plume/ast/assignment"
113
+ autoload :ConflictResolution, "plume/ast/conflict_resolution"
114
+ autoload :DeleteStatement, "plume/ast/delete_statement"
115
+ autoload :InsertStatement, "plume/ast/insert_statement"
116
+ autoload :ReturningClause, "plume/ast/returning_clause"
117
+ autoload :UpdateStatement, "plume/ast/update_statement"
118
+ autoload :UpsertClause, "plume/ast/upsert_clause"
119
+ autoload :CommonTableExpression, "plume/ast/common_table_expression"
120
+ autoload :CompoundSelect, "plume/ast/compound_select"
121
+ autoload :ExistsExpression, "plume/ast/exists_expression"
122
+ autoload :Join, "plume/ast/join"
123
+ autoload :JoinClause, "plume/ast/join_clause"
124
+ autoload :JoinOperator, "plume/ast/join_operator"
125
+ autoload :ParentheticalSource, "plume/ast/parenthetical_source"
126
+ autoload :SelectCore, "plume/ast/select_core"
127
+ autoload :SubqueryExpression, "plume/ast/subquery_expression"
128
+ autoload :SubquerySource, "plume/ast/subquery_source"
129
+ autoload :TableFunction, "plume/ast/table_function"
130
+ autoload :ValuesClause, "plume/ast/values_clause"
131
+ autoload :ValuesRow, "plume/ast/values_row"
132
+ autoload :WindowDefinition, "plume/ast/window_definition"
133
+ autoload :WithClause, "plume/ast/with_clause"
134
+
135
+ # Where parsing gave up, for editors and linters that want more than the message:
136
+ # `location` is the byte range `[beg, fin]` of the offending token, as `Node#location` gives it,
137
+ # `token` its type (nil at the end of input), `line` counts from 1
138
+ class SyntaxError < StandardError
139
+ attr_reader :location, :token, :line, :expected
140
+
141
+ def initialize(message = nil, location: nil, token: nil, line: nil, expected: [])
142
+ @location, @token, @line, @expected = location, token, line, expected
143
+ super(message)
144
+ end
145
+
146
+ def position = location&.first
147
+ end
148
+
149
+ # What running out of stack raises. JRuby lets a Java StackOverflowError through
150
+ # when the stack runs out inside its own Java code rather than in Ruby
151
+ STACK_EXHAUSTED =
152
+ if defined?(JRUBY_VERSION)
153
+ require "java"
154
+ [SystemStackError, Java::JavaLang::StackOverflowError].freeze
155
+ else
156
+ [SystemStackError].freeze
157
+ end
158
+
159
+ # `with_positions` adds each token's byte range `beg, fin`, after the value if there is one
160
+ def self.tokenize(str, with_values: false, with_positions: false, skip_spaces: true)
161
+ lex = Lexer.new(str, skip_spaces: skip_spaces)
162
+ [].tap do |tokens|
163
+ while (token = lex.next_token)
164
+ tokens <<
165
+ if with_positions
166
+ with_values ? [token, lex.value, lex.anchor, lex.cursor] : [token, lex.anchor, lex.cursor]
167
+ else
168
+ with_values ? [token, lex.value] : token
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ # Loads everything that is otherwise autoloaded and freezes what the node classes know
175
+ # about their props, which is what lets Plume run inside Ractors. Call it from the main
176
+ # Ractor before starting any. Node classes cannot get new props afterwards
177
+ def self.eager_load!
178
+ # SQLiteGrammar is a development tool which pulls in net/http
179
+ (constants - [:SQLiteGrammar]).each { |name| const_get(name) if autoload?(name) }
180
+ Analysis::Resolver
181
+ # Node constructors and type errors reach for these, and Ruby 3.3 cannot autoload outside the main Ractor.
182
+ # Not every Literal version has all of them
183
+ %i[Null TypeError ArgumentError].each { |name| Literal.const_get(name) if Literal.const_defined?(name) }
184
+ # Literal versions that cache a `_Deferred` do so on first use, which a frozen one cannot do.
185
+ # Each node's schema then shares Expression with the rest of its prop types
186
+ Expression === nil
187
+ make_shareable(Token::Tuple)
188
+ constants.each do |name|
189
+ klass = const_get(name)
190
+ klass.make_shareable! if Class === klass && klass <= Node
191
+ end
192
+ true
193
+ end
194
+
195
+ # The statements in `sql`, found by their tokens alone, so SQL that does not parse splits too.
196
+ # Each is its text from its first token to its last, without the semicolon; `with_positions`
197
+ # gives `[text, beg, fin]` with the byte range. A trigger's body stays in one piece
198
+ def self.split(sql, with_positions: false)
199
+ lexer = Lexer.new(sql, skip_spaces: true)
200
+ statements = []
201
+ beg = fin = nil
202
+ leading = [] # the first tokens of the statement, enough to tell a CREATE TRIGGER
203
+ body = cases = 0
204
+
205
+ while (token = lexer.next_token)
206
+ if token == :SEMI && body.zero?
207
+ statements << [beg, fin] if beg
208
+ beg = nil
209
+ leading.clear
210
+ cases = 0
211
+ next
212
+ end
213
+
214
+ beg ||= lexer.anchor
215
+ fin = lexer.cursor
216
+ leading << token if leading.size < 3
217
+ trigger = leading[0] == :CREATE && (leading[1] == :TRIGGER || ((leading[1] in :TEMP | :TEMPORARY) && leading[2] == :TRIGGER))
218
+ next unless trigger
219
+
220
+ # Only END closes the body, and a CASE inside it has an END of its own
221
+ case token
222
+ when :BEGIN then body += 1 if body.zero?
223
+ when :CASE then cases += 1 if body.positive?
224
+ when :END
225
+ if cases.positive? then cases -= 1
226
+ elsif body.positive? then body -= 1
227
+ end
228
+ end
229
+ end
230
+ statements << [beg, fin] if beg
231
+
232
+ src = sql.b
233
+ statements.map do |b, f|
234
+ text = src.byteslice(b, f - b).force_encoding(sql.encoding)
235
+ with_positions ? [text, b, f] : text
236
+ end
237
+ end
238
+
239
+ # Every comment in `sql`, in order
240
+ def self.comments(sql) = Comments.in(sql)
241
+
242
+ def self.parse(sql)
243
+ Parser.new(sql).parse
244
+ end
245
+
246
+ # The line of `sql` holding bytes `beg...fin`, with a caret underneath them
247
+ def self.excerpt(sql, beg, fin)
248
+ src = sql.b # positions are byte offsets
249
+ line_start = (beg.zero? ? nil : src.rindex("\n", beg - 1))&.+(1) || 0
250
+ line_end = src.index("\n", beg) || src.bytesize
251
+ line = src.byteslice(line_start, line_end - line_start).force_encoding(Encoding::UTF_8)
252
+ caret = (" " * (beg - line_start)) + ("^" * [fin - beg, 1].max)
253
+ " #{line}\n #{caret}"
254
+ end
255
+
256
+ # The bind parameters of one statement, given as SQL or already parsed
257
+ def self.parameters(sql)
258
+ statements = (Node === sql) ? [sql] : Array(Parser.new(sql).parse)
259
+ raise ArgumentError, "expected exactly one statement, got #{statements.size}" unless statements.size == 1
260
+
261
+ Parameters.new(statements.first)
262
+ end
263
+
264
+ # A tree from what `Node#dump` gave, or a list of them
265
+ def self.load(dump) = Dump.load(dump)
266
+ # At most `max_length` characters of `sql`, with the least telling parts left out first,
267
+ # see Plume::Truncate. A parsed tree is printed first
268
+ def self.truncate(sql, max_length) = Truncate.call((Node === sql) ? sql.to_sql : sql, max_length)
269
+ # The SQL with its constants turned into `?`, see Plume::Normalize. A parsed tree gives a tree back
270
+ def self.normalize(sql) = (Node === sql) ? Normalize.tree(sql) : Normalize.text(sql)
271
+
272
+ # The same for the same query whatever its values and spelling, see Plume::Fingerprint
273
+ def self.fingerprint(sql) = Fingerprint.of((Node === sql || Array === sql) ? sql : Parser.new(sql).parse)
274
+
275
+ def self.reads(...) = Analysis.reads(...)
276
+ def self.writes(...) = Analysis.writes(...)
277
+ def self.access(...) = Analysis.access(...)
278
+ def self.summary(...) = Analysis.summary(...)
8
279
  end
metadata CHANGED
@@ -1,18 +1,63 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plume
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Margheim
8
+ - Joel Drapper
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 2024-12-29 00:00:00.000000000 Z
11
- dependencies: []
12
- description: Plume is a Ruby library that allows you to write SQLite's dialect of
13
- SQL with structured Ruby.
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: literal
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: quickdraw
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Plume parses any statement in SQLite's grammar into a syntax tree you
56
+ can walk, change and print back to SQL, and tells you which tables and columns a
57
+ statement reads and writes.
14
58
  email:
15
59
  - stephen.margheim@gmail.com
60
+ - joel@drapper.me
16
61
  executables: []
17
62
  extensions: []
18
63
  extra_rdoc_files: []
@@ -20,14 +65,133 @@ files:
20
65
  - LICENSE.txt
21
66
  - README.md
22
67
  - lib/plume.rb
68
+ - lib/plume/analysis.rb
69
+ - lib/plume/analysis/resolver.rb
70
+ - lib/plume/ast/alter_table_statement.rb
71
+ - lib/plume/ast/analyze_statement.rb
72
+ - lib/plume/ast/assignment.rb
73
+ - lib/plume/ast/attach_statement.rb
74
+ - lib/plume/ast/begin_statement.rb
75
+ - lib/plume/ast/binary_expression.rb
76
+ - lib/plume/ast/case_condition.rb
77
+ - lib/plume/ast/case_expression.rb
78
+ - lib/plume/ast/cast_expression.rb
79
+ - lib/plume/ast/check_column_constraint.rb
80
+ - lib/plume/ast/check_table_constraint.rb
81
+ - lib/plume/ast/collate_column_constraint.rb
82
+ - lib/plume/ast/collation_expression.rb
83
+ - lib/plume/ast/column_constraint.rb
84
+ - lib/plume/ast/column_definition.rb
85
+ - lib/plume/ast/column_name.rb
86
+ - lib/plume/ast/column_type.rb
87
+ - lib/plume/ast/commit_statement.rb
88
+ - lib/plume/ast/common_table_expression.rb
89
+ - lib/plume/ast/compound_select.rb
90
+ - lib/plume/ast/conflict_clause.rb
91
+ - lib/plume/ast/conflict_resolution.rb
92
+ - lib/plume/ast/create_index_statement.rb
93
+ - lib/plume/ast/create_table_statement.rb
94
+ - lib/plume/ast/create_trigger_statement.rb
95
+ - lib/plume/ast/create_view_statement.rb
96
+ - lib/plume/ast/create_virtual_table_statement.rb
97
+ - lib/plume/ast/default_column_constraint.rb
98
+ - lib/plume/ast/deferrable_column_constraint.rb
99
+ - lib/plume/ast/delete_statement.rb
100
+ - lib/plume/ast/detach_statement.rb
101
+ - lib/plume/ast/drop_statement.rb
102
+ - lib/plume/ast/empty_function_argument.rb
103
+ - lib/plume/ast/exists_expression.rb
104
+ - lib/plume/ast/explain_statement.rb
105
+ - lib/plume/ast/expression.rb
106
+ - lib/plume/ast/foreign_key_action.rb
107
+ - lib/plume/ast/foreign_key_clause.rb
108
+ - lib/plume/ast/foreign_key_column_constraint.rb
109
+ - lib/plume/ast/foreign_key_table_constraint.rb
110
+ - lib/plume/ast/frame_boundary.rb
111
+ - lib/plume/ast/frame_spec.rb
112
+ - lib/plume/ast/function_arguments.rb
113
+ - lib/plume/ast/function_reference.rb
114
+ - lib/plume/ast/generated_as_column_constraint.rb
115
+ - lib/plume/ast/indexed_column.rb
116
+ - lib/plume/ast/insert_statement.rb
117
+ - lib/plume/ast/join.rb
118
+ - lib/plume/ast/join_clause.rb
119
+ - lib/plume/ast/join_on_constraint.rb
120
+ - lib/plume/ast/join_operator.rb
121
+ - lib/plume/ast/join_using_constraint.rb
122
+ - lib/plume/ast/like_expression.rb
123
+ - lib/plume/ast/literal_expression.rb
124
+ - lib/plume/ast/literal_false.rb
125
+ - lib/plume/ast/literal_nil.rb
126
+ - lib/plume/ast/literal_value.rb
127
+ - lib/plume/ast/match_clause.rb
128
+ - lib/plume/ast/node.rb
129
+ - lib/plume/ast/noop_column_constraint.rb
130
+ - lib/plume/ast/noop_table_constraint.rb
131
+ - lib/plume/ast/not_like_expression.rb
132
+ - lib/plume/ast/not_null_column_constraint.rb
133
+ - lib/plume/ast/null_column_constraint.rb
134
+ - lib/plume/ast/ordering_term.rb
135
+ - lib/plume/ast/over_clause.rb
136
+ - lib/plume/ast/parenthetical_expression.rb
137
+ - lib/plume/ast/parenthetical_source.rb
138
+ - lib/plume/ast/pragma_statement.rb
139
+ - lib/plume/ast/primary_key_column_constraint.rb
140
+ - lib/plume/ast/primary_key_table_constraint.rb
141
+ - lib/plume/ast/raise_expression.rb
142
+ - lib/plume/ast/reindex_statement.rb
143
+ - lib/plume/ast/release_statement.rb
144
+ - lib/plume/ast/result_column.rb
145
+ - lib/plume/ast/returning_clause.rb
146
+ - lib/plume/ast/rollback_statement.rb
147
+ - lib/plume/ast/savepoint_statement.rb
148
+ - lib/plume/ast/select_core.rb
149
+ - lib/plume/ast/select_statement.rb
150
+ - lib/plume/ast/star_function_argument.rb
151
+ - lib/plume/ast/star_result_column.rb
152
+ - lib/plume/ast/strict_table_option.rb
153
+ - lib/plume/ast/subquery_expression.rb
154
+ - lib/plume/ast/subquery_source.rb
155
+ - lib/plume/ast/table_constraint.rb
156
+ - lib/plume/ast/table_function.rb
157
+ - lib/plume/ast/table_name.rb
158
+ - lib/plume/ast/table_option.rb
159
+ - lib/plume/ast/ternary_expression.rb
160
+ - lib/plume/ast/token.rb
161
+ - lib/plume/ast/unary_expression.rb
162
+ - lib/plume/ast/unique_column_constraint.rb
163
+ - lib/plume/ast/unique_table_constraint.rb
164
+ - lib/plume/ast/update_statement.rb
165
+ - lib/plume/ast/upsert_clause.rb
166
+ - lib/plume/ast/vacuum_statement.rb
167
+ - lib/plume/ast/values_clause.rb
168
+ - lib/plume/ast/values_row.rb
169
+ - lib/plume/ast/variable.rb
170
+ - lib/plume/ast/window_definition.rb
171
+ - lib/plume/ast/with_clause.rb
172
+ - lib/plume/ast/without_rowid_table_option.rb
173
+ - lib/plume/catalog.rb
174
+ - lib/plume/comment.rb
175
+ - lib/plume/dump.rb
176
+ - lib/plume/fingerprint.rb
177
+ - lib/plume/lexer.rb
178
+ - lib/plume/literal_patches.rb
179
+ - lib/plume/normalize.rb
180
+ - lib/plume/parameters.rb
181
+ - lib/plume/parser.rb
182
+ - lib/plume/pretty_printer.rb
183
+ - lib/plume/printer.rb
184
+ - lib/plume/sqlite_grammar.rb
185
+ - lib/plume/truncate.rb
186
+ - lib/plume/union.rb
23
187
  - lib/plume/version.rb
24
- homepage: https://github.com/fractaledmind/plume
188
+ homepage: https://github.com/yippee-fun/plume
25
189
  licenses:
26
190
  - MIT
27
191
  metadata:
28
- homepage_uri: https://github.com/fractaledmind/plume
29
- source_code_uri: https://github.com/fractaledmind/plume
30
- changelog_uri: https://github.com/fractaledmind/plume/releases
192
+ homepage_uri: https://github.com/yippee-fun/plume
193
+ source_code_uri: https://github.com/yippee-fun/plume
194
+ changelog_uri: https://github.com/yippee-fun/plume/blob/main/CHANGELOG.md
31
195
  funding_uri: https://github.com/sponsors/fractaledmind
32
196
  rdoc_options: []
33
197
  require_paths:
@@ -36,14 +200,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
36
200
  requirements:
37
201
  - - ">="
38
202
  - !ruby/object:Gem::Version
39
- version: 3.1.0
203
+ version: 3.2.0
40
204
  required_rubygems_version: !ruby/object:Gem::Requirement
41
205
  requirements:
42
206
  - - ">="
43
207
  - !ruby/object:Gem::Version
44
208
  version: '0'
45
209
  requirements: []
46
- rubygems_version: 3.6.2
210
+ rubygems_version: 3.6.9
47
211
  specification_version: 4
48
- summary: Write SQLite's dialect of SQL with elegance.
212
+ summary: Parse, analyze and print SQLite's dialect of SQL
49
213
  test_files: []