skylight 0.2.2 → 0.2.3
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/CHANGELOG.md +6 -0
- data/lib/skylight/config.rb +2 -0
- data/lib/skylight/instrumenter.rb +5 -9
- data/lib/skylight/messages/error.rb +3 -2
- data/lib/skylight/normalizers.rb +11 -1
- data/lib/skylight/normalizers/sql.rb +2 -3
- data/lib/skylight/util/http.rb +2 -2
- data/lib/skylight/util/logging.rb +7 -1
- data/lib/skylight/version.rb +1 -1
- data/lib/skylight/worker/collector.rb +18 -8
- data/lib/skylight/worker/server.rb +35 -6
- data/lib/sql_lexer/lexer.rb +146 -8
- data/lib/sql_lexer/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38880fd1b62fad365c461e3fb6517e979f14baa6
|
4
|
+
data.tar.gz: 9c62ce619478170a9e1b38e788bc35e61cd41b94
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0aaab6c04ff20b4fcb06da895808f9c84ae9fda59fd33a62639355b9ae5933ad417a80224c2409de46121a5dcd6fe8d45d09434fbd02d04cf7007659e6a0254
|
7
|
+
data.tar.gz: 2c7a866a02d2746130638408246c9d751a46dd7395f6ae98533081e984a7dffd3334de731a8dfa5b88a52b019af25e69c0a32f463d202f0dfa6b68ac7ffb68d8
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## 0.2.3 (December 20, 2013)
|
2
|
+
|
3
|
+
* Fix SQL lexing for comments, arrays, double-colon casting, and multiple queries
|
4
|
+
* Handle template paths from gems
|
5
|
+
* Status and exception reports for agent debugging
|
6
|
+
|
1
7
|
## 0.2.2 (December 10, 2013)
|
2
8
|
|
3
9
|
* Added support for Mongoid/Moped
|
data/lib/skylight/config.rb
CHANGED
@@ -30,6 +30,7 @@ module Skylight
|
|
30
30
|
'AGENT_SAMPLE_SIZE' => :'agent.sample',
|
31
31
|
'AGENT_SOCKFILE_PATH' => :'agent.sockfile_path',
|
32
32
|
'AGENT_STRATEGY' => :'agent.strategy',
|
33
|
+
'AGENT_MAX_MEMORY' => :'agent.max_memory',
|
33
34
|
'REPORT_HOST' => :'report.host',
|
34
35
|
'REPORT_PORT' => :'report.port',
|
35
36
|
'REPORT_SSL' => :'report.ssl',
|
@@ -49,6 +50,7 @@ module Skylight
|
|
49
50
|
:'agent.keepalive' => 60,
|
50
51
|
:'agent.interval' => 5,
|
51
52
|
:'agent.sample' => 200,
|
53
|
+
:'agent.max_memory' => 256, # MB
|
52
54
|
:'report.host' => 'agent.skylight.io'.freeze,
|
53
55
|
:'report.port' => 443,
|
54
56
|
:'report.ssl' => true,
|
@@ -79,7 +79,7 @@ module Skylight
|
|
79
79
|
self
|
80
80
|
|
81
81
|
rescue Exception => e
|
82
|
-
|
82
|
+
log_error "failed to start instrumenter; msg=%s", e.message
|
83
83
|
nil
|
84
84
|
end
|
85
85
|
|
@@ -99,7 +99,7 @@ module Skylight
|
|
99
99
|
begin
|
100
100
|
trace = Messages::Trace::Builder.new(self, endpoint, Util::Clock.micros, cat, title, desc, annot)
|
101
101
|
rescue Exception => e
|
102
|
-
|
102
|
+
log_error e.message
|
103
103
|
t { e.backtrace.join("\n") }
|
104
104
|
return
|
105
105
|
end
|
@@ -183,14 +183,10 @@ module Skylight
|
|
183
183
|
end
|
184
184
|
end
|
185
185
|
|
186
|
-
def error(
|
187
|
-
t { fmt "processing error;
|
186
|
+
def error(type, description, details=nil)
|
187
|
+
t { fmt "processing error; type=%s; description=%s", type, description }
|
188
188
|
|
189
|
-
|
190
|
-
body = Base64.encode64(body)
|
191
|
-
end
|
192
|
-
|
193
|
-
message = Skylight::Messages::Error.new(reason: reason, body: body)
|
189
|
+
message = Skylight::Messages::Error.new(type: type, description: description, details: details && details.to_json)
|
194
190
|
|
195
191
|
unless @worker.submit(message)
|
196
192
|
warn "failed to submit error to worker"
|
@@ -3,8 +3,9 @@ require 'skylight/messages/base'
|
|
3
3
|
module Skylight
|
4
4
|
module Messages
|
5
5
|
class Error < Base
|
6
|
-
required :
|
7
|
-
required :
|
6
|
+
required :type, :string, 1
|
7
|
+
required :description, :string, 2
|
8
|
+
optional :details, :string, 3
|
8
9
|
end
|
9
10
|
end
|
10
11
|
end
|
data/lib/skylight/normalizers.rb
CHANGED
@@ -60,11 +60,21 @@ module Skylight
|
|
60
60
|
return path if relative_path?(path)
|
61
61
|
|
62
62
|
root = array_find(@paths) { |p| path.start_with?(p) }
|
63
|
+
type = :project
|
64
|
+
|
65
|
+
unless root
|
66
|
+
root = array_find(Gem.path) { |p| path.start_with?(p) }
|
67
|
+
type = :gem
|
68
|
+
end
|
63
69
|
|
64
70
|
if root
|
65
71
|
start = root.size
|
66
72
|
start += 1 if path.getbyte(start) == SEPARATOR_BYTE
|
67
|
-
|
73
|
+
if type == :gem
|
74
|
+
"$GEM_PATH/#{path[start, path.size]}"
|
75
|
+
else
|
76
|
+
path[start, path.size]
|
77
|
+
end
|
68
78
|
else
|
69
79
|
annotations[:skylight_error] = ["absolute_path", path]
|
70
80
|
"Absolute Path"
|
@@ -45,9 +45,8 @@ module Skylight
|
|
45
45
|
title, sql, binds = SqlLexer::Lexer.bindify(payload[:sql], precalculated)
|
46
46
|
[ title, sql, binds, nil ]
|
47
47
|
rescue
|
48
|
-
|
49
|
-
|
50
|
-
[ nil, nil, nil, ["sql_parse", encoded.to_json] ]
|
48
|
+
error = ["sql_parse", encode(payload[:sql]), encode(payload: payload, precalculated: precalculated)]
|
49
|
+
[ nil, nil, nil, error ]
|
51
50
|
end
|
52
51
|
|
53
52
|
def encode(body)
|
data/lib/skylight/util/http.rb
CHANGED
@@ -87,8 +87,8 @@ module Skylight
|
|
87
87
|
end
|
88
88
|
|
89
89
|
def execute(req, body=nil)
|
90
|
-
t { fmt "executing HTTP request; host=%s; port=%s; body=%s",
|
91
|
-
@host, @port, body && body.bytesize }
|
90
|
+
t { fmt "executing HTTP request; host=%s; port=%s; path=%s, body=%s",
|
91
|
+
@host, @port, req.path, body && body.bytesize }
|
92
92
|
|
93
93
|
if body
|
94
94
|
body = Gzip.compress(body) if @deflate
|
@@ -35,7 +35,13 @@ module Skylight
|
|
35
35
|
log :error, msg, *args
|
36
36
|
end
|
37
37
|
|
38
|
-
alias
|
38
|
+
alias log_trace trace
|
39
|
+
alias log_debug debug
|
40
|
+
alias log_info info
|
41
|
+
alias log_warn warn
|
42
|
+
alias log_error error
|
43
|
+
|
44
|
+
alias fmt sprintf
|
39
45
|
|
40
46
|
def log(level, msg, *args)
|
41
47
|
return unless respond_to?(:config)
|
data/lib/skylight/version.rb
CHANGED
@@ -62,27 +62,37 @@ module Skylight
|
|
62
62
|
true
|
63
63
|
end
|
64
64
|
|
65
|
+
def send_status(status)
|
66
|
+
post_data(:status, status)
|
67
|
+
end
|
68
|
+
|
69
|
+
def send_exception(exception)
|
70
|
+
post_data(:exception, exception)
|
71
|
+
end
|
72
|
+
|
65
73
|
private
|
66
74
|
|
67
|
-
def
|
68
|
-
|
75
|
+
def post_data(type, data)
|
76
|
+
t { "posting data (#{type}): #{data.inspect}" }
|
77
|
+
|
78
|
+
res = @http_auth.post("/agent/#{type}?hostname=#{escape(config[:'hostname'])}", data)
|
69
79
|
|
70
80
|
# error already handled in Util::HTTP
|
71
81
|
return unless res
|
72
82
|
|
73
83
|
unless res.success?
|
74
|
-
|
75
|
-
warn "error wasn't sent successfully; status=%s", res.status
|
76
|
-
end
|
77
|
-
|
78
|
-
warn "could not fetch report session token; status=%s", res.status
|
79
|
-
return
|
84
|
+
warn "#{type} wasn't sent successfully; status=%s", res.status
|
80
85
|
end
|
81
86
|
rescue Exception => e
|
82
87
|
error "exception; msg=%s; class=%s", e.message, e.class
|
83
88
|
t { e.backtrace.join("\n") }
|
84
89
|
end
|
85
90
|
|
91
|
+
def send_error(msg)
|
92
|
+
details = msg.details ? JSON.parse(msg.details) : nil
|
93
|
+
post_data(:error, type: msg.type, description: msg.description, details: details)
|
94
|
+
end
|
95
|
+
|
86
96
|
def finish
|
87
97
|
t { fmt "collector finishing up" }
|
88
98
|
|
@@ -2,8 +2,6 @@ require 'socket'
|
|
2
2
|
|
3
3
|
module Skylight
|
4
4
|
module Worker
|
5
|
-
# TODO:
|
6
|
-
# - Shutdown if no connections for over a minute
|
7
5
|
class Server
|
8
6
|
LOCKFILE_PATH = 'SKYLIGHT_LOCKFILE_PATH'.freeze
|
9
7
|
LOCKFILE_ENV_KEY = 'SKYLIGHT_LOCKFILE_FD'.freeze
|
@@ -18,7 +16,10 @@ module Skylight
|
|
18
16
|
:config,
|
19
17
|
:keepalive,
|
20
18
|
:lockfile_path,
|
21
|
-
:sockfile_path
|
19
|
+
:sockfile_path,
|
20
|
+
:status_interval,
|
21
|
+
:last_status_update,
|
22
|
+
:max_memory
|
22
23
|
|
23
24
|
def initialize(config, lockfile, srv, lockfile_path)
|
24
25
|
unless lockfile && srv
|
@@ -37,6 +38,8 @@ module Skylight
|
|
37
38
|
@connections = {}
|
38
39
|
@lockfile_path = lockfile_path
|
39
40
|
@sockfile_path = @config[:'agent.sockfile_path']
|
41
|
+
@status_interval = 60
|
42
|
+
@max_memory = @config[:'agent.max_memory']
|
40
43
|
end
|
41
44
|
|
42
45
|
# Called from skylight.rb on require
|
@@ -132,6 +135,7 @@ module Skylight
|
|
132
135
|
now = Time.now.to_i
|
133
136
|
next_sanity_check_at = now + tick
|
134
137
|
had_client_at = now
|
138
|
+
last_status_update = now
|
135
139
|
|
136
140
|
trace "starting IO loop"
|
137
141
|
begin
|
@@ -179,9 +183,16 @@ module Skylight
|
|
179
183
|
if keepalive < now - had_client_at
|
180
184
|
info "no clients for #{keepalive} sec - shutting down"
|
181
185
|
@run = false
|
182
|
-
|
183
|
-
next_sanity_check_at
|
184
|
-
|
186
|
+
else
|
187
|
+
if next_sanity_check_at <= now
|
188
|
+
next_sanity_check_at = now + tick
|
189
|
+
sanity_check
|
190
|
+
end
|
191
|
+
|
192
|
+
if status_interval < now - last_status_update
|
193
|
+
last_status_update = now
|
194
|
+
status_check
|
195
|
+
end
|
185
196
|
end
|
186
197
|
|
187
198
|
rescue SignalException => e
|
@@ -193,9 +204,11 @@ module Skylight
|
|
193
204
|
rescue Exception => e
|
194
205
|
error "Loop exception: %s (%s)", e.message, e.class
|
195
206
|
puts e.backtrace
|
207
|
+
@collector.send_exception(class_name: e.class.name, message: e.message, backtrace: e.backtrace)
|
196
208
|
return false
|
197
209
|
rescue Object => o
|
198
210
|
error "Unknown object thrown: `%s`", o.to_s
|
211
|
+
@collector.send_exception(class_name: o.class.name)
|
199
212
|
return false
|
200
213
|
end while @run
|
201
214
|
|
@@ -301,6 +314,22 @@ module Skylight
|
|
301
314
|
raise WorkerStateError, "sockfile gone"
|
302
315
|
end
|
303
316
|
end
|
317
|
+
|
318
|
+
def status_check
|
319
|
+
memory_usage = get_memory_usage
|
320
|
+
|
321
|
+
@collector.send_status(memory: memory_usage, max_memory: max_memory)
|
322
|
+
|
323
|
+
if memory_usage > max_memory
|
324
|
+
raise WorkerStateError, "Memory limit exceeded: #{memory_usage} (max: #{max_memory})"
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def get_memory_usage
|
329
|
+
`ps -o rss= -p #{Process.pid}`.to_i / 1024
|
330
|
+
rescue Errno::ENOENT
|
331
|
+
0
|
332
|
+
end
|
304
333
|
end
|
305
334
|
end
|
306
335
|
end
|
data/lib/sql_lexer/lexer.rb
CHANGED
@@ -19,7 +19,10 @@ module SqlLexer
|
|
19
19
|
End = %Q<;|$>
|
20
20
|
|
21
21
|
InOp = %q<IN>
|
22
|
-
|
22
|
+
ArrayOp = %q<ARRAY>
|
23
|
+
ColonColonOp = %Q<::(?=[#{StartID}])>
|
24
|
+
ArrayIndexOp = %q<\\[(?:\-?\d+(?::\-?\d+)?|NULL)\\]>
|
25
|
+
SpecialOps = %Q<#{InOp}(?=[#{WS}])|#{ColonColonOp}|#{ArrayOp}|#{ArrayIndexOp}>
|
23
26
|
|
24
27
|
StartQuotedID = %Q<">
|
25
28
|
StartTickedID = %Q<`>
|
@@ -35,13 +38,14 @@ module SqlLexer
|
|
35
38
|
StartAnyId = %Q<"#{StartID}>
|
36
39
|
Placeholder = %q<\$\p{Digit}+>
|
37
40
|
|
38
|
-
AfterID = %Q<[#{WS};#{StartNonBind}]|(?:#{OpPart})|$>
|
41
|
+
AfterID = %Q<[#{WS};#{StartNonBind}]|(?:#{OpPart})|(?:#{ColonColonOp})|(?:#{ArrayIndexOp})|$>
|
39
42
|
ID = %Q<[#{StartID}][#{PartID}]*(?=#{AfterID})>
|
40
43
|
AfterOp = %Q<[#{WS}]|[#{StartAnyId}]|[#{StartBind}]|(#{StartNonBind})|$>
|
41
44
|
Op = %Q<(?:#{OpPart})+(?=#{AfterOp})>
|
42
45
|
QuotedID = %Q<#{StartQuotedID}(?:[^"]|"")*">
|
43
46
|
TickedID = %Q<#{StartTickedID}(?:[^`]|``)*`>
|
44
47
|
NonBind = %Q<#{ID}|#{Op}|#{QuotedID}|#{TickedID}|#{Placeholder}>
|
48
|
+
Type = %Q<[#{StartID}][#{PartID}]*(?:\\(\d+\\)|\\[\\])?(?=#{AfterID})>
|
45
49
|
QuotedTable = %Q<#{TickedID}|#{QuotedID}>
|
46
50
|
|
47
51
|
StringBody = %q<(?:''|\x5C'|[^'])*>
|
@@ -62,8 +66,12 @@ module SqlLexer
|
|
62
66
|
TkWS = %r<[#{WS}]+>u
|
63
67
|
TkOptWS = %r<[#{WS}]*>u
|
64
68
|
TkOp = %r<[#{OpPart}]>u
|
69
|
+
TkComment = %r<^#{OptWS}--.*$>u
|
70
|
+
TkBlockCommentStart = %r</\*>u
|
71
|
+
TkBlockCommentEnd = %r<\*/>u
|
65
72
|
TkPlaceholder = %r<#{Placeholder}>u
|
66
73
|
TkNonBind = %r<#{NonBind}>u
|
74
|
+
TkType = %r<#{Type}>u
|
67
75
|
TkQuotedTable = %r<#{QuotedTable}>iu
|
68
76
|
TkUpdateTable = %r<UPDATE#{TableNext}>iu
|
69
77
|
TkInsertTable = %r<INSERT[#{WS}]+INTO#{TableNext}>iu
|
@@ -73,6 +81,9 @@ module SqlLexer
|
|
73
81
|
TkEnd = %r<;?[#{WS}]*>u
|
74
82
|
TkBind = %r<#{String}|#{Number}|#{Literals}>u
|
75
83
|
TkIn = %r<#{InOp}>iu
|
84
|
+
TkColonColon = %r<#{ColonColonOp}>u
|
85
|
+
TkArray = %r<#{ArrayOp}>iu
|
86
|
+
TkArrayIndex = %r<#{ArrayIndexOp}>iu
|
76
87
|
TkSpecialOp = %r<#{SpecialOps}>iu
|
77
88
|
TkStartSelect = %r<SELECT(?=(?:[#{WS}]|#{OpPart}))>iu
|
78
89
|
|
@@ -86,7 +97,8 @@ module SqlLexer
|
|
86
97
|
table_name: :process_table_name,
|
87
98
|
end: :process_end,
|
88
99
|
special: :process_special,
|
89
|
-
in: :process_in
|
100
|
+
in: :process_in,
|
101
|
+
array: :process_array
|
90
102
|
}
|
91
103
|
|
92
104
|
def self.bindify(string, binds=nil)
|
@@ -158,6 +170,8 @@ module SqlLexer
|
|
158
170
|
UNKNOWN = "<unknown>".freeze
|
159
171
|
|
160
172
|
def process(binds)
|
173
|
+
process_comments
|
174
|
+
|
161
175
|
@operation = nil
|
162
176
|
@provided_binds = binds
|
163
177
|
|
@@ -201,6 +215,64 @@ module SqlLexer
|
|
201
215
|
|
202
216
|
EMPTY = "".freeze
|
203
217
|
|
218
|
+
def process_comments
|
219
|
+
# SQL treats comments as similar to whitespace
|
220
|
+
# Here we replace all comments with spaces of the same length so as to not affect binds
|
221
|
+
|
222
|
+
# Remove block comments
|
223
|
+
# SQL allows for nested comments so this takes a bit more work
|
224
|
+
while @scanner.skip_until(TkBlockCommentStart)
|
225
|
+
count = 1
|
226
|
+
pos = @scanner.pos - 2
|
227
|
+
|
228
|
+
while true
|
229
|
+
# Determine whether we close the comment or start nesting
|
230
|
+
next_open = @scanner.skip_until(TkBlockCommentStart)
|
231
|
+
@scanner.unscan if next_open
|
232
|
+
next_close = @scanner.skip_until(TkBlockCommentEnd)
|
233
|
+
@scanner.unscan if next_close
|
234
|
+
|
235
|
+
if next_open && next_open < next_close
|
236
|
+
# We're nesting
|
237
|
+
count += 1
|
238
|
+
@scanner.skip_until(TkBlockCommentStart)
|
239
|
+
else
|
240
|
+
# We're closing
|
241
|
+
count -= 1
|
242
|
+
@scanner.skip_until(TkBlockCommentEnd)
|
243
|
+
end
|
244
|
+
|
245
|
+
if count > 10_000
|
246
|
+
raise "The SQL '#{@scanner.string}' could not be parsed because of too many iterations in block comments"
|
247
|
+
end
|
248
|
+
|
249
|
+
if count == 0
|
250
|
+
# We've closed all comments
|
251
|
+
length = @scanner.pos - pos
|
252
|
+
# Dup the string if necessary so we aren't destructive to the original value
|
253
|
+
@scanner.string = @input.dup if @scanner.string == @input
|
254
|
+
# Replace the comment with spaces
|
255
|
+
@scanner.string[pos, length] = SPACE*length
|
256
|
+
break
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
@scanner.reset
|
262
|
+
|
263
|
+
# Remove single line comments
|
264
|
+
while @scanner.skip_until(TkComment)
|
265
|
+
pos = @scanner.pos
|
266
|
+
len = @scanner.matched_size
|
267
|
+
# Dup the string if necessary so we aren't destructive to the original value
|
268
|
+
@scanner.string = @input.dup if @scanner.string == @input
|
269
|
+
# Replace the comment with spaces
|
270
|
+
@scanner.string[pos-len, len] = SPACE*len
|
271
|
+
end
|
272
|
+
|
273
|
+
@scanner.reset
|
274
|
+
end
|
275
|
+
|
204
276
|
def process_begin
|
205
277
|
@scanner.skip(TkOptWS)
|
206
278
|
@state = :first_token
|
@@ -281,6 +353,17 @@ module SqlLexer
|
|
281
353
|
@scanner.skip(TkOptWS)
|
282
354
|
@scanner.skip(/\(/u)
|
283
355
|
@state = :in
|
356
|
+
elsif @scanner.skip(TkArray)
|
357
|
+
@scanner.skip(/\[/u)
|
358
|
+
@state = :array
|
359
|
+
elsif @scanner.skip(TkColonColon)
|
360
|
+
if @scanner.skip(TkType)
|
361
|
+
@state = :tokens
|
362
|
+
else
|
363
|
+
@state = :end
|
364
|
+
end
|
365
|
+
elsif @scanner.skip(TkArrayIndex)
|
366
|
+
@state = :tokens
|
284
367
|
end
|
285
368
|
end
|
286
369
|
|
@@ -315,7 +398,57 @@ module SqlLexer
|
|
315
398
|
process_tokens
|
316
399
|
end
|
317
400
|
|
318
|
-
|
401
|
+
__send__ STATE_HANDLERS[@state]
|
402
|
+
end
|
403
|
+
|
404
|
+
@binds << pos
|
405
|
+
@binds << @scanner.pos - pos
|
406
|
+
|
407
|
+
@skip_binds = false
|
408
|
+
|
409
|
+
@state = :tokens
|
410
|
+
end
|
411
|
+
|
412
|
+
def process_array
|
413
|
+
nest = 1
|
414
|
+
iterations = 0
|
415
|
+
|
416
|
+
@skip_binds = true
|
417
|
+
pos = @scanner.pos - 6
|
418
|
+
|
419
|
+
while nest > 0
|
420
|
+
iterations += 1
|
421
|
+
|
422
|
+
if iterations > 10_000
|
423
|
+
raise "The SQL '#{@scanner.string}' could not be parsed because of too many iterations in ARRAY"
|
424
|
+
end
|
425
|
+
|
426
|
+
if @debug
|
427
|
+
p "array loop"
|
428
|
+
p @state
|
429
|
+
p @scanner
|
430
|
+
end
|
431
|
+
|
432
|
+
if @scanner.skip(/\[/u)
|
433
|
+
nest += 1
|
434
|
+
elsif @scanner.skip(/\]/u)
|
435
|
+
nest -= 1
|
436
|
+
|
437
|
+
break if nest.zero?
|
438
|
+
|
439
|
+
# End of final nested array
|
440
|
+
next if @scanner.skip(/#{TkOptWS}(?=\])/u)
|
441
|
+
end
|
442
|
+
|
443
|
+
# A NULL array
|
444
|
+
next if @scanner.skip(/NULL/iu)
|
445
|
+
|
446
|
+
# Another nested array
|
447
|
+
next if @scanner.skip(/#{TkOptWS},#{TkOptWS}(?=\[)/u)
|
448
|
+
|
449
|
+
process_tokens
|
450
|
+
|
451
|
+
__send__ STATE_HANDLERS[@state]
|
319
452
|
end
|
320
453
|
|
321
454
|
@binds << pos
|
@@ -344,13 +477,18 @@ module SqlLexer
|
|
344
477
|
end
|
345
478
|
|
346
479
|
def process_end
|
347
|
-
@scanner.skip(TkEnd)
|
480
|
+
if @scanner.skip(TkEnd)
|
481
|
+
if @scanner.eos?
|
482
|
+
@state = nil
|
483
|
+
else
|
484
|
+
process_tokens
|
485
|
+
end
|
486
|
+
end
|
348
487
|
|
349
|
-
|
488
|
+
# We didn't hit EOS and we couldn't process any tokens
|
489
|
+
if @state == :end
|
350
490
|
raise "The SQL '#{@scanner.string}' could not be parsed"
|
351
491
|
end
|
352
|
-
|
353
|
-
@state = nil
|
354
492
|
end
|
355
493
|
|
356
494
|
private
|
data/lib/sql_lexer/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: skylight
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tilde, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-12-
|
11
|
+
date: 2013-12-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|