ddl_parser 0.0.9 → 0.0.10

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.
@@ -1,506 +1,518 @@
1
- require_relative '../../../../spec/spec_helper'
2
-
3
- describe DDLParser::DDL::DB2::Parser do
4
- let(:parser) { DDLParser::DDL::DB2::Parser.new }
5
-
6
- context 'building blocks' do
7
-
8
- it 'parses arglist' do
9
- parser.arglist.parse('a').should == {:item=> 'a'}
10
- parser.arglist.parse('a,b,c').should == [{:item=> 'a'}, {:item=> 'b'}, {:item=> 'c'}]
11
- parser.arglist.parse('a, b, c').should == [{:item=> 'a'}, {:item=> 'b'}, {:item=> 'c'}]
12
- end
13
-
14
- end
15
-
16
- context 'column options parsing' do
17
- it 'parses not null' do
18
- expect(parser.column_options).to parse('not null')
19
- end
20
-
21
- it 'parses default values' do
22
- parser.column_options.parse('default 1').should == [{:default_clause=>[{:integer=> '1'}]}]
23
- parser.column_options.parse('with default 1').should == [{:default_clause=>[{:integer=> '1'}]}]
24
- parser.column_options.parse('default current timestamp').should == [{:default_clause=>[{:timestamp => "current timestamp"}]}]
25
- parser.column_options.parse("default 'foobar'").should == [{:default_clause=>[{:string => "'foobar'"}]}]
26
- parser.column_options.parse('default max(foo)').should == [{:default_clause=>[{:function=>{:name=>"max", :arguments=>{:item=>"foo"}}}]}]
27
-
28
- expect(
29
- parser.column_options.parse('default 1')
30
- ).to eq([{:default_clause=>[{:integer=> '1'}]}])
31
- end
32
-
33
- it 'parses generated by' do
34
- sql = 'generated by default as identity (start with 1 increment by 1 cache 20 )'
35
- begin
36
- result = parser.column_options.parse(sql.downcase)
37
- rescue Parslet::ParseFailed => error
38
- puts error.cause.ascii_tree
39
- end
40
- result.should == [{:identity=>{:start_value=>{:integer=> '1'},
41
- :increment_value=>{:integer=> '1'},
42
- :cache_value=>{:integer=> '20'}}}]
43
- end
44
-
45
- it 'parses identity' do
46
- expect(parser.column_options).to parse('as identity (start with 500, increment by 1)')
47
- expect(
48
- parser.column_options.parse('as identity (start with 500, increment by 1)')
49
- ).to eq([{:identity=>{:start_value=>{:integer=> '500'}, :increment_value=>{:integer=> '1'}}}])
50
- end
51
-
52
- it 'parses multible' do
53
- expect(parser.column_options).to parse('not null as identity (start with 500, increment by 1)')
54
-
55
- result = parser.column_options.parse('not null as identity (start with 500, increment by 1)')
56
- result.first.should include(:column_option)
57
- result.last.should include(:identity)
58
- end
59
-
60
- end
61
-
62
- context 'constraint' do
63
- it 'parses primary key' do
64
- sql = 'primary key (id)'
65
- parser.primary_key.parse(sql).should == {:primary_key=>{:item=> 'id'}}
66
- end
67
-
68
- it 'parses composed primary key' do
69
- sql = 'primary key (id,foo)'
70
- parser.primary_key.parse(sql).should == {:primary_key=>[{:item=> 'id'}, {:item=> 'foo'}]}
71
- end
72
-
73
- it 'parses unique' do
74
- sql = 'constraint emp_act_uniq unique (empno,projno,actno)'
75
-
76
- parser.constraint.parse(sql).should == {:constraint=>{:column_name=> 'emp_act_uniq',
77
- :constraint_type=> 'unique',
78
- :constraint_arglist=>[{:item=> 'empno'},
79
- {:item=> 'projno'},
80
- {:item=> 'actno'}]}}
81
- end
82
-
83
- it 'parses foreign key' do
84
- sql = 'constraint fk_act_proj foreign key (projno) references project (projno) on delete cascade'
85
- begin
86
- result = parser.constraint.parse(sql)
87
- rescue Parslet::ParseFailed => error
88
- puts error.cause.ascii_tree
89
- end
90
- result.should == {:constraint=>{:column_name=> 'fk_act_proj',
91
- :constraint_type=> 'foreign key',
92
- :constraint_arglist=>{:item=> 'projno'},
93
- :reference_arglist=>{:item=> 'projno'}}}
94
- end
95
-
96
- end
97
-
98
- context 'column definition' do
99
- it 'parses char not null' do
100
- sql = 'deptno char(3) not null'
101
- parser.column_definition.parse(sql).should == {:column => {
102
- :field=> 'deptno',
103
- :data_type=>{:char=>{:length=>{:integer=> '3'}}},
104
- :options=>[{:column_option=> 'not null'}]
105
- }
106
- }
107
-
108
- end
109
- it 'parses char not null - x' do
110
- sql = 'BUDGET_AMOUNT_IN_CO DECIMAL(15,2) NOT NULL DEFAULT 0'
111
- parser.column_definition.parse(sql.downcase).should == {:column => {
112
- :field=> 'budget_amount_in_co',
113
- :data_type=>{:decimal=>{:precision=>
114
- {:total=>{:integer=> '15'},
115
- :scale=>{:integer=> '2'}}}},
116
- :options=>[{:column_option=> 'not null'}, {:default_clause=>[{:integer=> '0'}]}]
117
- }
118
- }
119
-
120
- end
121
-
122
- end
123
-
124
- context 'element list' do
125
- it 'parses one element' do
126
- sql = '(deptno char(3) not null)'
127
- parser.element_list.parse(sql).should == {:elements => {:column=>{:field=> 'deptno',
128
- :data_type=>{:char=>{:length=>{:integer=> '3'}}},
129
- :options=>[{:column_option=> 'not null'}]}}}
130
- end
131
-
132
- it 'parses two element' do
133
- sql = '(field1 char(1) not null, field2 char(2))'
134
- parser.element_list.parse(sql).should == {:elements => [{:column=>{:field=> 'field1',
135
- :data_type=>{:char=>{:length=>{:integer=> '1'}}},
136
- :options=>[{:column_option=> 'not null'}]}},
137
- {:column=>{:field=> 'field2',
138
- :data_type=>{:char=>{:length=>{:integer=> '2'}}},
139
- :options=> ''}}]}
140
- end
141
-
142
- it 'parses with new lines' do
143
- sql = <<EOF
144
- (
145
- DEPTNO CHAR(3) NOT NULL,
146
- DEPTNAME VARCHAR(36) NOT NULL
147
- )
148
- EOF
149
- parser.element_list.parse(sql.downcase).should == {:elements => [{:column=>{:field=> 'deptno',
150
- :data_type=>{:char=>{:length=>{:integer=> '3'}}},
151
- :options=>[{:column_option=> 'not null'}]}},
152
- {:column=>{:field=> 'deptname',
153
- :data_type=>{:varchar=>{:length=>{:integer=> '36'}}},
154
- :options=>[{:column_option=> 'not null'}]}}]}
155
-
156
- end
157
- end
158
-
159
- context 'create table' do
160
- it 'parses simple create' do
161
- expect(parser.create_table_statement).to parse('create table foobar')
162
- expect(parser.create_table_statement).not_to parse('create table ')
163
-
164
- parser.create_table_statement.parse('create table foobar').should == {:operation=> 'create table', :table_name => 'foobar'}
165
- end
166
- end
167
-
168
-
169
-
170
- context 'simple statements' do
171
-
172
- it 'parses Example 1' do
173
- sql = <<EOF
174
- CREATE TABLE TDEPT
175
- (DEPTNO CHAR(3) NOT NULL,
176
- DEPTNAME VARCHAR(36) NOT NULL,
177
- MGRNO INT,
178
- ADMRDEPT DECIMAL(3,1) NOT NULL)
179
- IN DEPARTX
180
- EOF
181
- parser.create_table.parse(sql.downcase).should == {:operation=> 'create table',
182
- :table_name=> 'tdept',
183
- :elements => [
184
- {:column=>{:field=> 'deptno',
185
- :data_type=>{:char=>{:length=>{:integer=> '3'}}},
186
- :options=>[{:column_option=> 'not null'}]}},
187
- {:column=>{:field=> 'deptname',
188
- :data_type=>{:varchar=>{:length=>{:integer=> '36'}}},
189
- :options=>[{:column_option=> 'not null'}]}},
190
- {:column=>{:field=> 'mgrno',
191
- :data_type=> 'int',
192
- :options=> ''}},
193
- {:column=>{:field=> 'admrdept',
194
- :data_type=>{:decimal=>{:precision=>{:total=>{:integer=> '3'}, :scale=>{:integer=> '1'}}}},
195
- :options=>[{:column_option=> 'not null'}]}}
196
- ]}
197
- end
198
-
199
- it 'parses with primary key' do
200
- sql = <<EOF
201
- CREATE TABLE TEST
202
- (ID INT,
203
- PRIMARY KEY (ID))
204
- EOF
205
- parser.create_table.parse(sql.downcase).should == {:operation=> 'create table',
206
- :table_name=> 'test',
207
- :elements=>[
208
- {:column => {:field=> 'id', :data_type=> 'int', :options=> ''}},
209
- {:primary_key=>{:item=> 'id'}}
210
- ]
211
- }
212
- end
213
-
214
- end
215
-
216
- context 'full statements' do
217
-
218
- it 'parses Example 2' do
219
- sql = <<EOF
220
- CREATE TABLE PROJ
221
- (PROJNO CHAR(6) NOT NULL,
222
- PROJNAME VARCHAR(24) NOT NULL,
223
- DEPTNO CHAR(3) NOT NULL,
224
- RESPEMP CHAR(6) NOT NULL,
225
- PRSTAFF DECIMAL(5,2) ,
226
- PRSTDATE DATE ,
227
- PRENDATE DATE ,
228
- MAJPROJ CHAR(6) NOT NULL)
229
- IN SCHED
230
- EOF
231
- expect(parser).to parse(sql.downcase)
232
- end
233
-
234
- it 'parses Example 3' do
235
- sql = <<EOF
236
- CREATE TABLE EMPLOYEE_SALARY
237
- (DEPTNO CHAR(3) NOT NULL,
238
- DEPTNAME VARCHAR(36) NOT NULL,
239
- EMPNO CHAR(6) NOT NULL,
240
- SALARY DECIMAL(9,2) NOT NULL WITH DEFAULT)
241
- EOF
242
- expect(parser).to parse(sql.downcase)
243
- end
244
-
245
- it 'parses Example 6' do
246
- pending 'we do not support CHECK statements'
247
- sql = <<EOF
248
- CREATE TABLE EMPLOYEE
249
- (ID SMALLINT NOT NULL,
250
- NAME VARCHAR(9),
251
- DEPT SMALLINT CHECK (DEPT BETWEEN 10 AND 100),
252
- JOB CHAR(5) CHECK (JOB IN ('Sales','Mgr','Clerk')),
253
- HIREDATE DATE,
254
- SALARY DECIMAL(7,2),
255
- COMM DECIMAL(7,2),
256
- PRIMARY KEY (ID),
257
- CONSTRAINT YEARSAL CHECK (YEAR(HIREDATE) > 1986
258
- OR SALARY > 40500)
259
- )
260
- IN HUMRES
261
- EOF
262
- begin
263
- result = parser.parse(sql.downcase)
264
- # rescue Parslet::ParseFailed => error
265
- # puts error.cause.ascii_tree
266
- end
267
- result.should == [{:operation=> 'create table', :table_name=> 'emp_act'}]
268
- end
269
-
270
- it 'parses Example 11' do
271
- sql = <<EOF
272
- CREATE TABLE EMP_ACT
273
- (EMPNO CHAR(6) NOT NULL,
274
- PROJNO CHAR(6) NOT NULL,
275
- ACTNO SMALLINT NOT NULL,
276
- EMPTIME DECIMAL(5,2),
277
- EMSTDATE DATE,
278
- EMENDATE DATE,
279
- CONSTRAINT EMP_ACT_UNIQ UNIQUE (EMPNO,PROJNO,ACTNO),
280
- CONSTRAINT FK_ACT_PROJ FOREIGN KEY (PROJNO)
281
- REFERENCES PROJECT (PROJNO) ON DELETE CASCADE
282
- )
283
- IN SCHED
284
- EOF
285
- begin
286
- result = parser.parse(sql.downcase)
287
- rescue Parslet::ParseFailed => error
288
- puts error.cause.ascii_tree
289
- end
290
- result.should == {:operation=> 'create table',
291
- :table_name=> 'emp_act',
292
- :elements => [
293
- {:column=>
294
- {:field=> 'empno',
295
- :data_type=>{:char=>{:length=>{:integer=> '6'}}},
296
- :options=>[{:column_option=> 'not null'}]}},
297
- {:column=>
298
- {:field=> 'projno',
299
- :data_type=>{:char=>{:length=>{:integer=> '6'}}},
300
- :options=>[{:column_option=> 'not null'}]}},
301
- {:column=>
302
- {:field=> 'actno',
303
- :data_type=> 'smallint',
304
- :options=>[{:column_option=> 'not null'}]}},
305
- {:column=>
306
- {:field=> 'emptime',
307
- :data_type=>
308
- {:decimal=>
309
- {:precision=>
310
- {:total=>{:integer=> '5'}, :scale=>{:integer=> '2'}}}},
311
- :options=> ''}},
312
- {:column=>{:field=> 'emstdate', :data_type=> 'date', :options=> ''}},
313
- {:column=>{:field=> 'emendate', :data_type=> 'date', :options=> ''}},
314
- {:constraint=>
315
- {:column_name=> 'emp_act_uniq',
316
- :constraint_type=> 'unique',
317
- :constraint_arglist=>
318
- [{:item=> 'empno'}, {:item=> 'projno'}, {:item=> 'actno'}]}},
319
- {:constraint=>
320
- {:column_name=> 'fk_act_proj',
321
- :constraint_type=> 'foreign key',
322
- :constraint_arglist=>{:item=> 'projno'},
323
- :reference_arglist=>{:item=> 'projno'}}}
324
- ]}
325
-
326
- end
327
-
328
- it 'parses Example 12' do
329
- sql = <<EOF
330
- CREATE TABLE HOCKEY_GOALS
331
- ( BY_PLAYER VARCHAR(30) NOT NULL,
332
- BY_TEAM VARCHAR(30) NOT NULL,
333
- AGAINST_PLAYER VARCHAR(30) NOT NULL,
334
- AGAINST_TEAM VARCHAR(30) NOT NULL,
335
- DATE_OF_GOAL DATE NOT NULL,
336
- DESCRIPTION CLOB(5000))
337
- EOF
338
- begin
339
- result = parser.parse(sql.downcase)
340
- rescue Parslet::ParseFailed => error
341
- puts error.cause.ascii_tree
342
- end
343
- result.should == {:operation=> 'create table', :table_name=> 'hockey_goals',
344
- :elements => [
345
- {:column=>{:field=> 'by_player', :data_type=>{:varchar=>{:length=>{:integer=> '30'}}}, :options=>[{:column_option=> 'not null'}]}},
346
- {:column=>{:field=> 'by_team', :data_type=>{:varchar=>{:length=>{:integer=> '30'}}}, :options=>[{:column_option=> 'not null'}]}},
347
- {:column=>{:field=> 'against_player', :data_type=>{:varchar=>{:length=>{:integer=> '30'}}}, :options=>[{:column_option=> 'not null'}]}},
348
- {:column=>{:field=> 'against_team', :data_type=>{:varchar=>{:length=>{:integer=> '30'}}}, :options=>[{:column_option=> 'not null'}]}},
349
- {:column=>{:field=> 'date_of_goal', :data_type=> 'date', :options=>[{:column_option=> 'not null'}]}},
350
- {:column=>{:field=> 'description', :data_type=>{:clob=>{:length=>{:integer=> '5000'}}}, :options=> ''}}]}
351
- end
352
-
353
- it 'parses Example 16' do
354
- sql = <<EOF
355
- CREATE TABLE DEPT
356
- (DEPTNO SMALLINT NOT NULL
357
- GENERATED ALWAYS AS IDENTITY
358
- (START WITH 500, INCREMENT BY 1),
359
- DEPTNAME VARCHAR(36) NOT NULL,
360
- MGRNO CHAR(6),
361
- ADMRDEPT SMALLINT NOT NULL,
362
- LOCATION CHAR(30))
363
- EOF
364
-
365
- begin
366
- result = parser.parse(sql.downcase)
367
- rescue Parslet::ParseFailed => error
368
- puts error.cause.ascii_tree
369
- end
370
-
371
- result.should == {:operation=> 'create table',
372
- :table_name=> 'dept',
373
- :elements => [
374
- {:column=>{:field=> 'deptno',
375
- :data_type=> 'smallint',
376
- :options=>[{:column_option=> 'not null'},
377
- {:identity=>{:start_value=>{:integer=> '500'}, :increment_value=>{:integer=> '1'}}}]}},
378
- {:column=>{:field=> 'deptname',
379
- :data_type=>{:varchar=>{:length=>{:integer=> '36'}}},
380
- :options=>[{:column_option=> 'not null'}]}},
381
- {:column=>{:field=> 'mgrno',
382
- :data_type=>{:char=>{:length=>{:integer=> '6'}}},
383
- :options=> ''}},
384
- {:column=>{:field=> 'admrdept',
385
- :data_type=> 'smallint',
386
- :options=>[{:column_option=> 'not null'}]}},
387
- {:column=>{:field=> 'location',
388
- :data_type=>{:char=>{:length=>{:integer=> '30'}}},
389
- :options=> ''}}]}
390
- end
391
- end
392
-
393
- context 'real dsv examples 2' do
394
- it 'parses Example 16a' do
395
- sql = <<EOF
396
- CREATE TABLE COSTINVOICE
397
- (DOC_NO CHARACTER(8) NOT NULL,
398
- STATUS CHARACTER(12))
399
- EOF
400
-
401
- begin
402
- result = parser.parse(sql.downcase)
403
- rescue Parslet::ParseFailed => error
404
- puts error.cause.ascii_tree
405
- end
406
-
407
- result.should == {:operation=> 'create table',
408
- :table_name=> 'costinvoice',
409
- :elements => [
410
- {:column=>{:field=> 'doc_no',
411
- :data_type=>{:char=>{:length=>{:integer=> '8'}}},
412
- :options=>[{:column_option=> 'not null'}]}},
413
- {:column=>{:field=> 'status',
414
- :data_type=>{:char=>{:length=>{:integer=> '12'}}},
415
- :options=> ''}}]}
416
- end
417
- end
418
-
419
- context 'real dsv examples' do
420
- it 'parses Example 16' do
421
-
422
- sql = <<EOF
423
- Create table USER (USER_RW CHAR(4) NOT NULL,
424
- USER_NAME CHAR(20) NOT NULL,
425
- SECURITY_REPORT CHAR(2) NOT NULL,
426
- SECURITY_AUTH CHAR(2) NOT NULL,
427
- SECURITY_CORRECT CHAR(2) NOT NULL,
428
- SECURITY_REVIEW CHAR(2) NOT NULL,
429
- SECURITY_CHIEF CHAR(2) NOT NULL,
430
- PASSWORD CHAR(6) NOT NULL,
431
- SORT_SEQUENTIAL CHAR(4) NOT NULL,
432
- CREATE_TS TIMESTAMP NOT NULL With Default CURRENT TIMESTAMP,
433
- UPDATE_TS TIMESTAMP NOT NULL With Default CURRENT TIMESTAMP,
434
- UNIQUE_NO INTEGER NOT NULL
435
- Generated By Default as Identity (Start with 1 Increment by 1 cache 20))
436
- in CPH_TS001
437
- EOF
438
-
439
- begin
440
- result = parser.parse(sql.downcase)
441
- rescue Parslet::ParseFailed => error
442
- puts error.cause.ascii_tree
443
- end
444
-
445
- result[:operation].should == 'create table'
446
- result[:table_name].should == 'user'
447
- end
448
- it 'parses Example 17' do
449
-
450
- sql = <<EOF
451
- CREATE TABLE COST_INVOICE
452
- (DOC_NO CHARACTER(8) NOT NULL,
453
- STATUS CHARACTER(12) NOT NULL,
454
- DISPUTE CHARACTER(8),
455
- SAP_STATUS CHARACTER(1),
456
- DEPT CHARACTER(5) NOT NULL,
457
- FROM_DEPT CHARACTER(5) NOT NULL,
458
- CUSTOMER_NO DECIMAL(12) NOT NULL,
459
- SENDER_DOC_NO CHARACTER(8) NOT NULL,
460
- INVOICE_DATE DATE NOT NULL,
461
- DUE_DATE DATE NOT NULL,
462
- LASTUSER CHARACTER(4) NOT NULL,
463
- CREATE_TS TIMESTAMP NOT NULL,
464
- UPDATE_TS TIMESTAMP NOT NULL,
465
- UPDATE_TS_REAL_TIME TIMESTAMP NOT NULL,
466
- UNIQUE_NO INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1 INCREMENT BY 1 CACHE 20))
467
- IN CPH_TS001
468
- EOF
469
-
470
- begin
471
- result = parser.parse(sql.downcase)
472
- rescue Parslet::ParseFailed => error
473
- puts error.cause.ascii_tree
474
- end
475
-
476
- result[:operation].should == 'create table'
477
- result[:table_name].should == 'cost_invoice'
478
- end
479
- end
480
- context 'real dsv examples 3' do
481
- it 'parses Example 18' do
482
- sql = <<EOF
483
- CREATE TABLE COSTINVOICE
484
- (DOC_NO CHARACTER(8) NOT NULL DEFAULT 'TEST DEFAULT',
485
- STATUS CHARACTER(12))
486
-
487
- EOF
488
-
489
- begin
490
- result = parser.parse(sql.downcase)
491
- rescue Parslet::ParseFailed => error
492
- puts error.cause.ascii_tree
493
- end
494
-
495
- result.should == {:operation=>"create table",
496
- :table_name=>"costinvoice",
497
- :elements=>[{:column=>{:field=>"doc_no",
498
- :data_type=>{:char=>{:length=>{:integer=>"8"}}},
499
- :options=>[{:column_option=>"not null"},
500
- {:default_clause=>[{:string=>"'test default'"}]}]}},
501
- {:column=>{:field=>"status",
502
- :data_type=>{:char=>{:length=>{:integer=>"12"}}}, :options=>""}}]}
503
- end
504
-
505
- end
506
- end
1
+ require_relative '../../../../spec/spec_helper'
2
+
3
+ describe DDLParser::DDL::DB2::Parser do
4
+ let(:parser) { DDLParser::DDL::DB2::Parser.new }
5
+
6
+ context 'building blocks' do
7
+
8
+ it 'parses arglist' do
9
+ parser.arglist.parse('a').should == {:item=> 'a'}
10
+ parser.arglist.parse('a,b,c').should == [{:item=> 'a'}, {:item=> 'b'}, {:item=> 'c'}]
11
+ parser.arglist.parse('a, b, c').should == [{:item=> 'a'}, {:item=> 'b'}, {:item=> 'c'}]
12
+ end
13
+
14
+ end
15
+
16
+ context 'column options parsing' do
17
+ it 'parses not null' do
18
+ expect(parser.column_options).to parse('not null')
19
+ end
20
+
21
+ it 'parses default values' do
22
+ parser.column_options.parse('default 1').should == [{:default_clause=>[{:integer=> '1'}]}]
23
+ parser.column_options.parse('with default 1').should == [{:default_clause=>[{:integer=> '1'}]}]
24
+ parser.column_options.parse('default current timestamp').should == [{:default_clause=>[{:timestamp => "current timestamp"}]}]
25
+ parser.column_options.parse("default 'foobar'").should == [{:default_clause=>[{:string => "'foobar'"}]}]
26
+ parser.column_options.parse('default max(foo)').should == [{:default_clause=>[{:function=>{:name=>"max", :arguments=>{:item=>"foo"}}}]}]
27
+
28
+ expect(
29
+ parser.column_options.parse('default 1')
30
+ ).to eq([{:default_clause=>[{:integer=> '1'}]}])
31
+ end
32
+
33
+ it 'parses generated by' do
34
+ sql = 'generated by default as identity (start with 1 increment by 1 cache 20 )'
35
+ begin
36
+ result = parser.column_options.parse(sql.downcase)
37
+ rescue Parslet::ParseFailed => error
38
+ puts error.cause.ascii_tree
39
+ end
40
+ result.should == [{:identity=>{:start_value=>{:integer=> '1'},
41
+ :increment_value=>{:integer=> '1'},
42
+ :cache_value=>{:integer=> '20'}}}]
43
+ end
44
+
45
+ it 'parses generated by' do
46
+ sql = 'generated by default as identity (start with 1, increment by 1, cache 20 )'
47
+ begin
48
+ result = parser.column_options.parse(sql.downcase)
49
+ rescue Parslet::ParseFailed => error
50
+ puts error.cause.ascii_tree
51
+ end
52
+ result.should == [{:identity=>{:start_value=>{:integer=> '1'},
53
+ :increment_value=>{:integer=> '1'},
54
+ :cache_value=>{:integer=> '20'}}}]
55
+ end
56
+
57
+ it 'parses identity' do
58
+ expect(parser.column_options).to parse('as identity (start with 500, increment by 1)')
59
+ expect(
60
+ parser.column_options.parse('as identity (start with 500, increment by 1)')
61
+ ).to eq([{:identity=>{:start_value=>{:integer=> '500'}, :increment_value=>{:integer=> '1'}}}])
62
+ end
63
+
64
+ it 'parses multible' do
65
+ expect(parser.column_options).to parse('not null as identity (start with 500, increment by 1)')
66
+
67
+ result = parser.column_options.parse('not null as identity (start with 500, increment by 1)')
68
+ result.first.should include(:column_option)
69
+ result.last.should include(:identity)
70
+ end
71
+
72
+ end
73
+
74
+ context 'constraint' do
75
+ it 'parses primary key' do
76
+ sql = 'primary key (id)'
77
+ parser.primary_key.parse(sql).should == {:primary_key=>{:item=> 'id'}}
78
+ end
79
+
80
+ it 'parses composed primary key' do
81
+ sql = 'primary key (id,foo)'
82
+ parser.primary_key.parse(sql).should == {:primary_key=>[{:item=> 'id'}, {:item=> 'foo'}]}
83
+ end
84
+
85
+ it 'parses unique' do
86
+ sql = 'constraint emp_act_uniq unique (empno,projno,actno)'
87
+
88
+ parser.constraint.parse(sql).should == {:constraint=>{:column_name=> 'emp_act_uniq',
89
+ :constraint_type=> 'unique',
90
+ :constraint_arglist=>[{:item=> 'empno'},
91
+ {:item=> 'projno'},
92
+ {:item=> 'actno'}]}}
93
+ end
94
+
95
+ it 'parses foreign key' do
96
+ sql = 'constraint fk_act_proj foreign key (projno) references project (projno) on delete cascade'
97
+ begin
98
+ result = parser.constraint.parse(sql)
99
+ rescue Parslet::ParseFailed => error
100
+ puts error.cause.ascii_tree
101
+ end
102
+ result.should == {:constraint=>{:column_name=> 'fk_act_proj',
103
+ :constraint_type=> 'foreign key',
104
+ :constraint_arglist=>{:item=> 'projno'},
105
+ :reference_arglist=>{:item=> 'projno'}}}
106
+ end
107
+
108
+ end
109
+
110
+ context 'column definition' do
111
+ it 'parses char not null' do
112
+ sql = 'deptno char(3) not null'
113
+ parser.column_definition.parse(sql).should == {:column => {
114
+ :field=> 'deptno',
115
+ :data_type=>{:char=>{:length=>{:integer=> '3'}}},
116
+ :options=>[{:column_option=> 'not null'}]
117
+ }
118
+ }
119
+
120
+ end
121
+ it 'parses char not null - x' do
122
+ sql = 'BUDGET_AMOUNT_IN_CO DECIMAL(15,2) NOT NULL DEFAULT 0'
123
+ parser.column_definition.parse(sql.downcase).should == {:column => {
124
+ :field=> 'budget_amount_in_co',
125
+ :data_type=>{:decimal=>{:precision=>
126
+ {:total=>{:integer=> '15'},
127
+ :scale=>{:integer=> '2'}}}},
128
+ :options=>[{:column_option=> 'not null'}, {:default_clause=>[{:integer=> '0'}]}]
129
+ }
130
+ }
131
+
132
+ end
133
+
134
+ end
135
+
136
+ context 'element list' do
137
+ it 'parses one element' do
138
+ sql = '(deptno char(3) not null)'
139
+ parser.element_list.parse(sql).should == {:elements => {:column=>{:field=> 'deptno',
140
+ :data_type=>{:char=>{:length=>{:integer=> '3'}}},
141
+ :options=>[{:column_option=> 'not null'}]}}}
142
+ end
143
+
144
+ it 'parses two element' do
145
+ sql = '(field1 char(1) not null, field2 char(2))'
146
+ parser.element_list.parse(sql).should == {:elements => [{:column=>{:field=> 'field1',
147
+ :data_type=>{:char=>{:length=>{:integer=> '1'}}},
148
+ :options=>[{:column_option=> 'not null'}]}},
149
+ {:column=>{:field=> 'field2',
150
+ :data_type=>{:char=>{:length=>{:integer=> '2'}}},
151
+ :options=> ''}}]}
152
+ end
153
+
154
+ it 'parses with new lines' do
155
+ sql = <<EOF
156
+ (
157
+ DEPTNO CHAR(3) NOT NULL,
158
+ DEPTNAME VARCHAR(36) NOT NULL
159
+ )
160
+ EOF
161
+ parser.element_list.parse(sql.downcase).should == {:elements => [{:column=>{:field=> 'deptno',
162
+ :data_type=>{:char=>{:length=>{:integer=> '3'}}},
163
+ :options=>[{:column_option=> 'not null'}]}},
164
+ {:column=>{:field=> 'deptname',
165
+ :data_type=>{:varchar=>{:length=>{:integer=> '36'}}},
166
+ :options=>[{:column_option=> 'not null'}]}}]}
167
+
168
+ end
169
+ end
170
+
171
+ context 'create table' do
172
+ it 'parses simple create' do
173
+ expect(parser.create_table_statement).to parse('create table foobar')
174
+ expect(parser.create_table_statement).not_to parse('create table ')
175
+
176
+ parser.create_table_statement.parse('create table foobar').should == {:operation=> 'create table', :table_name => 'foobar'}
177
+ end
178
+ end
179
+
180
+
181
+
182
+ context 'simple statements' do
183
+
184
+ it 'parses Example 1' do
185
+ sql = <<EOF
186
+ CREATE TABLE TDEPT
187
+ (DEPTNO CHAR(3) NOT NULL,
188
+ DEPTNAME VARCHAR(36) NOT NULL,
189
+ MGRNO INT,
190
+ ADMRDEPT DECIMAL(3,1) NOT NULL)
191
+ IN DEPARTX
192
+ EOF
193
+ parser.create_table.parse(sql.downcase).should == {:operation=> 'create table',
194
+ :table_name=> 'tdept',
195
+ :elements => [
196
+ {:column=>{:field=> 'deptno',
197
+ :data_type=>{:char=>{:length=>{:integer=> '3'}}},
198
+ :options=>[{:column_option=> 'not null'}]}},
199
+ {:column=>{:field=> 'deptname',
200
+ :data_type=>{:varchar=>{:length=>{:integer=> '36'}}},
201
+ :options=>[{:column_option=> 'not null'}]}},
202
+ {:column=>{:field=> 'mgrno',
203
+ :data_type=> 'int',
204
+ :options=> ''}},
205
+ {:column=>{:field=> 'admrdept',
206
+ :data_type=>{:decimal=>{:precision=>{:total=>{:integer=> '3'}, :scale=>{:integer=> '1'}}}},
207
+ :options=>[{:column_option=> 'not null'}]}}
208
+ ]}
209
+ end
210
+
211
+ it 'parses with primary key' do
212
+ sql = <<EOF
213
+ CREATE TABLE TEST
214
+ (ID INT,
215
+ PRIMARY KEY (ID))
216
+ EOF
217
+ parser.create_table.parse(sql.downcase).should == {:operation=> 'create table',
218
+ :table_name=> 'test',
219
+ :elements=>[
220
+ {:column => {:field=> 'id', :data_type=> 'int', :options=> ''}},
221
+ {:primary_key=>{:item=> 'id'}}
222
+ ]
223
+ }
224
+ end
225
+
226
+ end
227
+
228
+ context 'full statements' do
229
+
230
+ it 'parses Example 2' do
231
+ sql = <<EOF
232
+ CREATE TABLE PROJ
233
+ (PROJNO CHAR(6) NOT NULL,
234
+ PROJNAME VARCHAR(24) NOT NULL,
235
+ DEPTNO CHAR(3) NOT NULL,
236
+ RESPEMP CHAR(6) NOT NULL,
237
+ PRSTAFF DECIMAL(5,2) ,
238
+ PRSTDATE DATE ,
239
+ PRENDATE DATE ,
240
+ MAJPROJ CHAR(6) NOT NULL)
241
+ IN SCHED
242
+ EOF
243
+ expect(parser).to parse(sql.downcase)
244
+ end
245
+
246
+ it 'parses Example 3' do
247
+ sql = <<EOF
248
+ CREATE TABLE EMPLOYEE_SALARY
249
+ (DEPTNO CHAR(3) NOT NULL,
250
+ DEPTNAME VARCHAR(36) NOT NULL,
251
+ EMPNO CHAR(6) NOT NULL,
252
+ SALARY DECIMAL(9,2) NOT NULL WITH DEFAULT)
253
+ EOF
254
+ expect(parser).to parse(sql.downcase)
255
+ end
256
+
257
+ it 'parses Example 6' do
258
+ pending 'we do not support CHECK statements'
259
+ sql = <<EOF
260
+ CREATE TABLE EMPLOYEE
261
+ (ID SMALLINT NOT NULL,
262
+ NAME VARCHAR(9),
263
+ DEPT SMALLINT CHECK (DEPT BETWEEN 10 AND 100),
264
+ JOB CHAR(5) CHECK (JOB IN ('Sales','Mgr','Clerk')),
265
+ HIREDATE DATE,
266
+ SALARY DECIMAL(7,2),
267
+ COMM DECIMAL(7,2),
268
+ PRIMARY KEY (ID),
269
+ CONSTRAINT YEARSAL CHECK (YEAR(HIREDATE) > 1986
270
+ OR SALARY > 40500)
271
+ )
272
+ IN HUMRES
273
+ EOF
274
+ begin
275
+ result = parser.parse(sql.downcase)
276
+ # rescue Parslet::ParseFailed => error
277
+ # puts error.cause.ascii_tree
278
+ end
279
+ result.should == [{:operation=> 'create table', :table_name=> 'emp_act'}]
280
+ end
281
+
282
+ it 'parses Example 11' do
283
+ sql = <<EOF
284
+ CREATE TABLE EMP_ACT
285
+ (EMPNO CHAR(6) NOT NULL,
286
+ PROJNO CHAR(6) NOT NULL,
287
+ ACTNO SMALLINT NOT NULL,
288
+ EMPTIME DECIMAL(5,2),
289
+ EMSTDATE DATE,
290
+ EMENDATE DATE,
291
+ CONSTRAINT EMP_ACT_UNIQ UNIQUE (EMPNO,PROJNO,ACTNO),
292
+ CONSTRAINT FK_ACT_PROJ FOREIGN KEY (PROJNO)
293
+ REFERENCES PROJECT (PROJNO) ON DELETE CASCADE
294
+ )
295
+ IN SCHED
296
+ EOF
297
+ begin
298
+ result = parser.parse(sql.downcase)
299
+ rescue Parslet::ParseFailed => error
300
+ puts error.cause.ascii_tree
301
+ end
302
+ result.should == {:operation=> 'create table',
303
+ :table_name=> 'emp_act',
304
+ :elements => [
305
+ {:column=>
306
+ {:field=> 'empno',
307
+ :data_type=>{:char=>{:length=>{:integer=> '6'}}},
308
+ :options=>[{:column_option=> 'not null'}]}},
309
+ {:column=>
310
+ {:field=> 'projno',
311
+ :data_type=>{:char=>{:length=>{:integer=> '6'}}},
312
+ :options=>[{:column_option=> 'not null'}]}},
313
+ {:column=>
314
+ {:field=> 'actno',
315
+ :data_type=> 'smallint',
316
+ :options=>[{:column_option=> 'not null'}]}},
317
+ {:column=>
318
+ {:field=> 'emptime',
319
+ :data_type=>
320
+ {:decimal=>
321
+ {:precision=>
322
+ {:total=>{:integer=> '5'}, :scale=>{:integer=> '2'}}}},
323
+ :options=> ''}},
324
+ {:column=>{:field=> 'emstdate', :data_type=> 'date', :options=> ''}},
325
+ {:column=>{:field=> 'emendate', :data_type=> 'date', :options=> ''}},
326
+ {:constraint=>
327
+ {:column_name=> 'emp_act_uniq',
328
+ :constraint_type=> 'unique',
329
+ :constraint_arglist=>
330
+ [{:item=> 'empno'}, {:item=> 'projno'}, {:item=> 'actno'}]}},
331
+ {:constraint=>
332
+ {:column_name=> 'fk_act_proj',
333
+ :constraint_type=> 'foreign key',
334
+ :constraint_arglist=>{:item=> 'projno'},
335
+ :reference_arglist=>{:item=> 'projno'}}}
336
+ ]}
337
+
338
+ end
339
+
340
+ it 'parses Example 12' do
341
+ sql = <<EOF
342
+ CREATE TABLE HOCKEY_GOALS
343
+ ( BY_PLAYER VARCHAR(30) NOT NULL,
344
+ BY_TEAM VARCHAR(30) NOT NULL,
345
+ AGAINST_PLAYER VARCHAR(30) NOT NULL,
346
+ AGAINST_TEAM VARCHAR(30) NOT NULL,
347
+ DATE_OF_GOAL DATE NOT NULL,
348
+ DESCRIPTION CLOB(5000))
349
+ EOF
350
+ begin
351
+ result = parser.parse(sql.downcase)
352
+ rescue Parslet::ParseFailed => error
353
+ puts error.cause.ascii_tree
354
+ end
355
+ result.should == {:operation=> 'create table', :table_name=> 'hockey_goals',
356
+ :elements => [
357
+ {:column=>{:field=> 'by_player', :data_type=>{:varchar=>{:length=>{:integer=> '30'}}}, :options=>[{:column_option=> 'not null'}]}},
358
+ {:column=>{:field=> 'by_team', :data_type=>{:varchar=>{:length=>{:integer=> '30'}}}, :options=>[{:column_option=> 'not null'}]}},
359
+ {:column=>{:field=> 'against_player', :data_type=>{:varchar=>{:length=>{:integer=> '30'}}}, :options=>[{:column_option=> 'not null'}]}},
360
+ {:column=>{:field=> 'against_team', :data_type=>{:varchar=>{:length=>{:integer=> '30'}}}, :options=>[{:column_option=> 'not null'}]}},
361
+ {:column=>{:field=> 'date_of_goal', :data_type=> 'date', :options=>[{:column_option=> 'not null'}]}},
362
+ {:column=>{:field=> 'description', :data_type=>{:clob=>{:length=>{:integer=> '5000'}}}, :options=> ''}}]}
363
+ end
364
+
365
+ it 'parses Example 16' do
366
+ sql = <<EOF
367
+ CREATE TABLE DEPT
368
+ (DEPTNO SMALLINT NOT NULL
369
+ GENERATED ALWAYS AS IDENTITY
370
+ (START WITH 500, INCREMENT BY 1),
371
+ DEPTNAME VARCHAR(36) NOT NULL,
372
+ MGRNO CHAR(6),
373
+ ADMRDEPT SMALLINT NOT NULL,
374
+ LOCATION CHAR(30))
375
+ EOF
376
+
377
+ begin
378
+ result = parser.parse(sql.downcase)
379
+ rescue Parslet::ParseFailed => error
380
+ puts error.cause.ascii_tree
381
+ end
382
+
383
+ result.should == {:operation=> 'create table',
384
+ :table_name=> 'dept',
385
+ :elements => [
386
+ {:column=>{:field=> 'deptno',
387
+ :data_type=> 'smallint',
388
+ :options=>[{:column_option=> 'not null'},
389
+ {:identity=>{:start_value=>{:integer=> '500'}, :increment_value=>{:integer=> '1'}}}]}},
390
+ {:column=>{:field=> 'deptname',
391
+ :data_type=>{:varchar=>{:length=>{:integer=> '36'}}},
392
+ :options=>[{:column_option=> 'not null'}]}},
393
+ {:column=>{:field=> 'mgrno',
394
+ :data_type=>{:char=>{:length=>{:integer=> '6'}}},
395
+ :options=> ''}},
396
+ {:column=>{:field=> 'admrdept',
397
+ :data_type=> 'smallint',
398
+ :options=>[{:column_option=> 'not null'}]}},
399
+ {:column=>{:field=> 'location',
400
+ :data_type=>{:char=>{:length=>{:integer=> '30'}}},
401
+ :options=> ''}}]}
402
+ end
403
+ end
404
+
405
+ context 'real dsv examples 2' do
406
+ it 'parses Example 16a' do
407
+ sql = <<EOF
408
+ CREATE TABLE COSTINVOICE
409
+ (DOC_NO CHARACTER(8) NOT NULL,
410
+ STATUS CHARACTER(12))
411
+ EOF
412
+
413
+ begin
414
+ result = parser.parse(sql.downcase)
415
+ rescue Parslet::ParseFailed => error
416
+ puts error.cause.ascii_tree
417
+ end
418
+
419
+ result.should == {:operation=> 'create table',
420
+ :table_name=> 'costinvoice',
421
+ :elements => [
422
+ {:column=>{:field=> 'doc_no',
423
+ :data_type=>{:char=>{:length=>{:integer=> '8'}}},
424
+ :options=>[{:column_option=> 'not null'}]}},
425
+ {:column=>{:field=> 'status',
426
+ :data_type=>{:char=>{:length=>{:integer=> '12'}}},
427
+ :options=> ''}}]}
428
+ end
429
+ end
430
+
431
+ context 'real dsv examples' do
432
+ it 'parses Example 16' do
433
+
434
+ sql = <<EOF
435
+ Create table USER (USER_RW CHAR(4) NOT NULL,
436
+ USER_NAME CHAR(20) NOT NULL,
437
+ SECURITY_REPORT CHAR(2) NOT NULL,
438
+ SECURITY_AUTH CHAR(2) NOT NULL,
439
+ SECURITY_CORRECT CHAR(2) NOT NULL,
440
+ SECURITY_REVIEW CHAR(2) NOT NULL,
441
+ SECURITY_CHIEF CHAR(2) NOT NULL,
442
+ PASSWORD CHAR(6) NOT NULL,
443
+ SORT_SEQUENTIAL CHAR(4) NOT NULL,
444
+ CREATE_TS TIMESTAMP NOT NULL With Default CURRENT TIMESTAMP,
445
+ UPDATE_TS TIMESTAMP NOT NULL With Default CURRENT TIMESTAMP,
446
+ UNIQUE_NO INTEGER NOT NULL
447
+ Generated By Default as Identity (Start with 1 Increment by 1 cache 20))
448
+ in CPH_TS001
449
+ EOF
450
+
451
+ begin
452
+ result = parser.parse(sql.downcase)
453
+ rescue Parslet::ParseFailed => error
454
+ puts error.cause.ascii_tree
455
+ end
456
+
457
+ result[:operation].should == 'create table'
458
+ result[:table_name].should == 'user'
459
+ end
460
+ it 'parses Example 17' do
461
+
462
+ sql = <<EOF
463
+ CREATE TABLE COST_INVOICE
464
+ (DOC_NO CHARACTER(8) NOT NULL,
465
+ STATUS CHARACTER(12) NOT NULL,
466
+ DISPUTE CHARACTER(8),
467
+ SAP_STATUS CHARACTER(1),
468
+ DEPT CHARACTER(5) NOT NULL,
469
+ FROM_DEPT CHARACTER(5) NOT NULL,
470
+ CUSTOMER_NO DECIMAL(12) NOT NULL,
471
+ SENDER_DOC_NO CHARACTER(8) NOT NULL,
472
+ INVOICE_DATE DATE NOT NULL,
473
+ DUE_DATE DATE NOT NULL,
474
+ LASTUSER CHARACTER(4) NOT NULL,
475
+ CREATE_TS TIMESTAMP NOT NULL,
476
+ UPDATE_TS TIMESTAMP NOT NULL,
477
+ UPDATE_TS_REAL_TIME TIMESTAMP NOT NULL,
478
+ UNIQUE_NO INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1 INCREMENT BY 1 CACHE 20))
479
+ IN CPH_TS001
480
+ EOF
481
+
482
+ begin
483
+ result = parser.parse(sql.downcase)
484
+ rescue Parslet::ParseFailed => error
485
+ puts error.cause.ascii_tree
486
+ end
487
+
488
+ result[:operation].should == 'create table'
489
+ result[:table_name].should == 'cost_invoice'
490
+ end
491
+ end
492
+ context 'real dsv examples 3' do
493
+ it 'parses Example 18' do
494
+ sql = <<EOF
495
+ CREATE TABLE COSTINVOICE
496
+ (DOC_NO CHARACTER(8) NOT NULL DEFAULT 'TEST DEFAULT',
497
+ STATUS CHARACTER(12))
498
+
499
+ EOF
500
+
501
+ begin
502
+ result = parser.parse(sql.downcase)
503
+ rescue Parslet::ParseFailed => error
504
+ puts error.cause.ascii_tree
505
+ end
506
+
507
+ result.should == {:operation=>"create table",
508
+ :table_name=>"costinvoice",
509
+ :elements=>[{:column=>{:field=>"doc_no",
510
+ :data_type=>{:char=>{:length=>{:integer=>"8"}}},
511
+ :options=>[{:column_option=>"not null"},
512
+ {:default_clause=>[{:string=>"'test default'"}]}]}},
513
+ {:column=>{:field=>"status",
514
+ :data_type=>{:char=>{:length=>{:integer=>"12"}}}, :options=>""}}]}
515
+ end
516
+
517
+ end
518
+ end