Skip to content

Commit 9f1d527

Browse files
committed
Error Positions
1 parent ca03818 commit 9f1d527

File tree

10 files changed

+964
-16
lines changed

10 files changed

+964
-16
lines changed

versions/17/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ ifdef EMSCRIPTEN
5757
-I$(LIBPG_QUERY_DIR) \
5858
-I$(LIBPG_QUERY_DIR)/vendor \
5959
-L$(LIBPG_QUERY_DIR) \
60-
-sEXPORTED_FUNCTIONS="['_malloc','_free','_wasm_parse_query','_wasm_free_string']" \
61-
-sEXPORTED_RUNTIME_METHODS="['lengthBytesUTF8','stringToUTF8','UTF8ToString','HEAPU8','HEAPU32']" \
60+
-sEXPORTED_FUNCTIONS="['_malloc','_free','_wasm_parse_query','_wasm_free_string','_wasm_parse_query_raw','_wasm_free_parse_result']" \
61+
-sEXPORTED_RUNTIME_METHODS="['lengthBytesUTF8','stringToUTF8','UTF8ToString','getValue','HEAPU8','HEAPU32']" \
6262
-sEXPORT_NAME="$(WASM_MODULE_NAME)" \
6363
-sENVIRONMENT="web,node" \
6464
-sMODULARIZE=1 \
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Enhanced Error Handling in libpg-query-node v17
2+
3+
## Overview
4+
5+
Version 17 includes enhanced error handling that provides detailed information about SQL parsing errors, including exact error positions, source file information, and visual error indicators.
6+
7+
## Error Details
8+
9+
When a parsing error occurs, the error object now includes a `sqlDetails` property with the following information:
10+
11+
```typescript
12+
interface SqlErrorDetails {
13+
message: string; // Full error message
14+
cursorPosition: number; // 0-based position in the query
15+
fileName?: string; // Source file (e.g., 'scan.l', 'gram.y')
16+
functionName?: string; // Internal function name
17+
lineNumber?: number; // Line number in source file
18+
context?: string; // Additional context
19+
}
20+
```
21+
22+
## Basic Usage
23+
24+
```javascript
25+
const { parseSync, loadModule } = require('@libpg-query/v17');
26+
27+
await loadModule();
28+
29+
try {
30+
const result = parseSync("SELECT * FROM users WHERE id = 'unclosed");
31+
} catch (error) {
32+
if (error.sqlDetails) {
33+
console.log('Error:', error.message);
34+
console.log('Position:', error.sqlDetails.cursorPosition);
35+
console.log('Source:', error.sqlDetails.fileName);
36+
}
37+
}
38+
```
39+
40+
## Error Formatting Helper
41+
42+
The library includes a built-in `formatSqlError()` function for consistent error formatting:
43+
44+
```javascript
45+
const { parseSync, loadModule, formatSqlError } = require('@libpg-query/v17');
46+
47+
await loadModule();
48+
49+
const query = "SELECT * FROM users WHERE id = 'unclosed";
50+
51+
try {
52+
parseSync(query);
53+
} catch (error) {
54+
console.log(formatSqlError(error, query));
55+
}
56+
```
57+
58+
Output:
59+
```
60+
Error: unterminated quoted string at or near "'unclosed"
61+
Position: 31
62+
Source: file: scan.l, function: scanner_yyerror, line: 1262
63+
SELECT * FROM users WHERE id = 'unclosed
64+
^
65+
```
66+
67+
## Formatting Options
68+
69+
The `formatSqlError()` function accepts options to customize the output:
70+
71+
```typescript
72+
interface SqlErrorFormatOptions {
73+
showPosition?: boolean; // Show the error position marker (default: true)
74+
showQuery?: boolean; // Show the query text (default: true)
75+
color?: boolean; // Use ANSI colors (default: false)
76+
maxQueryLength?: number; // Max query length to display (default: no limit)
77+
}
78+
```
79+
80+
### Examples
81+
82+
#### With Colors (for terminal output)
83+
```javascript
84+
console.log(formatSqlError(error, query, { color: true }));
85+
```
86+
87+
#### Without Position Marker
88+
```javascript
89+
console.log(formatSqlError(error, query, { showPosition: false }));
90+
```
91+
92+
#### With Query Truncation (for long queries)
93+
```javascript
94+
console.log(formatSqlError(error, longQuery, { maxQueryLength: 80 }));
95+
```
96+
97+
## Type Guard
98+
99+
Use the `hasSqlDetails()` function to check if an error has SQL details:
100+
101+
```javascript
102+
const { hasSqlDetails } = require('@libpg-query/v17');
103+
104+
try {
105+
parseSync(query);
106+
} catch (error) {
107+
if (hasSqlDetails(error)) {
108+
// TypeScript knows error has sqlDetails property
109+
console.log('Error at position:', error.sqlDetails.cursorPosition);
110+
}
111+
}
112+
```
113+
114+
## Error Types
115+
116+
Errors are classified by their source file:
117+
- **Lexer errors** (`scan.l`): Token recognition errors (invalid characters, unterminated strings)
118+
- **Parser errors** (`gram.y`): Grammar violations (syntax errors, missing keywords)
119+
120+
## Examples of Common Errors
121+
122+
### Unterminated String
123+
```sql
124+
SELECT * FROM users WHERE name = 'unclosed
125+
```
126+
Error: `unterminated quoted string at or near "'unclosed"`
127+
128+
### Invalid Character
129+
```sql
130+
SELECT * FROM users WHERE id = @
131+
```
132+
Error: `syntax error at end of input`
133+
134+
### Reserved Keyword
135+
```sql
136+
SELECT * FROM table
137+
```
138+
Error: `syntax error at or near "table"` (use quotes: `"table"`)
139+
140+
### Missing Keyword
141+
```sql
142+
SELECT * WHERE id = 1
143+
```
144+
Error: `syntax error at or near "WHERE"`
145+
146+
## Backward Compatibility
147+
148+
The enhanced error handling is fully backward compatible:
149+
- Existing code that catches errors will continue to work
150+
- The `sqlDetails` property is added without modifying the base Error object
151+
- All existing error properties and methods remain unchanged
152+
153+
## Migration Guide
154+
155+
To take advantage of the new error handling:
156+
157+
1. **Check for sqlDetails**:
158+
```javascript
159+
if (error.sqlDetails) {
160+
// Use enhanced error information
161+
}
162+
```
163+
164+
2. **Use the formatting helper**:
165+
```javascript
166+
console.log(formatSqlError(error, query));
167+
```
168+
169+
3. **Type-safe access** (TypeScript):
170+
```typescript
171+
if (hasSqlDetails(error)) {
172+
// error.sqlDetails is now typed
173+
}
174+
```
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const { parseSync, loadModule, formatSqlError } = require('./wasm/index.cjs');
2+
3+
async function main() {
4+
await loadModule();
5+
6+
const query = "SELECT * FROM users WHERE id = 'unclosed";
7+
8+
try {
9+
parseSync(query);
10+
} catch (error) {
11+
// Simple format matching your example
12+
console.log(`Query: ${query}`);
13+
console.log(formatSqlError(error, query));
14+
}
15+
}
16+
17+
main().catch(console.error);

0 commit comments

Comments
 (0)