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.
- data/.gitignore +7 -0
- data/.rspec +1 -0
- data/LICENSE.txt +22 -0
- data/README.md +45 -0
- data/Rakefile +30 -0
- data/lib/vorax/base_funnel.rb +30 -0
- data/lib/vorax/output/html_convertor.rb +120 -0
- data/lib/vorax/output/html_funnel.rb +79 -0
- data/lib/vorax/output/pagezip_convertor.rb +20 -0
- data/lib/vorax/output/tablezip_convertor.rb +22 -0
- data/lib/vorax/output/vertical_convertor.rb +53 -0
- data/lib/vorax/output/zip_convertor.rb +117 -0
- data/lib/vorax/parser/argument.rb~ +125 -0
- data/lib/vorax/parser/body_split.rb +168 -0
- data/lib/vorax/parser/conn_string.rb +104 -0
- data/lib/vorax/parser/grammars/alias.rb +912 -0
- data/lib/vorax/parser/grammars/alias.rl +146 -0
- data/lib/vorax/parser/grammars/column.rb +454 -0
- data/lib/vorax/parser/grammars/column.rl +64 -0
- data/lib/vorax/parser/grammars/common.rl +98 -0
- data/lib/vorax/parser/grammars/package_spec.rb +1186 -0
- data/lib/vorax/parser/grammars/package_spec.rl +78 -0
- data/lib/vorax/parser/grammars/plsql_def.rb +469 -0
- data/lib/vorax/parser/grammars/plsql_def.rl +59 -0
- data/lib/vorax/parser/grammars/statement.rb +925 -0
- data/lib/vorax/parser/grammars/statement.rl +83 -0
- data/lib/vorax/parser/parser.rb +320 -0
- data/lib/vorax/parser/plsql_structure.rb +158 -0
- data/lib/vorax/parser/plsql_walker.rb +143 -0
- data/lib/vorax/parser/statement_inspector.rb~ +52 -0
- data/lib/vorax/parser/stmt_inspector.rb +78 -0
- data/lib/vorax/parser/target_ref.rb +110 -0
- data/lib/vorax/sqlplus.rb +281 -0
- data/lib/vorax/version.rb +7 -0
- data/lib/vorax/vorax_io.rb +70 -0
- data/lib/vorax.rb +60 -0
- data/spec/column_spec.rb +40 -0
- data/spec/conn_string_spec.rb +53 -0
- data/spec/package_spec_spec.rb +48 -0
- data/spec/pagezip_spec.rb +153 -0
- data/spec/parser_spec.rb +299 -0
- data/spec/plsql_structure_spec.rb +44 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/sql/create_objects.sql +69 -0
- data/spec/sql/dbms_crypto.spc +339 -0
- data/spec/sql/dbms_crypto.~spc +339 -0
- data/spec/sql/dbms_stats.spc +4097 -0
- data/spec/sql/drop_user.sql +10 -0
- data/spec/sql/muci.spc +24 -0
- data/spec/sql/setup_user.sql +22 -0
- data/spec/sql/test.pkg +67 -0
- data/spec/sqlplus_spec.rb +52 -0
- data/spec/stmt_inspector_spec.rb +84 -0
- data/spec/tablezip_spec.rb +111 -0
- data/spec/vertical_spec.rb +150 -0
- data/vorax.gemspec +21 -0
- 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
|
+
|
data/spec/parser_spec.rb
ADDED
@@ -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
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
+
|