Skip to content

Commit b1ba408

Browse files
Finished error diagnostics
1 parent c36bc10 commit b1ba408

File tree

7 files changed

+220
-118
lines changed

7 files changed

+220
-118
lines changed

doc/error_diagnostics.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Error Diagnostics
2+
3+
# API
4+
5+
Error diagnostics procedure is called like this
6+
7+
8+
let output = ddl.errorMsgs(`dept
9+
col1
10+
"is this table or misaligned column?"`);
11+
12+
13+
It returns an array of the Error objects
14+
15+
16+
function SyntaxError( message, from, to, severity ) {
17+
this.from = from;
18+
this.to = to;
19+
if( to == null )
20+
this.to = new Offset(from.line, from.depth+1);
21+
this.message = message;
22+
this.severity = severity; // warning or error
23+
if( severity == null )
24+
this.severity = 'error';
25+
}
26+
27+
28+
where error message offsets are stuctured as follows
29+
30+
31+
function Offset( line, depth ) {
32+
this.line = line; // 0-based
33+
this.depth = depth; // 0-based
34+
}
35+
36+
37+
# Error Types
38+
39+
## Misaligned Attributes
40+
41+
Indentation refers to the spaces at the beginning of a code line. Where in most of the other other programming languages the indentation in code is for readability only, QSQL uses indentation to define QSQL tree/forest structure. Parent-child relationship in this tree is interpreted as ether table containing list of columns, or as 2 tables related with referential integrity key constraint.
42+
43+
Inconsistent indentation is detected, with the error message designed to resolve any confusion.
44+
45+
## Invalid Object references
46+
47+
### Invalid Table reference in a View
48+
49+
Exanple:
50+
51+
dept
52+
name
53+
54+
view customer_view customer
55+
56+
57+
Here the `customer` table (or view) is missing.
58+
59+
### Invalid foreign key
60+
61+
Exanple:
62+
63+
department
64+
name
65+
empployee
66+
dept /fk dept
67+
68+
Here the `dept` table (or view) is missing.
69+
70+
### Duplicate ID
71+
72+
Since identity columns are generated by default, specifying them explictly like this
73+
74+
dept
75+
id
76+
77+
would be diagnosed an error.
78+
79+
### Misconstructed column type
80+
81+
QSQL also recognizes syntactically invalid column types like this
82+
83+
dept
84+
name vc-200
85+
name vc0
86+
87+
88+
89+
File renamed without changes.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@oracle/quicksql",
3-
"version": "1.1.1",
3+
"version": "1.1.2",
44
"description": "Quick SQL to DDL translator and GUI",
55
"main": "src/index.js",
66
"type": "module",

src/ddl.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ const ddl = (function () {
444444
this.toDDL(input, options);
445445
this.options = JSON.parse(JSON.stringify(this.appliedOptions));
446446

447-
const ret = errorMsgs.findErrors(this);
447+
const ret = errorMsgs.findErrors(this, input);
448448

449449
this.appliedOptions = JSON.parse(JSON.stringify(this.options));
450450
this.resetOptions();

src/errorMsgs.js

Lines changed: 86 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import split_str from './split_str.js';
12

23
const findErrors = (function () {
34

4-
function ErrorMsg( message, from, to, severity ) {
5+
function SyntaxError( message, from, to, severity ) {
56
this.from = from;
67
this.to = to;
78
if( to == null )
@@ -17,30 +18,33 @@ const findErrors = (function () {
1718
this.depth = depth; // 0-based
1819
}
1920

20-
function checkSyntax( ddlInstance ) {
21+
function checkSyntax( ddlInstance, input ) {
2122
const ddl = ddlInstance;
2223

2324
let ret = [];
2425

26+
const lines = input.split("\n");
27+
28+
ret = ret.concat(line_mismatch(lines));
2529
const descendants = ddl.descendants();
2630

2731
for( let i = 0; i < descendants.length; i++ ) {
2832
const node = descendants[i];
2933
if( ddl.optionEQvalue('genpk', true) && descendants[i].parseName() == 'id' ) {
3034
const depth = node.content.toLowerCase().indexOf('id');
31-
ret.push(new ErrorMsg( messages.duplicateId, new Offset(node.line, depth) ));
35+
ret.push(new SyntaxError( messages.duplicateId, new Offset(node.line, depth) ));
3236
continue;
3337
}
3438
const src2 = node.src[2];
3539
if( 2 < node.src.length && src2.value == '-' ) {
3640
const depth = src2.begin;
37-
ret.push(new ErrorMsg( messages.invalidDatatype, new Offset(node.line,depth) ));
41+
ret.push(new SyntaxError( messages.invalidDatatype, new Offset(node.line,depth) ));
3842
continue;
3943
}
4044
const src1 = node.src[1];
4145
if( 1 < node.src.length && 0 < src1.value.indexOf('0') ) {
4246
const depth = src1.begin;
43-
ret.push(new ErrorMsg( messages.invalidDatatype, new Offset(node.line,depth) ));
47+
ret.push(new SyntaxError( messages.invalidDatatype, new Offset(node.line,depth) ));
4448
continue;
4549
}
4650

@@ -54,20 +58,23 @@ const findErrors = (function () {
5458
function ref_error_in_view( ddl, node ) {
5559
var ret = [];
5660

57-
var nodeUpperContent = node.trimmedContent().toUpperCase();
61+
var line = node.content.toLowerCase();
5862
if( node.parseType() == 'view' ) {
59-
var chunks = nodeUpperContent.split(' ');
60-
for( var j = 2; j < chunks.length; j++ ) {
61-
if( chunks[j].trim() == "" )
63+
var chunks = split_str(line,' '); //line.split(' ');
64+
let pos = 0;
65+
for( var j = 0; j < chunks.length; j++ ) {
66+
pos += chunks[j].length;
67+
if( chunks[j] == ' ' )
6268
continue;
63-
if( 0 == chunks[j].indexOf("/") )
69+
if( chunks[j] == 'view' )
6470
continue;
65-
var tbl = ddl.find(chunks[j]);
71+
if( j == 1 )
72+
continue;
73+
var tbl = ddl.find(chunks[j]);
6674
if( tbl == null ) {
67-
var pos = nodeUpperContent.indexOf(chunks[j]);
68-
ret.push( new ErrorMsg(
75+
ret.push( new SyntaxError(
6976
messages.undefinedObject+chunks[j],
70-
new Offset(node.line, pos)
77+
new Offset(node.line, pos-chunks[j].length)
7178
));
7279
}
7380
}
@@ -79,10 +86,12 @@ const findErrors = (function () {
7986
var ret = [];
8087
var line = node.content.toLowerCase();
8188
if( 0 < line.indexOf("/fk") || 0 < line.indexOf("/reference") ) {
82-
var chunks = line.split(' ');
83-
var refIsNext = false;
84-
for( var j = 1; j < chunks.length; j++ ) {
85-
if( chunks[j].trim() == "" )
89+
let chunks = split_str(line,' '); //line.split(' ');
90+
let pos = 0;
91+
let refIsNext = false;
92+
for( var j = 0; j < chunks.length; j++ ) {
93+
pos += chunks[j].length;
94+
if( chunks[j] == ' ' )
8695
continue;
8796
if( chunks[j] == "/fk" || 0 == chunks[j].indexOf("/reference") ) {
8897
refIsNext = true;
@@ -92,10 +101,9 @@ const findErrors = (function () {
92101
continue;
93102
var tbl = ddl.find(chunks[j]);
94103
if( tbl == null ) {
95-
var pos = line.indexOf(chunks[j]);
96-
ret.push( new ErrorMsg(
104+
ret.push( new SyntaxError(
97105
messages.undefinedObject+chunks[j],
98-
new Offset(node.line, pos)
106+
new Offset(node.line, pos-chunks[j].length)
99107
));
100108
break;
101109
}
@@ -104,110 +112,72 @@ const findErrors = (function () {
104112
return ret;
105113
}
106114

107-
108-
return checkSyntax;
109-
}());
110-
111-
function parse_errors( input ) {
112-
var ret = [];
113-
114-
var lines = input.split("\n");
115-
116-
if( lines.length < 5 || ddl == null )
117-
return ret;
118-
119-
ret = ret.concat(line_mismatch(lines));
120-
ddl.translate(input);
121-
ret = ret.concat(ref_error(lines));
122-
123-
return ret;
124-
}
125-
126-
function line_mismatch( lines ) {
127-
var ret = [];
128-
129-
var indent = guessIndent( lines )
130-
131-
for( var i = 1; i < lines.length; i++ ) {
132-
var priorline = lines[i-1];
133-
var line = lines[i];
115+
function line_mismatch( lines ) {
116+
var ret = [];
134117

135-
var priorIndent = apparentDepth(priorline);
136-
var lineIndent = apparentDepth(line);
118+
var indent = guessIndent( lines )
137119

138-
if( lineIndent == 0 )
139-
continue;
140-
141-
if( priorIndent < lineIndent && lineIndent < priorIndent+indent )
142-
ret.push({
143-
from: {line:i, ch:lineIndent, },
144-
to: {line:i, ch:lineIndent+1, },
145-
//severity: "error",
146-
message: "Misaligned code. \nThe indent appears to be "+indent+" spaces."
147-
});
120+
for( var i = 1; i < lines.length; i++ ) {
121+
var priorline = lines[i-1];
122+
var line = lines[i];
123+
124+
var priorIndent = depth(priorline);
125+
var lineIndent = depth(line);
126+
127+
if( lineIndent == 0 )
128+
continue;
129+
130+
if( priorIndent < lineIndent && lineIndent < priorIndent+indent )
131+
ret.push(new SyntaxError(
132+
messages.misalignedAttribute+indent,
133+
new Offset(i, lineIndent)
134+
)
135+
);
136+
}
137+
138+
return ret;
148139
}
140+
return checkSyntax;
141+
}());
142+
149143

150-
return ret;
151-
}
152144

153145
function guessIndent( lines ) {
154-
var idents = [];
155-
156-
var priorFullIndent = -1;
157-
var priorRelativeIndents = [];
158-
146+
let depths = [];
147+
159148
for( var i = 0; i < lines.length; i++ ) {
160149
var line = lines[i];
161-
if( "\n" == line )
162-
continue;
163-
164-
var ident = apparentDepth(line);
165-
166-
if( priorFullIndent == -1 ) {
167-
priorFullIndent = ident;
168-
priorRelativeIndents.push(ident);
169-
continue;
170-
}
171-
var index = ident - priorFullIndent;
172-
if( index == 0 ) {
173-
var tmp = priorRelativeIndents[priorRelativeIndents.length-1];
174-
if( tmp != 0 )
175-
index = tmp;
176-
}
177-
if( index < 0 ) {
178-
index = -index;
179-
priorRelativeIndents.length--;
180-
} else {
181-
if( priorFullIndent < ident)
182-
priorRelativeIndents.push(index);
183-
}
184-
if( index != 0 ) {
185-
if( idents[index] == null )
186-
idents[index] = 0;
187-
idents[index]++;
188-
}
189-
priorFullIndent = ident;
150+
depths[i] = depth(line);
190151
}
191-
var ret = 1;
192-
var cmp = idents[ret];
193-
if( cmp == null )
194-
cmp = 0;
195-
for( var i = 1; i < idents.length; i++ ) {
196-
if( cmp < idents[i] ) {
197-
ret = i;
198-
cmp = idents[i];
152+
153+
let frequencies = [];
154+
for( let i = 0; i < depths.length; i++ ) {
155+
let j = parentIndex(depths, i);
156+
if( j != null ) {
157+
let f = frequencies[depths[i]-depths[j]];
158+
if( f == null )
159+
f = 0;
160+
frequencies[depths[i]-depths[j]] = f+1;
199161
}
200162
}
201-
return ret;
163+
164+
let indent = null;
165+
for( let i in frequencies ) {
166+
if( indent == null || frequencies[indent] <= frequencies[i] )
167+
indent = i;
168+
}
169+
return indent;
170+
202171
}
203172

204-
function apparentDepth( line ) {
173+
function depth( line ) {
205174
var chunks = line.split(/ |\t/);
206175
var offset = 0;
207176
for( var j = 0; j < chunks.length; j++ ) {
208-
var chunk = chunks[j]/*.intern()*/;
209-
//if( "\t" == chunk )
210-
//TODO;
177+
var chunk = chunks[j];
178+
if( "\t" == chunk ) {
179+
offset += 4; //TODO;
180+
}
211181
if( "" == chunk ) {
212182
offset++;
213183
continue;
@@ -218,12 +188,19 @@ function apparentDepth( line ) {
218188
return 0;
219189
}
220190

191+
function parentIndex( depths, lineNo ) {
192+
for( let i = lineNo; 0 <= i; i-- )
193+
if( depths[i] < depths[lineNo] )
194+
return i;
195+
return null;
196+
}
221197

222198

223199
const messages = {
224200
duplicateId: 'Explicit ID column conflicts with genpk',
225201
invalidDatatype: 'Invalid Datatype',
226202
undefinedObject: 'Undefined Object: ',
203+
misalignedAttribute: 'Misaligned Table or Column; apparent indent = ',
227204
}
228205

229206
export default {findErrors, messages};

0 commit comments

Comments
 (0)