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,53 @@
1
+ # encoding: UTF-8
2
+
3
+ include Vorax
4
+
5
+ describe 'sqlplus' do
6
+
7
+ before(:all) do# {{{
8
+ @cstr = Parser::ConnString.new
9
+ end# }}}
10
+
11
+ it 'should parse a connection string containing just the user' do# {{{
12
+ @cstr.parse('scott')[:user].should eq('scott')
13
+ end# }}}
14
+
15
+ it 'should parse a connection string containing the user and the password' do# {{{
16
+ @cstr.parse('scott/tiger')[:user].should eq('scott')
17
+ end# }}}
18
+
19
+ it 'should parse a connection string containing the user and the database' do# {{{
20
+ @cstr.parse('scott@db')[:user].should eq('scott')
21
+ end# }}}
22
+
23
+ it 'should parse a connection string containing the user with special chars' do# {{{
24
+ @cstr.parse('"scott@man"@db')[:user].should eq('scott@man')
25
+ @cstr.parse('"scott/man"@db')[:user].should eq('scott/man')
26
+ end# }}}
27
+
28
+ it 'should parse a connection string containing an empty user' do# {{{
29
+ @cstr.parse('/')[:user].should eq('')
30
+ end# }}}
31
+
32
+ it 'should extract the role' do# {{{
33
+ @cstr.parse('talek/muci as sysdba').should eq({:prompt_for => nil, :user =>'talek', :password => 'muci', :db => '', :role => 'sysdba'})
34
+ @cstr.parse('talek/"muciBuci123" as SYSASM').should eq({:prompt_for => nil, :user =>'talek', :password => 'muciBuci123', :db => '', :role => 'sysasm'})
35
+ @cstr.parse('scott/tiger@//fox:1521/fd.fox.ro as sysoper').should eq({:prompt_for => nil, :user =>'scott', :password => 'tiger', :db => '//fox:1521/fd.fox.ro', :role => 'sysoper'})
36
+ @cstr.parse('scott@//fox:1521/fd.fox.ro as sysoper').should eq({:prompt_for => :password, :user =>'scott', :password => '', :db => '//fox:1521/fd.fox.ro', :role => 'sysoper'})
37
+ end# }}}
38
+
39
+ it 'should work with OS auth' do# {{{
40
+ @cstr.parse('/').should eq({:user =>'', :password => '', :db => '', :role => '', :prompt_for => nil})
41
+ @cstr.parse('/ as sysdba').should eq({:prompt_for => nil, :user =>'', :password => '', :db => '', :role => 'sysdba'})
42
+ @cstr.parse('/@db').should eq({:prompt_for => nil, :user =>'', :password => '', :db => 'db', :role => ''})
43
+ end# }}}
44
+
45
+ it 'should prompt for password when just the user is given' do# {{{
46
+ @cstr.parse('scott').should eq({:user =>'scott', :password => '', :db => '', :role => '', :prompt_for => :password})
47
+ end# }}}
48
+
49
+ it 'should prompt for user when nothing is given' do# {{{
50
+ @cstr.parse('').should eq({:user =>'', :password => '', :db => '', :role => '', :prompt_for => :user})
51
+ end# }}}
52
+
53
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: UTF-8
2
+
3
+ include Vorax
4
+ include Parser
5
+
6
+ describe 'package_spec' do
7
+
8
+ it 'should work with a simple package' do
9
+ text = File.open("spec/sql/muci.spc", 'rb') { |file| file.read }
10
+ parser = Vorax::Parser::PackageSpec.new
11
+ parser.walk(text)
12
+
13
+ parser.constants.should eq(Set.new(["MY_CONSTANT1", "MY_CONSTANT2"]))
14
+ parser.exceptions.should eq(Set.new(["ex_no_data_found", "ex_custom"]))
15
+ parser.cursors.should eq(Set.new(["my_cursor"]))
16
+ parser.types.should eq(Set.new(["population_type"]))
17
+ parser.variables.should eq(Set.new(["g_var1", "g_var2"]))
18
+ parser.procedures.should eq(Set.new(["my_proc"]))
19
+ parser.functions.should eq(Set.new(["my_func"]))
20
+ end
21
+
22
+ it 'should work with a big package spec' do
23
+ text = File.open('spec/sql/dbms_stats.spc', 'rb') { |file| file.read }
24
+ parser = Vorax::Parser::PackageSpec.new
25
+ parser.walk(text)
26
+ parser.constants.should eq(Set.new(["AUTO_CASCADE", "AUTO_INVALIDATE", "AUTO_SAMPLE_SIZE", "DEFAULT_DEGREE", "AUTO_DEGREE", "DEFAULT_CASCADE", "DEFAULT_DEGREE_VALUE", "DEFAULT_ESTIMATE_PERCENT", "DEFAULT_METHOD_OPT", "DEFAULT_NO_INVALIDATE", "DEFAULT_GRANULARITY", "DEFAULT_PUBLISH", "DEFAULT_INCREMENTAL", "DEFAULT_STALE_PERCENT", "DEFAULT_AUTOSTATS_TARGET", "DEFAULT_STAT_CATEGORY", "PURGE_ALL"]))
27
+ parser.exceptions.should eq(Set.new([]))
28
+ parser.cursors.should eq(Set.new([]))
29
+ parser.types.should eq(Set.new(["numarray", "datearray", "chararray", "rawarray", "fltarray", "dblarray", "StatRec", "ObjectElem", "ObjectTab", "DiffRepElem", "DiffRepTab", "CContext"]))
30
+ parser.variables.should eq(Set.new([]))
31
+ parser.procedures.should eq(Set.new(["prepare_column_values", "prepare_column_values_nvarchar", "prepare_column_values_rowid", "set_param", "reset_param_defaults", "reset_global_pref_defaults", "set_global_prefs", "set_table_prefs", "delete_table_prefs", "export_table_prefs", "import_table_prefs", "set_schema_prefs", "delete_schema_prefs", "export_schema_prefs", "import_schema_prefs", "set_database_prefs", "delete_database_prefs", "export_database_prefs", "import_database_prefs", "init_package", "publish_pending_stats", "export_pending_stats", "delete_pending_stats", "resume_gather_stats", "set_column_stats", "set_index_stats", "set_table_stats", "convert_raw_value", "convert_raw_value_nvarchar", "convert_raw_value_rowid", "get_column_stats", "get_index_stats", "get_table_stats", "delete_column_stats", "delete_index_stats", "delete_table_stats", "delete_schema_stats", "delete_database_stats", "create_stat_table", "drop_stat_table", "upgrade_stat_table", "export_column_stats", "export_index_stats", "export_table_stats", "export_schema_stats", "export_database_stats", "import_column_stats", "import_index_stats", "import_table_stats", "import_schema_stats", "import_database_stats", "gather_index_stats", "gather_table_stats", "gather_schema_stats", "gather_database_stats", "generate_stats", "flush_database_monitoring_info", "alter_schema_tab_monitoring", "alter_database_tab_monitoring", "gather_system_stats", "get_system_stats", "set_system_stats", "delete_system_stats", "import_system_stats", "export_system_stats", "gather_fixed_objects_stats", "delete_fixed_objects_stats", "export_fixed_objects_stats", "import_fixed_objects_stats", "gather_dictionary_stats", "delete_dictionary_stats", "export_dictionary_stats", "import_dictionary_stats", "lock_table_stats", "lock_partition_stats", "lock_schema_stats", "unlock_table_stats", "unlock_partition_stats", "unlock_schema_stats", "restore_table_stats", "restore_schema_stats", "restore_database_stats", "restore_fixed_objects_stats", "restore_dictionary_stats", "restore_system_stats", "purge_stats", "alter_stats_history_retention", "copy_table_stats", "drop_extended_stats", "merge_col_usage", "seed_col_usage", "reset_col_usage", "gather_database_stats_job_proc", "cleanup_stats_job_proc"]))
32
+ parser.functions.should eq(Set.new(["get_param", "get_prefs", "to_cascade_type", "to_estimate_percent_type", "to_degree_type", "to_no_invalidate_type", "to_publish_type", "get_stats_history_retention", "get_stats_history_availability", "diff_table_stats_in_stattab", "diff_table_stats_in_history", "diff_table_stats_in_pending", "create_extended_stats", "show_extended_stats_name", "report_col_usage"]))
33
+ end
34
+
35
+ it 'should work with a SYS package spec' do
36
+ text = File.open('spec/sql/dbms_crypto.spc', 'rb') { |file| file.read }
37
+ parser = Vorax::Parser::PackageSpec.new
38
+ parser.walk(text)
39
+ parser.constants.should eq(Set.new(["HASH_MD4", "HASH_MD5", "HASH_SH1", "HMAC_MD5", "HMAC_SH1", "ENCRYPT_DES", "ENCRYPT_3DES_2KEY", "ENCRYPT_3DES", "ENCRYPT_AES", "ENCRYPT_PBE_MD5DES", "ENCRYPT_AES128", "ENCRYPT_AES192", "ENCRYPT_AES256", "CHAIN_CBC", "CHAIN_CFB", "CHAIN_ECB", "CHAIN_OFB", "PAD_PKCS5", "PAD_NONE", "PAD_ZERO", "PAD_ORCL", "ENCRYPT_RC4", "DES_CBC_PKCS5", "DES3_CBC_PKCS5", "AES_CBC_PKCS5"]))
40
+ parser.exceptions.should eq(Set.new(["CipherSuiteInvalid", "CipherSuiteNull", "KeyNull", "KeyBadSize", "DoubleEncryption"]))
41
+ parser.cursors.should eq(Set.new([]))
42
+ parser.types.should eq(Set.new([]))
43
+ parser.variables.should eq(Set.new([]))
44
+ parser.procedures.should eq(Set.new(["Encrypt", "Decrypt"]))
45
+ parser.functions.should eq(Set.new(["Encrypt", "Decrypt", "Hash", "Mac", "RandomBytes", "RandomNumber", "RandomInteger"]))
46
+ end
47
+
48
+ end
@@ -0,0 +1,153 @@
1
+ # encoding: UTF-8
2
+
3
+ include Vorax
4
+
5
+ describe 'pagezip layout' do
6
+
7
+ before(:each) do# {{{
8
+ @sp = Sqlplus.new('sqlplus')
9
+ @sp.default_convertor = :pagezip
10
+ @prep = [VORAX_CSTR,
11
+ "set tab off",
12
+ "set linesize 10000",
13
+ "set markup html on",
14
+ "set echo off",
15
+ "set pause off",
16
+ "set define &",
17
+ "set termout on",
18
+ "set verify off",
19
+ "set pagesize 5"].join("\n")
20
+ @result = ""
21
+ end# }}}
22
+
23
+ it 'should work with multi line records' do# {{{
24
+ @sp.exec("select * from departments where id in (1, 2, 6);", :prep => @prep)
25
+ @result << @sp.read_output(32767) while @sp.busy?
26
+ expected = <<OUTPUT
27
+
28
+ SQL>
29
+
30
+ ID NAME DESCRIPTION
31
+ -- ----------- -----------------------------------
32
+ 1 Bookkeeping This department is responsible for:
33
+ - financial reporting
34
+ - analysis
35
+ - other boring tasks
36
+ 2 Marketing  
37
+ 6 Management The bad guys department
38
+
39
+ SQL>
40
+ OUTPUT
41
+ @result.should eq(expected)
42
+ end# }}}
43
+
44
+ it 'should work with a single line record' do# {{{
45
+ @sp.exec("select * from departments where id=2;", :prep => @prep)
46
+ @result << @sp.read_output(32767) while @sp.busy?
47
+ expected = <<OUTPUT
48
+
49
+ SQL>
50
+
51
+ ID NAME DESCRIPTION
52
+ -- --------- -----------
53
+ 2 Marketing  
54
+
55
+ SQL>
56
+ OUTPUT
57
+ @result.should eq(expected)
58
+ end# }}}
59
+
60
+ it 'should work without headers' do# {{{
61
+ @sp.exec("select * from departments where id<=4;", :prep => "#@prep\nset pagesize 0")
62
+ @result << @sp.read_output(32767) while @sp.busy?
63
+ expected = <<OUTPUT
64
+
65
+ SQL>
66
+
67
+ 1 Bookkeeping This department is responsible for:
68
+ - financial reporting
69
+ - analysis
70
+ - other boring tasks
71
+ 2 Marketing  
72
+ 3 Deliveries  
73
+ 4 CRM  
74
+
75
+ SQL>
76
+ OUTPUT
77
+ @result.should eq(expected)
78
+ end# }}}
79
+
80
+ it 'should work with special unicode chars' do# {{{
81
+ @sp.exec("select * from employees where id=1;", :prep => @prep)
82
+ @result << @sp.read_output(32767) while @sp.busy?
83
+ expected = <<OUTPUT
84
+
85
+ SQL>
86
+
87
+ ID NAME SALARY DEPARTMENT_ID
88
+ -- ----------- ------ -------------
89
+ 1 Tică Șerban 570 1
90
+
91
+ SQL>
92
+ OUTPUT
93
+ @result.should eq(expected)
94
+ end# }}}
95
+
96
+ it 'should work with multiple pages' do# {{{
97
+ @sp.exec("select * from departments where id<=10;", :prep => "#@prep\nset pagesize 4")
98
+ @result << @sp.read_output(32767) while @sp.busy?
99
+ expected = <<OUTPUT
100
+
101
+ SQL>
102
+
103
+ ID NAME DESCRIPTION
104
+ -- ----------- -----------------------------------
105
+ 1 Bookkeeping This department is responsible for:
106
+ - financial reporting
107
+ - analysis
108
+ - other boring tasks
109
+ 2 Marketing  
110
+ 3 Deliveries  
111
+ 4 CRM  
112
+
113
+ ID NAME DESCRIPTION
114
+ -- ---------------- -----------------------
115
+ 5 Legal Stuff  
116
+ 6 Management The bad guys department
117
+ 7 Cooking  
118
+ 8 Public Relations  
119
+
120
+ ID NAME DESCRIPTION
121
+ -- ----------- -----------
122
+ 9 Aquisitions  
123
+ 10 Cleaning  
124
+
125
+ 10 rows selected.
126
+
127
+ SQL>
128
+ OUTPUT
129
+ #puts @result
130
+ @result.should eq(expected)
131
+ end# }}}
132
+
133
+ it 'should work with accept prompts' do# {{{
134
+ begin
135
+ pack_file = Tempfile.new(['vorax', '.sql'])
136
+ @sp.exec("accept var prompt \"Enter var: \"\nprompt &var", :prep => @prep, :pack_file => pack_file.path)
137
+ Timeout::timeout(10) {
138
+ @result << @sp.read_output(32767) while @result !~ /Enter var:\z/
139
+ @sp.send_text("muci\n")
140
+ @result << @sp.read_output(32767) while @result !~ /muci\n\z/
141
+ }
142
+ ensure
143
+ pack_file.unlink
144
+ end
145
+ end# }}}
146
+
147
+ after(:each) do# {{{
148
+ @sp.terminate
149
+ end# }}}
150
+
151
+ end
152
+
153
+
@@ -0,0 +1,299 @@
1
+ # encoding: UTF-8
2
+
3
+ include Vorax
4
+
5
+ describe 'Parser' do
6
+
7
+ it 'should detect the end of a subprogram' do# {{{
8
+ text = "end;"
9
+ Parser.plsql_def(text)[:end_def].should be >0;
10
+ text = "end if;"
11
+ Parser.plsql_def(text)[:end_def].should be <0;
12
+ text = "end loop;"
13
+ Parser.plsql_def(text)[:end_def].should be <0;
14
+ text = "end my_func;"
15
+ Parser.plsql_def(text)[:end_def].should be >0;
16
+ text = "end/*test*/my_prog;"
17
+ Parser.plsql_def(text)[:end_def].should be >0;
18
+ text = "end/*test*/\"my_special_prog\";"
19
+ Parser.plsql_def(text)[:end_def].should be >0;
20
+ text = "end is_AdminBasket;\r\n\r\n function get_BasketId(pi_impkey in integer, pi_tablename in varchar2)\r\n "
21
+ Parser.plsql_def(text)[:end_def].should be >0;
22
+ end# }}}
23
+
24
+ it 'should detect the definition of a plsql module' do# {{{
25
+ text = "PACKAGE muci AS "
26
+ result = Parser.plsql_def(text)
27
+ result[:end_def].should be >0
28
+ result[:name].should == 'muci'
29
+ result[:type].should == 'SPEC'
30
+ text = "package\nbody muci.buci/* bla bla*/ as "
31
+ result = Parser.plsql_def(text)
32
+ result[:end_def].should be >0;
33
+ result[:name].should == 'muci.buci'
34
+ result[:type].should == 'BODY'
35
+ text = "package bdy muci.buci/* bla bla*/ as "
36
+ Parser.plsql_def(text)[:end_def].should be <0;
37
+ end# }}}
38
+
39
+ it 'should work with balanced paren' do# {{{
40
+ Parser.walk_balanced_paren('(a(b)c)').should eq("(a(b)c)")
41
+ Parser.walk_balanced_paren('(a(b)(xyz)c)').should eq("(a(b)(xyz)c)")
42
+ Parser.walk_balanced_paren("(a')'(b)c)bla bla bla").should eq("(a')'(b)c)")
43
+ Parser.walk_balanced_paren('(a")"(b)c)bla bla bla').should eq('(a")"(b)c)')
44
+ Parser.walk_balanced_paren("(a/*)*/(b)c)bla bla bla").should eq("(a/*)*/(b)c)")
45
+ Parser.walk_balanced_paren("(a--)\n(b)c)bla bla bla").should eq("(a--)\n(b)c)")
46
+ Parser.walk_balanced_paren("(aq[')]'(b)c)bla bla bla").should eq("(aq[')]'(b)c)")
47
+ end# }}}
48
+
49
+ it 'should add the proper end delimitator' do# {{{
50
+ text = "select * from cat"
51
+ Parser.prepare_exec(text).should eq("select * from cat;")
52
+
53
+ text = <<TEXT
54
+ begin
55
+ null;
56
+ end;
57
+ TEXT
58
+ Parser.prepare_exec(text).should eq("begin\n null;\nend;\n/\n")
59
+
60
+ text = "set serveroutput on"
61
+ Parser.prepare_exec(text).should eq("set serveroutput on")
62
+
63
+ text = "select * from dual;"
64
+ Parser.prepare_exec(text).should eq("select * from dual;")
65
+ end# }}}
66
+
67
+ describe 'Comments parsing' do# {{{
68
+
69
+ it 'should be smart enough to leave quotes unchanged' do# {{{
70
+ stmt = <<STR
71
+ select '/*muci*/' from/*abc*/dual;
72
+ STR
73
+ Parser.remove_all_comments(stmt).should eq("select '/*muci*/' from dual;\n")
74
+ end# }}}
75
+
76
+ it 'should remove all comments' do# {{{
77
+ stmt = <<STR
78
+ select /* muci */ * from --muhah
79
+ --bla
80
+ dual
81
+ /* muha
82
+ ha*/
83
+ ;
84
+ STR
85
+ Parser.remove_all_comments(stmt).should eq("select * from dual\n \n;\n")
86
+ end# }}}
87
+
88
+ it 'should remove all comments from incomplete statements' do# {{{
89
+ Parser.remove_all_comments('select /* bla').should eq('select /* bla')
90
+ end# }}}
91
+
92
+ it 'should remove trailing comments' do# {{{
93
+ text = <<TEXT
94
+ select * from cat --comment
95
+ -- alt comment
96
+ -- si inca un comment
97
+ TEXT
98
+ Parser.remove_trailing_comments(text).should eq("select * from cat ")
99
+
100
+ text = <<TEXT
101
+ select * from cat
102
+ /* multi
103
+ line comment */
104
+ /* inca
105
+ unul */
106
+ TEXT
107
+ Parser.remove_trailing_comments(text).should eq("select * from cat\n")
108
+
109
+ text = <<TEXT
110
+ select * from cat
111
+ /* multi
112
+ line comment */
113
+ -- single line comment
114
+ /* inca
115
+ unul */
116
+ -- muhaha
117
+ TEXT
118
+ Parser.remove_trailing_comments(text).should eq("select * from cat\n")
119
+ end# }}}
120
+
121
+ end# }}}
122
+
123
+ describe 'Arguments parsing' do# {{{
124
+
125
+ it 'should detect basic argument owner' do# {{{
126
+ text = 'select col1, 1+2, func(a, b, my_f(x, y), c, \'a\'\'bc\', "xxx".y, d,'
127
+ Parser.argument_belongs_to(text, 65).should eq('func')
128
+ Parser.argument_belongs_to(text, 37).should eq('my_f')
129
+ end# }}}
130
+
131
+ it 'should handle whitespaces between function name and open paran' do# {{{
132
+ Parser.argument_belongs_to('exec dbms_stats.gather_schema_stats (owname => user, ').should eq('dbms_stats.gather_schema_stats')
133
+ Parser.argument_belongs_to("begin dbms_stats.gather_schema_stats \n\t (owname => user, ").should eq('dbms_stats.gather_schema_stats')
134
+ end# }}}
135
+
136
+ it 'should handle quoted identifiers' do# {{{
137
+ Parser.argument_belongs_to('exec "My user" . "bla@hei.la" @ dblink.hop.hu(owname => user, ').should eq("\"My user\".\"bla@hei.la\"@dblink.hop.hu")
138
+ Parser.argument_belongs_to('exec "ABC!"."bla"(owname => user, ').should eq("\"ABC!\".\"bla\"")
139
+ Parser.argument_belongs_to('exec owner."pkg"."my func"(owname => user, ').should eq("owner.\"pkg\".\"my func\"")
140
+ end# }}}
141
+
142
+ it 'should ignore comments' do# {{{
143
+ Parser.argument_belongs_to('exec dbms_stats./*muci*/gather_schema_stats/*abc*/ (owname => user, ').should eq('dbms_stats.gather_schema_stats')
144
+ end# }}}
145
+
146
+ it 'should handle plsql quoting' do# {{{
147
+ text = "exec pkg.my_proc(q'[Isn't the \"(\" character sweet?]', "
148
+ Parser.argument_belongs_to(text).should eq('pkg.my_proc')
149
+ text = "exec pkg.my_proc(q'{Isn't the \"(\" character sweet?}', "
150
+ Parser.argument_belongs_to(text).should eq('pkg.my_proc')
151
+ text = "exec pkg.my_proc(q'<Isn't the \"(\" character sweet?>', "
152
+ Parser.argument_belongs_to(text).should eq('pkg.my_proc')
153
+ end# }}}
154
+
155
+ it 'should fallback to simple quoting' do# {{{
156
+ text = "exec pkg.my_proc('<Isn''t the \"(\" character sweet?>', "
157
+ Parser.argument_belongs_to(text).should eq('pkg.my_proc')
158
+ end# }}}
159
+
160
+ end# }}}
161
+
162
+ describe 'Statement type' do# {{{
163
+
164
+ it 'should detect an anonymous block' do# {{{
165
+ Parser.statement_type("begin dbms_output.put_line('xx');").should eq("ANONYMOUS")
166
+ Parser.statement_type("declare l_test varchar2(10);").should eq("ANONYMOUS")
167
+ Parser.statement_type("bbegin dbms_output.put_line('xx');").should be_nil
168
+ Parser.statement_type("ddeclare dbms_output.put_line('xx');").should be_nil
169
+ Parser.statement_type("select 1 from dual;").should be_nil
170
+ end# }}}
171
+
172
+ it 'should detect a function' do# {{{
173
+ Parser.statement_type("create or\nreplace function muci as").should eq("FUNCTION")
174
+ Parser.statement_type("create orreplace function muci as").should be_nil
175
+ end# }}}
176
+
177
+ it 'should detect a procedure' do# {{{
178
+ Parser.statement_type("create or replace\n procedure muci as").should eq("PROCEDURE")
179
+ Parser.statement_type("create or replace proceduremuci as").should be_nil
180
+ end# }}}
181
+
182
+ it 'should detect a trigger' do# {{{
183
+ Parser.statement_type("create or replace\n trigger muci as").should eq("TRIGGER")
184
+ Parser.statement_type("createor replace trigger as").should be_nil
185
+ end# }}}
186
+
187
+ it 'should detect a package spec' do# {{{
188
+ Parser.statement_type("create or replace\n package muci as").should eq("PACKAGE")
189
+ Parser.statement_type("createor replace package as").should be_nil
190
+ end# }}}
191
+
192
+ it 'should detect a package body' do# {{{
193
+ Parser.statement_type("create or replace\n package body muci as").should eq("PACKAGE BODY")
194
+ Parser.statement_type("create or replace packagebody as").should be_nil
195
+ Parser.statement_type("create or replace package bodyas").should_not eq("PACKAGE BODY")
196
+ end# }}}
197
+
198
+ it 'should detect a type spec' do# {{{
199
+ Parser.statement_type("create or replace\n type muci as").should eq("TYPE")
200
+ Parser.statement_type("createor replace type as").should be_nil
201
+ end# }}}
202
+
203
+ it 'should detect a type body' do# {{{
204
+ Parser.statement_type("create or replace\n type body muci as").should eq("TYPE BODY")
205
+ Parser.statement_type("create or replace typebody as").should be_nil
206
+ Parser.statement_type("create or replace type bodyas").should_not eq("TYPE BODY")
207
+ end# }}}
208
+
209
+ it 'should detect java source' do# {{{
210
+ Parser.statement_type("create or replace java muci as").should eq("JAVA")
211
+ Parser.statement_type("create java muci as").should eq("JAVA")
212
+ Parser.statement_type("create or replace and compile java muci as").should eq("JAVA")
213
+ Parser.statement_type("create or replace and resolve noforce java muci as").should eq("JAVA")
214
+ Parser.statement_type("create or replace and resolvenoforce java muci as").should_not eq("JAVA")
215
+ end# }}}
216
+
217
+ it 'should detect sqlplus command' do# {{{
218
+ Parser.statement_type("accept muci").should eq("SQLPLUS")
219
+ Parser.statement_type("acc muci").should eq("SQLPLUS")
220
+ Parser.statement_type("acce muci").should eq("SQLPLUS")
221
+ Parser.statement_type("@muci.sql").should eq("SQLPLUS")
222
+ Parser.statement_type("@@muci.sql").should eq("SQLPLUS")
223
+ Parser.statement_type("/").should eq("SQLPLUS")
224
+ Parser.statement_type("archive log list").should eq("SQLPLUS")
225
+ Parser.statement_type("attribute muci").should eq("SQLPLUS")
226
+ Parser.statement_type("break on whatever").should eq("SQLPLUS")
227
+ Parser.statement_type("btitle abc").should eq("SQLPLUS")
228
+ Parser.statement_type("btit abc").should eq("SQLPLUS")
229
+ Parser.statement_type("cle").should eq("SQLPLUS")
230
+ Parser.statement_type("colu abc format a15").should eq("SQLPLUS")
231
+ Parser.statement_type("compute on bla bla bla").should eq("SQLPLUS")
232
+ Parser.statement_type("connect muci/buci@db").should eq("SQLPLUS")
233
+ Parser.statement_type("copy").should eq("SQLPLUS")
234
+ Parser.statement_type("def var").should eq("SQLPLUS")
235
+ Parser.statement_type("desc my_table").should eq("SQLPLUS")
236
+ Parser.statement_type("discon").should eq("SQLPLUS")
237
+ Parser.statement_type("execu dbms_output.put_line('abc');").should eq("SQLPLUS")
238
+ Parser.statement_type("exit").should eq("SQLPLUS")
239
+ Parser.statement_type("quit").should eq("SQLPLUS")
240
+ Parser.statement_type("help").should eq("SQLPLUS")
241
+ Parser.statement_type("host ls -al").should eq("SQLPLUS")
242
+ Parser.statement_type("!ls -al").should eq("SQLPLUS")
243
+ Parser.statement_type("passw").should eq("SQLPLUS")
244
+ Parser.statement_type("password user").should eq("SQLPLUS")
245
+ Parser.statement_type("pause press any key").should eq("SQLPLUS")
246
+ Parser.statement_type("print curs").should eq("SQLPLUS")
247
+ Parser.statement_type("prom var").should eq("SQLPLUS")
248
+ Parser.statement_type("recover database").should eq("SQLPLUS")
249
+ Parser.statement_type("rem comment").should eq("SQLPLUS")
250
+ Parser.statement_type("repf on").should eq("SQLPLUS")
251
+ Parser.statement_type("rephe off").should eq("SQLPLUS")
252
+ Parser.statement_type("sav muci.sql").should eq("SQLPLUS")
253
+ Parser.statement_type("set pagesize 100").should eq("SQLPLUS")
254
+ Parser.statement_type("set transaction name muci;").should be_nil
255
+ Parser.statement_type("show pagesize").should eq("SQLPLUS")
256
+ Parser.statement_type("shutdown").should eq("SQLPLUS")
257
+ Parser.statement_type("spool muci.log").should eq("SQLPLUS")
258
+ Parser.statement_type("start muci.sql").should eq("SQLPLUS")
259
+ Parser.statement_type("startup").should eq("SQLPLUS")
260
+ Parser.statement_type("store set options.sql").should eq("SQLPLUS")
261
+ Parser.statement_type("timing stop").should eq("SQLPLUS")
262
+ Parser.statement_type("title abc").should eq("SQLPLUS")
263
+ Parser.statement_type("undef var").should eq("SQLPLUS")
264
+ Parser.statement_type("var muci").should eq("SQLPLUS")
265
+ Parser.statement_type("whenever oserror exit").should eq("SQLPLUS")
266
+ Parser.statement_type("xquery bla bla bla").should eq("SQLPLUS")
267
+ Parser.statement_type("\n-- APEX RMS DB UPDATES FILE\n").should be_nil
268
+ end# }}}
269
+
270
+ end# }}}
271
+
272
+ it 'should get the current statement' do# {{{
273
+ text = <<STRING
274
+ select /* comment: ; */ 'muci;buci''s yea' from dual; -- interesting ha;ha?
275
+ select * from cat;
276
+ STRING
277
+ Parser.current_statement(text, 10)[:statement].should eq("select /* comment: ; */ 'muci;buci''s yea' from dual;")
278
+ Parser.current_statement(text, 85)[:statement].should eq(" -- interesting ha;ha?\nselect * from cat;")
279
+
280
+ text = <<STRING
281
+ set serveroutput on
282
+ column c1 format a10
283
+ select 1 from dual;
284
+ begin
285
+ null;
286
+ end;
287
+ /
288
+ select c2 from t1
289
+ /
290
+ update t set x=1;
291
+ STRING
292
+ Parser.current_statement(text, 10, :sqlplus_commands => false, :plsql_blocks => false)[:statement].should eq("set serveroutput on\ncolumn c1 format a10\nselect 1 from dual;")
293
+ Parser.current_statement(text, 10, :sqlplus_commands => true, :plsql_blocks => false)[:statement].should eq("set serveroutput on")
294
+ Parser.current_statement(text, 71, :sqlplus_commands => true, :plsql_blocks => false)[:statement].should eq("\nbegin\n null;")
295
+ Parser.current_statement(text, 71, :sqlplus_commands => true, :plsql_blocks => true)[:statement].should eq("\nbegin\n null;\nend;\n/\n")
296
+ Parser.current_statement(text, 88, :sqlplus_commands => true, :plsql_blocks => true)[:statement].should eq("select c2 from t1\n/\n")
297
+ end# }}}
298
+
299
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: UTF-8
2
+
3
+ include Vorax
4
+
5
+ describe 'plsql structure' do
6
+
7
+ it 'should work for complex plsql code' do
8
+ text = File.open('spec/sql/test.pkg', 'rb') { |file| file.read }
9
+ #text = File.open('spec/sql/admin_toolkit.bdy', 'rb') { |file| file.read }
10
+ structure = Parser::PlsqlStructure.new(text)
11
+ @result = ''
12
+ def compute_tree(node, level = 0)
13
+ if node.is_root?
14
+ @result << "*"
15
+ else
16
+ @result << "|" unless node.parent.is_last_sibling?
17
+ @result << (' ' * (node.level - 1) * 4)
18
+ @result << (node.is_last_sibling? ? "+" : "|")
19
+ @result << "---"
20
+ @result << (node.has_children? ? "+" : ">")
21
+ end
22
+
23
+ @result << " #{node.name} - #{(node.content ? node.content.end_pos : '')}\n"
24
+
25
+ node.children { |child| compute_tree(child, level + 1)}
26
+ end
27
+
28
+ compute_tree(structure.tree)
29
+ @result.should eq(<<STR
30
+ * root -
31
+ |---+ test[SPEC]: 25 - 154
32
+ | |---> test[PROCEDURE]: 70 - 70
33
+ | +---> muci[FUNCTION]: 99 - 99
34
+ |---+ test[BODY]: 180 - 876
35
+ | |---> private_proc[PROCEDURE]: 241 - 394
36
+ | +---> xyz[PROCEDURE]: 414 - 451
37
+ |---> test[PROCEDURE]: 890 - 0
38
+ +---> anonymous[BLOCK]: 969 - 1040
39
+ STR
40
+ )
41
+ end
42
+
43
+ end
44
+
@@ -0,0 +1,13 @@
1
+ # encoding: UTF-8
2
+
3
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
4
+
5
+ # Initialize the VORAX_TEST_CSTR environment variable with
6
+ # the user/pwd@db connection string, pointing to VORAX test
7
+ # user, or change the below line accordingly.
8
+ VORAX_CSTR = "connect #{ENV['VORAX_TEST_CSTR']}"
9
+
10
+ require 'tempfile'
11
+ require 'timeout'
12
+ require 'vorax'
13
+