vorax 0.1.0pre

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 (57) hide show
  1. data/.gitignore +7 -0
  2. data/.rspec +1 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +45 -0
  5. data/Rakefile +30 -0
  6. data/lib/vorax/base_funnel.rb +30 -0
  7. data/lib/vorax/output/html_convertor.rb +120 -0
  8. data/lib/vorax/output/html_funnel.rb +79 -0
  9. data/lib/vorax/output/pagezip_convertor.rb +20 -0
  10. data/lib/vorax/output/tablezip_convertor.rb +22 -0
  11. data/lib/vorax/output/vertical_convertor.rb +53 -0
  12. data/lib/vorax/output/zip_convertor.rb +117 -0
  13. data/lib/vorax/parser/argument.rb~ +125 -0
  14. data/lib/vorax/parser/body_split.rb +168 -0
  15. data/lib/vorax/parser/conn_string.rb +104 -0
  16. data/lib/vorax/parser/grammars/alias.rb +912 -0
  17. data/lib/vorax/parser/grammars/alias.rl +146 -0
  18. data/lib/vorax/parser/grammars/column.rb +454 -0
  19. data/lib/vorax/parser/grammars/column.rl +64 -0
  20. data/lib/vorax/parser/grammars/common.rl +98 -0
  21. data/lib/vorax/parser/grammars/package_spec.rb +1186 -0
  22. data/lib/vorax/parser/grammars/package_spec.rl +78 -0
  23. data/lib/vorax/parser/grammars/plsql_def.rb +469 -0
  24. data/lib/vorax/parser/grammars/plsql_def.rl +59 -0
  25. data/lib/vorax/parser/grammars/statement.rb +925 -0
  26. data/lib/vorax/parser/grammars/statement.rl +83 -0
  27. data/lib/vorax/parser/parser.rb +320 -0
  28. data/lib/vorax/parser/plsql_structure.rb +158 -0
  29. data/lib/vorax/parser/plsql_walker.rb +143 -0
  30. data/lib/vorax/parser/statement_inspector.rb~ +52 -0
  31. data/lib/vorax/parser/stmt_inspector.rb +78 -0
  32. data/lib/vorax/parser/target_ref.rb +110 -0
  33. data/lib/vorax/sqlplus.rb +281 -0
  34. data/lib/vorax/version.rb +7 -0
  35. data/lib/vorax/vorax_io.rb +70 -0
  36. data/lib/vorax.rb +60 -0
  37. data/spec/column_spec.rb +40 -0
  38. data/spec/conn_string_spec.rb +53 -0
  39. data/spec/package_spec_spec.rb +48 -0
  40. data/spec/pagezip_spec.rb +153 -0
  41. data/spec/parser_spec.rb +299 -0
  42. data/spec/plsql_structure_spec.rb +44 -0
  43. data/spec/spec_helper.rb +13 -0
  44. data/spec/sql/create_objects.sql +69 -0
  45. data/spec/sql/dbms_crypto.spc +339 -0
  46. data/spec/sql/dbms_crypto.~spc +339 -0
  47. data/spec/sql/dbms_stats.spc +4097 -0
  48. data/spec/sql/drop_user.sql +10 -0
  49. data/spec/sql/muci.spc +24 -0
  50. data/spec/sql/setup_user.sql +22 -0
  51. data/spec/sql/test.pkg +67 -0
  52. data/spec/sqlplus_spec.rb +52 -0
  53. data/spec/stmt_inspector_spec.rb +84 -0
  54. data/spec/tablezip_spec.rb +111 -0
  55. data/spec/vertical_spec.rb +150 -0
  56. data/vorax.gemspec +21 -0
  57. metadata +139 -0
@@ -0,0 +1,168 @@
1
+ # encoding: utf-8
2
+ require 'strscan'
3
+
4
+ module Vorax
5
+
6
+ module Parser
7
+
8
+ class SubProgram
9
+
10
+ attr_accessor :start_pos, :declare_pos, :end_pos, :text, :level
11
+ attr_reader :name, :type
12
+
13
+ def initialize(name, type, start_pos)
14
+ @name = name
15
+ @type = type
16
+ @start_pos = start_pos
17
+ @declare_pos = nil
18
+ @end_pos = nil
19
+ @text = ''
20
+ @level = 0
21
+ end
22
+
23
+ end
24
+
25
+ class BodySplit
26
+
27
+ PLSQL_SPEC = '(?:\bpackage\b|\btype\b)'
28
+ SUBPROG = '(?:\bfunction\b|\bprocedure\b)'
29
+ BEGIN_MODULE = '(?:\bbegin\b)'
30
+ END_MODULE = '(?:\bend\b)'
31
+
32
+ def initialize
33
+ spots = [BEGIN_ML_COMMENT, BEGIN_SL_COMMENT,
34
+ BEGIN_PLSQL_SPECIAL_QUOTING, BEGIN_QUOTING,
35
+ PLSQL_SPEC, SUBPROG, BEGIN_MODULE, END_MODULE]
36
+ @marks = Regexp.new(spots.join('|'), Regexp::IGNORECASE)
37
+ @level = 0
38
+ end
39
+
40
+ def split(text)
41
+ @blocks = []
42
+ @ss = StringScanner.new(text)
43
+ @level = 0
44
+ @crr_block = nil
45
+ while !@ss.eos?
46
+ Parser::consume(@ss, :scan_until, @marks)
47
+ process_ml_comment
48
+ process_sl_comment
49
+ process_double_quotes
50
+ process_plsql_quoting
51
+ process_single_quotes
52
+ process_plsql_spec
53
+ process_subprog
54
+ process_begin
55
+ process_end
56
+ end
57
+ return @blocks
58
+ end
59
+
60
+ private
61
+
62
+ def process_ml_comment
63
+ scan_until(/\*\/\s*/) if @ss.matched =~ /#{BEGIN_ML_COMMENT}/
64
+ end
65
+
66
+ def process_sl_comment
67
+ scan_until(/\n/) if @ss.matched =~ /#{BEGIN_SL_COMMENT}/
68
+ end
69
+
70
+ def process_double_quotes
71
+ scan_until(/"/) if @ss.matched == '"'
72
+ end
73
+
74
+ def process_plsql_quoting
75
+ if @ss.matched =~ /q'\[/
76
+ scan_until(/\]'/)
77
+ elsif @ss.matched =~ /q'[{]/
78
+ scan_until(/[}]'/)
79
+ elsif @ss.matched =~ /q'[(]/
80
+ scan_until(/[)]'/)
81
+ elsif @ss.matched =~ /q'[<]/
82
+ scan_until(/[>]'/)
83
+ end
84
+ end
85
+
86
+ def process_single_quotes
87
+ if @ss.matched == "'"
88
+ begin
89
+ scan_until(/\'+/)
90
+ end while (@ss.matched != "'" && !@ss.eos?)
91
+ end
92
+ end
93
+
94
+ def process_plsql_spec
95
+ if @ss.matched =~ /#{PLSQL_SPEC}/i
96
+ meta_data = Parser.plsql_spec("#{@ss.matched}#{@ss.rest}")
97
+ if meta_data[:end_def] > 0
98
+ spec = SubProgram.new(meta_data[:name], 'spec', @ss.pos)
99
+ spec.declare_pos = @ss.pos + meta_data[:end_def]
100
+ scan_until(/#{SLASH_TERMINATOR}/)
101
+ spec.end_pos = @ss.pos
102
+ spec.text = @ss.string[(spec.start_pos..spec.end_pos)]
103
+ @blocks << spec
104
+ end
105
+ end
106
+ end
107
+
108
+ def process_subprog
109
+ if @ss.matched =~ /#{SUBPROG}/i
110
+ subprog_name = @ss.peek(32)[/(?:"[^"]+")|(?:[A-Z0-9$_#]+)/i]
111
+ if @ss.matched =~ /function/i
112
+ subprog_type = 'function'
113
+ elsif @ss.matched =~ /procedure/i
114
+ subprog_type = 'procedure'
115
+ end
116
+ start_pos = @ss.pos - @ss.matched.length
117
+ @crr_block = SubProgram.new(subprog_name, subprog_type, start_pos)
118
+ end
119
+ end
120
+
121
+ def process_begin
122
+ if @ss.matched =~ /#{BEGIN_MODULE}/i && @crr_block
123
+ @level += 1
124
+ @crr_block.declare_pos = @ss.pos
125
+ @crr_block.level = @level
126
+ end
127
+ end
128
+
129
+ def process_end
130
+ if @ss.matched =~ /#{END_MODULE}/i
131
+ # we have an "end" match. first of all check if it's not part
132
+ # of an conditional compiling "$end" definition
133
+ char_behind = @ss.string[@ss.pos - @ss.matched.length - 1, 1]
134
+ if char_behind != '$'
135
+ finish = Parser.end_subprog("#{@ss.matched}#{@ss.rest}")
136
+ if finish > 0
137
+ if @level == 1
138
+ if @crr_block
139
+ @crr_block.end_pos = (@ss.pos - 1) + (finish - 1)
140
+ @crr_block.text = @ss.string[(@crr_block.start_pos..@crr_block.end_pos)]
141
+ @blocks << @crr_block.clone
142
+ @crr_block = nil
143
+ end
144
+ end
145
+ @level -= 1
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ def scan_until(regexp, skip = false)
152
+ if skip
153
+ @ss.skip_until(regexp)
154
+ else
155
+ segment = @ss.scan_until(regexp)
156
+ if !segment
157
+ @ss.terminate
158
+ end
159
+ end
160
+ end
161
+
162
+ end
163
+
164
+ end
165
+
166
+ end
167
+
168
+
@@ -0,0 +1,104 @@
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