vorax 0.4.2 → 5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +4 -29
- data/vorax.gemspec +3 -11
- metadata +4 -92
- data/.rspec +0 -1
- data/Rakefile +0 -30
- data/lib/vorax.rb +0 -60
- data/lib/vorax/base_funnel.rb +0 -30
- data/lib/vorax/output/html_convertor.rb +0 -120
- data/lib/vorax/output/html_funnel.rb +0 -79
- data/lib/vorax/output/pagezip_convertor.rb +0 -20
- data/lib/vorax/output/tablezip_convertor.rb +0 -22
- data/lib/vorax/output/vertical_convertor.rb +0 -53
- data/lib/vorax/output/zip_convertor.rb +0 -117
- data/lib/vorax/parser/argument.rb~ +0 -125
- data/lib/vorax/parser/conn_string.rb +0 -104
- data/lib/vorax/parser/grammars/alias.rb +0 -904
- data/lib/vorax/parser/grammars/alias.rl +0 -138
- data/lib/vorax/parser/grammars/column.rb +0 -454
- data/lib/vorax/parser/grammars/column.rl +0 -64
- data/lib/vorax/parser/grammars/common.rl +0 -107
- data/lib/vorax/parser/grammars/declare.rb +0 -9606
- data/lib/vorax/parser/grammars/declare.rl +0 -160
- data/lib/vorax/parser/grammars/for_block.rb +0 -440
- data/lib/vorax/parser/grammars/for_block.rl +0 -73
- data/lib/vorax/parser/grammars/plsql_def.rb +0 -539
- data/lib/vorax/parser/grammars/plsql_def.rl +0 -73
- data/lib/vorax/parser/grammars/statement.rb +0 -925
- data/lib/vorax/parser/grammars/statement.rl +0 -83
- data/lib/vorax/parser/parser.rb +0 -344
- data/lib/vorax/parser/plsql_structure.rb +0 -222
- data/lib/vorax/parser/plsql_walker.rb +0 -143
- data/lib/vorax/parser/statement_inspector.rb~ +0 -52
- data/lib/vorax/parser/stmt_inspector.rb +0 -78
- data/lib/vorax/parser/target_ref.rb +0 -110
- data/lib/vorax/sqlplus.rb +0 -273
- data/lib/vorax/version.rb +0 -7
- data/lib/vorax/vorax_io.rb +0 -70
- data/spec/column_spec.rb +0 -40
- data/spec/conn_string_spec.rb +0 -53
- data/spec/declare_spec.rb +0 -281
- data/spec/pagezip_spec.rb +0 -153
- data/spec/parser_spec.rb +0 -352
- data/spec/plsql_structure_spec.rb +0 -68
- data/spec/spec_helper.rb +0 -13
- data/spec/sql/create_objects.sql +0 -69
- data/spec/sql/dbms_crypto.spc +0 -339
- data/spec/sql/dbms_stats.spc +0 -4097
- data/spec/sql/drop_user.sql +0 -10
- data/spec/sql/muci.spc +0 -26
- data/spec/sql/setup_user.sql +0 -22
- data/spec/sql/test.fnc +0 -12
- data/spec/sql/test.pkg +0 -83
- data/spec/sqlplus_spec.rb +0 -52
- data/spec/stmt_inspector_spec.rb +0 -96
- data/spec/tablezip_spec.rb +0 -111
- data/spec/vertical_spec.rb +0 -150
@@ -1,79 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require 'nokogiri'
|
4
|
-
|
5
|
-
module Vorax
|
6
|
-
|
7
|
-
# A namespace for everything related to the sqlplus output processing.
|
8
|
-
module Output
|
9
|
-
|
10
|
-
# A special type of funnel to work with HTML convertors.
|
11
|
-
class HTMLFunnel < BaseFunnel
|
12
|
-
|
13
|
-
# Creates a new HTML funnel.
|
14
|
-
#
|
15
|
-
# @param convertor [HTMLConvertor] the convertor to be used
|
16
|
-
# to transform the HTML content.
|
17
|
-
def initialize(convertor)
|
18
|
-
@tail = ''
|
19
|
-
@parser = Nokogiri::HTML::SAX::PushParser.new(convertor)
|
20
|
-
end
|
21
|
-
|
22
|
-
# Put the html content into the funnel.
|
23
|
-
#
|
24
|
-
# @param text [String] a chunk of HTML text
|
25
|
-
def write(text)
|
26
|
-
if text && !text.empty?
|
27
|
-
@parser.write text
|
28
|
-
if @parser.document.should_spit_text?
|
29
|
-
@tail << text
|
30
|
-
# just to be sure we don't have stale text after
|
31
|
-
# the last end tag
|
32
|
-
ping
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Get the formatted text from the funnel, as it is transformed
|
38
|
-
# by the buddy convertor.
|
39
|
-
#
|
40
|
-
# @return the formatted text
|
41
|
-
def read
|
42
|
-
@parser.document.io.rewind
|
43
|
-
chunk = @parser.document.io.read
|
44
|
-
@parser.document.io.truncate(0)
|
45
|
-
@parser.document.io.seek(0)
|
46
|
-
return chunk
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
# This is a workaround. The Nokogiri pull parser doesn't spit anything
|
52
|
-
# if there's no endding tag. For example, if "<p>Text" is given, "Text" will not
|
53
|
-
# be spit because the end "</p>" is missing. In sqlplus this is common
|
54
|
-
# especially for "prompt" or "accept" commands which spit output without
|
55
|
-
# enclosing the text in any tags. The main problem is ACCEPT, where VoraX
|
56
|
-
# will wait for users input, but the prompt will not be shown which will
|
57
|
-
# make the poor user confused. The solution is to force a random tag into
|
58
|
-
# the HTML input stream so that the parser to move along.
|
59
|
-
def ping
|
60
|
-
unless @tail.empty?
|
61
|
-
# be carefull not to ping into incomplete tags
|
62
|
-
last_open_tag_position = (@tail.rindex('<') || -1)
|
63
|
-
last_close_tag_position = (@tail.rindex('>') || -1)
|
64
|
-
last_open_entity_position = (@tail.rindex('&') || -1)
|
65
|
-
last_close_entity_position = (@tail.rindex(';') || -1)
|
66
|
-
hwm = [last_close_tag_position, last_close_entity_position].max
|
67
|
-
@tail = @tail[hwm + 1 .. -1] if hwm >= 0
|
68
|
-
if last_close_tag_position >= last_open_tag_position &&
|
69
|
-
last_close_entity_position >= last_open_entity_position
|
70
|
-
@parser << "<#{HTMLConvertor.ping_tag}/>"
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
module Vorax
|
4
|
-
module Output
|
5
|
-
|
6
|
-
# A convertor used to compress every page returned by sqlplus when executing
|
7
|
-
# a query.
|
8
|
-
class PagezipConvertor < ZipConvertor
|
9
|
-
|
10
|
-
# @see ZipConvertor.should_spit?
|
11
|
-
def should_spit?(end_tag)
|
12
|
-
("table" == end_tag) ||
|
13
|
-
("th" == end_tag && rows.size > 0)
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
module Vorax
|
4
|
-
|
5
|
-
module Output
|
6
|
-
|
7
|
-
# A convertor used to compress every HTML TABLE from the
|
8
|
-
# sqlplus output.
|
9
|
-
class TablezipConvertor < ZipConvertor
|
10
|
-
|
11
|
-
# see ZipConvertor.should_spit?
|
12
|
-
def should_spit?(current_tag)
|
13
|
-
current_tag == "table"
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
|
@@ -1,53 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
module Vorax
|
3
|
-
module Output
|
4
|
-
|
5
|
-
class VerticalConvertor < HTMLConvertor
|
6
|
-
|
7
|
-
def initialize()
|
8
|
-
super()
|
9
|
-
reset_props()
|
10
|
-
end
|
11
|
-
|
12
|
-
def end_element name
|
13
|
-
super(name)
|
14
|
-
@io << "\n" if ['br', 'p'].include?(name)
|
15
|
-
if "th" == name and @collect_columns
|
16
|
-
column = @text.strip
|
17
|
-
@columns << column
|
18
|
-
@th_length = [column.length, @th_length].max
|
19
|
-
end
|
20
|
-
@values << @text.strip if "td" == name
|
21
|
-
if "tr" == name && (not @values.empty?)
|
22
|
-
@columns = Array.new(@values.size, "") if @columns.empty?
|
23
|
-
@columns.each_with_index do |column, i|
|
24
|
-
value = HTMLConvertor.ml_indent(@values[i], @th_length + 3)
|
25
|
-
@io.print("#{column.ljust(@th_length)} : #{value}\n")
|
26
|
-
end
|
27
|
-
@values.clear
|
28
|
-
@collect_columns = false
|
29
|
-
print_row_separator
|
30
|
-
end
|
31
|
-
@text.clear unless HTMLConvertor.ping_tag == name
|
32
|
-
reset_props() if name == "table"
|
33
|
-
end
|
34
|
-
|
35
|
-
def print_row_separator
|
36
|
-
@separator ||= '-' * 60
|
37
|
-
@io.print("#{'-' * 60}\n")
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def reset_props
|
43
|
-
@columns = []
|
44
|
-
@values = []
|
45
|
-
@th_length = 0
|
46
|
-
@text = ""
|
47
|
-
@collect_columns = true
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
end
|
@@ -1,117 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
module Vorax
|
4
|
-
|
5
|
-
module Output
|
6
|
-
|
7
|
-
# A special type of HTML convertor used for compressing HTML tables. This is an
|
8
|
-
# abstract class and every convertor which needs compressing utilities must
|
9
|
-
# inherit from this class.
|
10
|
-
class ZipConvertor < HTMLConvertor
|
11
|
-
|
12
|
-
attr_reader :rows, :last_close_tag, :last_open_tag
|
13
|
-
|
14
|
-
# Create a new ZipConvertor.
|
15
|
-
def initialize()
|
16
|
-
super()
|
17
|
-
reset_props()
|
18
|
-
end
|
19
|
-
|
20
|
-
# @see HTMLConvertor.start_hook
|
21
|
-
def start_hook name, attrs = []
|
22
|
-
@last_open_tag[:name] = name
|
23
|
-
@last_open_tag[:attrs] = attrs
|
24
|
-
end
|
25
|
-
|
26
|
-
# @see HTMLConvertor.end_hook
|
27
|
-
def end_hook name
|
28
|
-
@io << "\n" if ['br', 'p'].include?(name)
|
29
|
-
@record << {:text => text.strip,
|
30
|
-
:is_column => ("th" == name),
|
31
|
-
:align => get_align(@last_open_tag)} if ["td", "th"].include?(name)
|
32
|
-
if "tr" == name
|
33
|
-
@rows << @record.dup
|
34
|
-
@record.clear
|
35
|
-
end
|
36
|
-
if should_spit?(name)
|
37
|
-
spit
|
38
|
-
@rows.clear
|
39
|
-
end
|
40
|
-
text.clear unless HTMLConvertor.ping_tag == name
|
41
|
-
@last_close_tag.clear
|
42
|
-
@last_close_tag << name
|
43
|
-
reset_props() if "table" == name
|
44
|
-
end
|
45
|
-
|
46
|
-
# A method which tells if the accumulated compressed text should
|
47
|
-
# be spit or not. This is the only method which must be implemented
|
48
|
-
# into subclasses.
|
49
|
-
#
|
50
|
-
# @param name [String] the end HTML tag
|
51
|
-
def should_spit?(name)
|
52
|
-
raise RuntimeError.new "Implement me"
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
def columns_layout
|
58
|
-
layout = []
|
59
|
-
(0..@rows.first.size-1).each do |i|
|
60
|
-
width = @rows.inject(0) do |result, element|
|
61
|
-
[result, HTMLConvertor.ml_width(element[i][:text])].max
|
62
|
-
end
|
63
|
-
first_data_row = @rows.detect { |row| row[i][:is_column] == false }
|
64
|
-
align = first_data_row[i][:align] if first_data_row
|
65
|
-
layout << {:width => width, :align => (align && align == "right" ? "rjust" : "ljust")}
|
66
|
-
end
|
67
|
-
layout
|
68
|
-
end
|
69
|
-
|
70
|
-
def separator(layout)
|
71
|
-
# spit separator
|
72
|
-
layout.each_with_index do |column, i|
|
73
|
-
@io << "-" * column[:width]
|
74
|
-
@io << (i == layout.size - 1 ? "\n" : " ")
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def spit
|
79
|
-
layout = columns_layout
|
80
|
-
# spit records
|
81
|
-
@rows.each do |record|
|
82
|
-
max_height = record.inject(0) { |result, element| [result, HTMLConvertor.ml_count(element[:text])].max }
|
83
|
-
(0..max_height).each do |j|
|
84
|
-
layout.length.times do |i|
|
85
|
-
@io << "\n" if record[i][:is_column] && i == 0 && @first_spit == false
|
86
|
-
@first_spit = false
|
87
|
-
@io << HTMLConvertor.ml_segment(record[i][:text], j).send(layout[i][:align], layout[i][:width])
|
88
|
-
@io << (i == layout.size - 1 ? "\n" : " ")
|
89
|
-
separator(layout) if i == layout.size - 1 && record[i][:is_column]
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def get_align(tag)
|
96
|
-
attrs = tag[:attrs]
|
97
|
-
if attrs
|
98
|
-
align_pair = attrs.find { |pair| pair[0] == "align" }
|
99
|
-
return align_pair[1] if align_pair
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def reset_props
|
104
|
-
@record = []
|
105
|
-
@rows = []
|
106
|
-
@last_open_tag = {:name => '', :attrs => []}
|
107
|
-
@last_close_tag = ''
|
108
|
-
@first_spit = true
|
109
|
-
end
|
110
|
-
|
111
|
-
end
|
112
|
-
|
113
|
-
end
|
114
|
-
|
115
|
-
end
|
116
|
-
|
117
|
-
|
@@ -1,125 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Vorax
|
4
|
-
|
5
|
-
module Parser
|
6
|
-
|
7
|
-
# Given a statement and a position we want to see if on that position we should
|
8
|
-
# provide code completion for an argument
|
9
|
-
class Argument
|
10
|
-
|
11
|
-
def initialize(statement)
|
12
|
-
@stmt = statement
|
13
|
-
# interesting points to search within statement
|
14
|
-
@marks = [BEGIN_PLSQL_SPECIAL_QUOTING, BEGIN_QUOTING,
|
15
|
-
CLOSE_PARAN, OPEN_PARAN, ANY]
|
16
|
-
# level referes to open/close brackets
|
17
|
-
@level = 0
|
18
|
-
end
|
19
|
-
|
20
|
-
# the function/procedure which owns the argument at the
|
21
|
-
# provided position
|
22
|
-
def belongs_to(position = @stmt.length)
|
23
|
-
@belongs_to = ''
|
24
|
-
stmt = @stmt[(0...position)]
|
25
|
-
# remove all comments
|
26
|
-
stmt = Parser::Comment.new.remove_all(stmt)
|
27
|
-
stmt.reverse!
|
28
|
-
@ss = StringScanner.new(stmt)
|
29
|
-
consume
|
30
|
-
return @belongs_to
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def consume
|
36
|
-
while !@ss.eos?
|
37
|
-
@ss.skip_until(/#{@marks.join('|')}/im)
|
38
|
-
|
39
|
-
process_plsql_quoting
|
40
|
-
process_double_quotes
|
41
|
-
process_single_quotes
|
42
|
-
process_close_paran
|
43
|
-
process_open_paran
|
44
|
-
|
45
|
-
p @ss.rest
|
46
|
-
gets
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def process_plsql_quoting
|
51
|
-
# pay attention, is reveresed
|
52
|
-
if @ss.matched =~ /'\]/
|
53
|
-
@ss.skip_until(/\['q/)
|
54
|
-
p 'a intrat'
|
55
|
-
end
|
56
|
-
#@ss.skip_until(/\{'q/) if @ss.matched =~ /'\}/
|
57
|
-
#@ss.skip_until(/\('q/) if @ss.matched =~ /'\)/
|
58
|
-
#@ss.skip_until(/\>'q/) if @ss.matched =~ /'\</
|
59
|
-
end
|
60
|
-
|
61
|
-
def process_double_quotes
|
62
|
-
@ss.skip_until(/"/) if @ss.matched == '"'
|
63
|
-
end
|
64
|
-
|
65
|
-
def process_single_quotes
|
66
|
-
if @ss.matched == "'"
|
67
|
-
begin
|
68
|
-
@ss.skip_until(/\'+/)
|
69
|
-
end while (@ss.matched != "'" && !@ss.eos?)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def process_close_paran
|
74
|
-
if @ss.matched =~ /#{CLOSE_PARAN}/
|
75
|
-
@level += 1
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def process_open_paran
|
80
|
-
if @ss.matched =~ /#{OPEN_PARAN}/
|
81
|
-
if @level == 0
|
82
|
-
extract_module
|
83
|
-
else
|
84
|
-
@level -= 1
|
85
|
-
@ss.terminate if @level < 0 #give up, it's an invalid statement
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def extract_module
|
91
|
-
while !@ss.eos?
|
92
|
-
# consume leading whitspaces
|
93
|
-
@ss.scan(/\s*/)
|
94
|
-
if @ss.check(/"/) == '"'
|
95
|
-
# we have a quoted identifier
|
96
|
-
@belongs_to << @ss.scan(/"/)
|
97
|
-
@belongs_to << @ss.scan_until(/"/)
|
98
|
-
else
|
99
|
-
# unquoted identifier
|
100
|
-
@belongs_to << @ss.scan(/\S+/)
|
101
|
-
end
|
102
|
-
# consume trailing whitespaces
|
103
|
-
@ss.scan(/\s*/)
|
104
|
-
|
105
|
-
# might be a dblink
|
106
|
-
if @ss.check(/@/) == '@'
|
107
|
-
@belongs_to << @ss.scan(/@/)
|
108
|
-
next
|
109
|
-
end
|
110
|
-
|
111
|
-
# might be package or a owner
|
112
|
-
if @ss.check(/\./) == '.'
|
113
|
-
@belongs_to << @ss.scan(/\./)
|
114
|
-
next
|
115
|
-
end
|
116
|
-
@ss.terminate
|
117
|
-
end
|
118
|
-
@belongs_to.reverse!
|
119
|
-
end
|
120
|
-
|
121
|
-
end
|
122
|
-
|
123
|
-
end
|
124
|
-
|
125
|
-
end
|
@@ -1,104 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'strscan'
|
4
|
-
|
5
|
-
module Vorax
|
6
|
-
|
7
|
-
module Parser
|
8
|
-
|
9
|
-
# A class used to parse a connection string.
|
10
|
-
class ConnString
|
11
|
-
|
12
|
-
# Parse the given connection string.
|
13
|
-
#
|
14
|
-
# @return [Hash] a hash with the following keys:
|
15
|
-
#
|
16
|
-
# :user => the username,
|
17
|
-
# :password => the password,
|
18
|
-
# :db => the target database,
|
19
|
-
# :role => sysdba, sysasm or sysoper (if that's the case),
|
20
|
-
# :prompt_for => when some pieces are missing (e.g. password), the corresponding keys from above is put here
|
21
|
-
def parse(cstr)
|
22
|
-
# well known connection strings:
|
23
|
-
# - user => just the password will be prompted
|
24
|
-
# - user/pwd
|
25
|
-
# - user@db => just the password will be prompted
|
26
|
-
# - user/pwd@db [as sysdba|sysoper|sysasm]
|
27
|
-
# - /
|
28
|
-
# - / [as sysdba|sysoper|sysasm}
|
29
|
-
# - /@db => use wallet
|
30
|
-
Vorax::debug("parse connection string #{cstr.inspect}")
|
31
|
-
result = {:user => '', :password => '', :db => '', :role => '', :prompt_for => nil}
|
32
|
-
input = cstr.strip
|
33
|
-
scanner = StringScanner.new(input)
|
34
|
-
result[:user] = unquoted_scan(scanner, /[@\/]/)
|
35
|
-
has_pwd_slash = false
|
36
|
-
|
37
|
-
if scanner.matched?
|
38
|
-
# we have a @ or a / in our connection string
|
39
|
-
result[:user].chop!
|
40
|
-
if scanner.matched == "/"
|
41
|
-
has_pwd_slash = true
|
42
|
-
result[:password] = unquoted_scan(scanner, /@/)
|
43
|
-
if scanner.matched?
|
44
|
-
# there is a "@" so we know where to stop to extract the pwd
|
45
|
-
result[:password].chop!
|
46
|
-
extract_db(scanner, result)
|
47
|
-
else
|
48
|
-
# there's no "@" so assume everything to be the password
|
49
|
-
# except for the role
|
50
|
-
result[:password] = scanner.scan_until(/\z/)
|
51
|
-
extract_role(result, :password)
|
52
|
-
end
|
53
|
-
elsif scanner.matched == "@"
|
54
|
-
extract_db(scanner, result)
|
55
|
-
end
|
56
|
-
else
|
57
|
-
# we don't have a @ or a / in our connection string
|
58
|
-
result[:user] = input
|
59
|
-
end
|
60
|
-
result[:user] = strip_quotes(result[:user])
|
61
|
-
result[:password] = strip_quotes(result[:password])
|
62
|
-
if result[:user].empty? && result[:password].empty? && has_pwd_slash
|
63
|
-
# assume OS authentication
|
64
|
-
result[:prompt_for] = nil
|
65
|
-
else
|
66
|
-
if result[:user].empty?
|
67
|
-
result[:prompt_for] = :user
|
68
|
-
elsif (not result[:user].empty?) && result[:password].empty?
|
69
|
-
result[:prompt_for] = :password
|
70
|
-
end
|
71
|
-
end
|
72
|
-
result
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
def strip_quotes(text)
|
78
|
-
text.gsub(/\A"|"\z/, '')
|
79
|
-
end
|
80
|
-
|
81
|
-
def extract_db(scanner, cstr_hash)
|
82
|
-
cstr_hash[:db] = scanner.scan_until(/\z/)
|
83
|
-
extract_role(cstr_hash, :db)
|
84
|
-
end
|
85
|
-
|
86
|
-
def extract_role(cstr_hash, from_attr)
|
87
|
-
cstr_hash[from_attr].sub!(/\s+as\s+(sysdba|sysoper|sysasm)\z/i, '')
|
88
|
-
cstr_hash[:role] = $1.downcase if $1
|
89
|
-
end
|
90
|
-
|
91
|
-
def unquoted_scan(scanner, pattern)
|
92
|
-
result = ''
|
93
|
-
begin
|
94
|
-
fragment = scanner.scan_until(pattern)
|
95
|
-
result << fragment if fragment
|
96
|
-
end while fragment && result.count('"').odd? # go on if between quotes
|
97
|
-
result
|
98
|
-
end
|
99
|
-
|
100
|
-
end
|
101
|
-
|
102
|
-
end
|
103
|
-
|
104
|
-
end
|