vorax 0.4.2 → 5.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.
- 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
|