happy-sql 1.0.0 → 1.0.1
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.
- package/ChangeLog +4 -0
- package/README.md +2 -0
- package/lib/parser/parser.js +35 -1
- package/lib/parser/visitors/clause/delete/delete.js +3 -2
- package/lib/parser/visitors/clause/find-clause.js +3 -0
- package/lib/parser/visitors/clause/insert/fixture/insert-identifier-fix.js +3 -0
- package/lib/parser/visitors/clause/insert/fixture/insert-identifier.sql +1 -0
- package/lib/parser/visitors/clause/insert/insert.js +9 -30
- package/lib/parser/visitors/clause/operand.js +20 -0
- package/lib/parser/visitors/clause/select/fixture/select-alias-param-fix.js +3 -0
- package/lib/parser/visitors/clause/select/fixture/select-alias-param.sql +1 -0
- package/lib/parser/visitors/clause/select/select.js +2 -15
- package/lib/parser/visitors/clause/set.js +18 -0
- package/lib/parser/visitors/clause/update/fixture/update-identifier-fix.js +3 -0
- package/lib/parser/visitors/clause/update/fixture/update-identifier.sql +1 -0
- package/lib/parser/visitors/clause/update/fixture/update-number-fix.js +3 -0
- package/lib/parser/visitors/clause/update/fixture/update-number.sql +1 -0
- package/lib/parser/visitors/clause/update/update.js +5 -17
- package/lib/printer/visitors/call-expression/clauses/clauses.js +2 -0
- package/lib/printer/visitors/call-expression/clauses/on-conflict/fixture/on-conflict-multi-fix.sql +1 -0
- package/lib/printer/visitors/call-expression/clauses/section/fixture/section-fix.sql +3 -0
- package/lib/printer/visitors/call-expression/clauses/section/section.js +5 -0
- package/package.json +8 -4
package/ChangeLog
ADDED
package/README.md
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
[LicenseIMGURL]: https://img.shields.io/badge/license-MIT-317BF9.svg?style=flat
|
|
8
8
|
[LicenseURL]: https://tldrlegal.com/license/mit-license "MIT License"
|
|
9
9
|
|
|
10
|
+
<img width="600" height="428" alt="image" src="https://github.com/user-attachments/assets/aefbd055-957e-4de3-aca5-c30e5e3ee3f9" />
|
|
11
|
+
|
|
10
12
|
SQL to JS AST parser and printer.
|
|
11
13
|
|
|
12
14
|
## Install
|
package/lib/parser/parser.js
CHANGED
|
@@ -3,20 +3,54 @@ import {types} from '@putout/babel';
|
|
|
3
3
|
import {convertClause} from '#parser/clause';
|
|
4
4
|
|
|
5
5
|
const {
|
|
6
|
+
identifier,
|
|
7
|
+
stringLiteral,
|
|
8
|
+
callExpression,
|
|
6
9
|
arrayExpression,
|
|
7
10
|
expressionStatement,
|
|
8
11
|
file,
|
|
9
12
|
program,
|
|
10
13
|
} = types;
|
|
11
14
|
|
|
15
|
+
const getSectionName = (leading) => {
|
|
16
|
+
const comment = leading?.find((c) => c.text.startsWith('-- @'));
|
|
17
|
+
|
|
18
|
+
return comment ? comment.text
|
|
19
|
+
.slice(2)
|
|
20
|
+
.trim() : null;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const wrapSection = (stmtNode, sectionName) => {
|
|
24
|
+
if (!sectionName)
|
|
25
|
+
return stmtNode;
|
|
26
|
+
|
|
27
|
+
return callExpression(identifier('section'), [
|
|
28
|
+
stringLiteral(sectionName),
|
|
29
|
+
stmtNode,
|
|
30
|
+
]);
|
|
31
|
+
};
|
|
32
|
+
|
|
12
33
|
export const parseSqlNode = (source) => {
|
|
13
34
|
const cst = parse(source, {
|
|
14
35
|
dialect: 'sqlite',
|
|
15
36
|
paramTypes: [':name'],
|
|
16
37
|
includeRange: true,
|
|
38
|
+
includeComments: true,
|
|
17
39
|
});
|
|
18
40
|
|
|
19
|
-
const elements =
|
|
41
|
+
const elements = [];
|
|
42
|
+
|
|
43
|
+
for (let i = 0; i < cst.statements.length; i++) {
|
|
44
|
+
const stmt = cst.statements[i];
|
|
45
|
+
|
|
46
|
+
if (stmt.type === 'empty')
|
|
47
|
+
continue;
|
|
48
|
+
|
|
49
|
+
const stmtNode = convertClause(stmt);
|
|
50
|
+
const leading = !i ? cst.leading : stmt.leading;
|
|
51
|
+
|
|
52
|
+
elements.push(wrapSection(stmtNode, getSectionName(leading)));
|
|
53
|
+
}
|
|
20
54
|
|
|
21
55
|
return file(program([
|
|
22
56
|
expressionStatement(arrayExpression(elements)),
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {types} from '@putout/babel';
|
|
2
2
|
import {convertWhere} from '#parser/clause/where';
|
|
3
|
+
import {findClause} from '#parser/clause/find';
|
|
3
4
|
|
|
4
5
|
const {identifier, callExpression} = types;
|
|
5
6
|
|
|
6
7
|
export const convertDelete = ({clauses}) => {
|
|
7
|
-
const deleteClause = clauses
|
|
8
|
-
const whereClause = clauses
|
|
8
|
+
const deleteClause = findClause(clauses, 'delete_clause');
|
|
9
|
+
const whereClause = findClause(clauses, 'where_clause');
|
|
9
10
|
|
|
10
11
|
const table = identifier(deleteClause.tables.items[0].name);
|
|
11
12
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
INSERT INTO t (x) VALUES (:x) ON CONFLICT (x) DO UPDATE SET y = x
|
|
@@ -1,41 +1,22 @@
|
|
|
1
1
|
import {types} from '@putout/babel';
|
|
2
|
+
import {convertSet} from '#parser/clause/set';
|
|
3
|
+
import {findClause} from '#parser/clause/find';
|
|
2
4
|
|
|
3
5
|
const {
|
|
4
6
|
identifier,
|
|
5
|
-
stringLiteral,
|
|
6
|
-
binaryExpression,
|
|
7
|
-
arrayExpression,
|
|
8
7
|
callExpression,
|
|
8
|
+
arrayExpression,
|
|
9
|
+
stringLiteral,
|
|
9
10
|
} = types;
|
|
10
11
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
return stringLiteral(node.text);
|
|
14
|
-
|
|
15
|
-
if (node.type === 'number_literal')
|
|
16
|
-
return types.numericLiteral(node.value);
|
|
17
|
-
|
|
18
|
-
if (node.type === 'string_literal')
|
|
19
|
-
return stringLiteral(node.value);
|
|
20
|
-
|
|
21
|
-
return identifier(node.name);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const convertSet = (setClause) => {
|
|
25
|
-
const args = [];
|
|
26
|
-
|
|
27
|
-
for (const a of setClause.assignments.items) {
|
|
28
|
-
args.push(binaryExpression('===', identifier(a.column.name), convertOperand(a.expr)));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return callExpression(identifier('set'), args);
|
|
32
|
-
};
|
|
12
|
+
const convertExprToIdentifier = ({expr}) => identifier(expr.name);
|
|
13
|
+
const convertColToIdentifier = ({name}) => identifier(name);
|
|
33
14
|
|
|
34
15
|
const convertUpsert = (upsertClause) => {
|
|
35
16
|
const target = upsertClause.conflictTarget;
|
|
36
17
|
const {action} = upsertClause;
|
|
37
18
|
|
|
38
|
-
const targetArgs = target ? target.expr.items.map(
|
|
19
|
+
const targetArgs = target ? target.expr.items.map(convertExprToIdentifier) : [];
|
|
39
20
|
const actionNode = action.type === 'upsert_action_nothing' ? identifier('nothing') : convertSet(action.set);
|
|
40
21
|
|
|
41
22
|
return callExpression(identifier('onConflict'), [...targetArgs, actionNode]);
|
|
@@ -44,13 +25,11 @@ const convertUpsert = (upsertClause) => {
|
|
|
44
25
|
export const convertInsert = ({clauses}) => {
|
|
45
26
|
const [ic] = clauses;
|
|
46
27
|
const [, vc] = clauses;
|
|
47
|
-
const uc = clauses
|
|
28
|
+
const uc = findClause(clauses, 'upsert_clause');
|
|
48
29
|
|
|
49
30
|
const cols = ic.columns.expr.items;
|
|
50
31
|
const vals = vc.values.items[0].expr.items;
|
|
51
|
-
|
|
52
|
-
const colArg = cols.length === 1 ? identifier(cols[0].name) : arrayExpression(cols.map((c) => identifier(c.name)));
|
|
53
|
-
|
|
32
|
+
const colArg = cols.length === 1 ? identifier(cols[0].name) : arrayExpression(cols.map(convertColToIdentifier));
|
|
54
33
|
const valArgs = [];
|
|
55
34
|
|
|
56
35
|
for (const p of vals) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {types} from '@putout/babel';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
identifier,
|
|
5
|
+
stringLiteral,
|
|
6
|
+
numericLiteral,
|
|
7
|
+
} = types;
|
|
8
|
+
|
|
9
|
+
export const convertOperand = (node) => {
|
|
10
|
+
if (node.type === 'parameter')
|
|
11
|
+
return stringLiteral(node.text);
|
|
12
|
+
|
|
13
|
+
if (node.type === 'number_literal')
|
|
14
|
+
return numericLiteral(node.value);
|
|
15
|
+
|
|
16
|
+
if (node.type === 'string_literal')
|
|
17
|
+
return stringLiteral(node.value);
|
|
18
|
+
|
|
19
|
+
return identifier(node.name);
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SELECT :file AS file FROM t
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {types} from '@putout/babel';
|
|
2
|
+
import {convertOperand} from '#parser/clause/operand';
|
|
2
3
|
import {convertFrom} from '#parser/clause/from';
|
|
3
4
|
import {convertWhere} from '#parser/clause/where';
|
|
5
|
+
import {findClause} from '#parser/clause/find';
|
|
4
6
|
|
|
5
7
|
const {
|
|
6
8
|
identifier,
|
|
@@ -9,21 +11,6 @@ const {
|
|
|
9
11
|
callExpression,
|
|
10
12
|
} = types;
|
|
11
13
|
|
|
12
|
-
const findClause = (clauses, type) => clauses.find((c) => c.type === type);
|
|
13
|
-
|
|
14
|
-
const convertOperand = (node) => {
|
|
15
|
-
if (node.type === 'parameter')
|
|
16
|
-
return stringLiteral(node.text);
|
|
17
|
-
|
|
18
|
-
if (node.type === 'number_literal')
|
|
19
|
-
return types.numericLiteral(node.value);
|
|
20
|
-
|
|
21
|
-
if (node.type === 'string_literal')
|
|
22
|
-
return stringLiteral(node.value);
|
|
23
|
-
|
|
24
|
-
return identifier(node.name);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
14
|
const convertColumn = (col) => {
|
|
28
15
|
if (col.type === 'alias')
|
|
29
16
|
return assignmentExpression('=', identifier(col.alias.name), convertOperand(col.expr));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {types} from '@putout/babel';
|
|
2
|
+
import {convertOperand} from '#parser/clause/operand';
|
|
3
|
+
|
|
4
|
+
const {
|
|
5
|
+
callExpression,
|
|
6
|
+
identifier,
|
|
7
|
+
binaryExpression,
|
|
8
|
+
} = types;
|
|
9
|
+
|
|
10
|
+
export const convertSet = (setClause) => {
|
|
11
|
+
const args = [];
|
|
12
|
+
|
|
13
|
+
for (const a of setClause.assignments.items) {
|
|
14
|
+
args.push(binaryExpression('===', identifier(a.column.name), convertOperand(a.expr)));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return callExpression(identifier('set'), args);
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
UPDATE t SET x = y WHERE id = :id
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
UPDATE t SET x = 1 WHERE id = :id
|
|
@@ -1,30 +1,18 @@
|
|
|
1
1
|
import {types} from '@putout/babel';
|
|
2
2
|
import {convertWhere} from '#parser/clause/where';
|
|
3
|
+
import {convertOperand} from '#parser/clause/operand';
|
|
4
|
+
import {findClause} from '#parser/clause/find';
|
|
3
5
|
|
|
4
6
|
const {
|
|
5
7
|
identifier,
|
|
6
|
-
stringLiteral,
|
|
7
8
|
binaryExpression,
|
|
8
9
|
callExpression,
|
|
9
10
|
} = types;
|
|
10
11
|
|
|
11
|
-
const convertOperand = (node) => {
|
|
12
|
-
if (node.type === 'parameter')
|
|
13
|
-
return stringLiteral(node.text);
|
|
14
|
-
|
|
15
|
-
if (node.type === 'number_literal')
|
|
16
|
-
return types.numericLiteral(node.value);
|
|
17
|
-
|
|
18
|
-
if (node.type === 'string_literal')
|
|
19
|
-
return stringLiteral(node.value);
|
|
20
|
-
|
|
21
|
-
return identifier(node.name);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
12
|
export const convertUpdate = ({clauses}) => {
|
|
25
|
-
const updateClause = clauses
|
|
26
|
-
const setClause = clauses
|
|
27
|
-
const whereClause = clauses
|
|
13
|
+
const updateClause = findClause(clauses, 'update_clause');
|
|
14
|
+
const setClause = findClause(clauses, 'set_clause');
|
|
15
|
+
const whereClause = findClause(clauses, 'where_clause');
|
|
28
16
|
|
|
29
17
|
const table = identifier(updateClause.tables.items[0].name);
|
|
30
18
|
const setArgs = [];
|
|
@@ -8,6 +8,7 @@ import {onConflict} from './on-conflict/on-conflict.js';
|
|
|
8
8
|
import {update} from './update/update.js';
|
|
9
9
|
import {set} from './set/set.js';
|
|
10
10
|
import {del} from './delete/delete.js';
|
|
11
|
+
import {section} from './section/section.js';
|
|
11
12
|
|
|
12
13
|
export const clauses = {
|
|
13
14
|
select,
|
|
@@ -20,4 +21,5 @@ export const clauses = {
|
|
|
20
21
|
update,
|
|
21
22
|
set,
|
|
22
23
|
delete: del,
|
|
24
|
+
section,
|
|
23
25
|
};
|
package/lib/printer/visitors/call-expression/clauses/on-conflict/fixture/on-conflict-multi-fix.sql
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
INSERT INTO t (x) VALUES (:x) ON CONFLICT (x, y) DO UPDATE SET x = :x
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "happy-sql",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "sql to js ast parser and printer",
|
|
6
6
|
"exports": {
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
"imports": {
|
|
13
13
|
"#happy-sql": "./lib/happy-sql.js",
|
|
14
14
|
"#parser": "./lib/parser/parser.js",
|
|
15
|
+
"#parser/clause/operand": "./lib/parser/visitors/clause/operand.js",
|
|
16
|
+
"#parser/clause/set": "./lib/parser/visitors/clause/set.js",
|
|
17
|
+
"#parser/clause/find": "./lib/parser/visitors/clause/find-clause.js",
|
|
15
18
|
"#parser/test": "./test/create-parser-test.js",
|
|
16
19
|
"#parser/clause": "./lib/parser/visitors/clause/index.js",
|
|
17
20
|
"#parser/clause/select": "./lib/parser/visitors/clause/select/select.js",
|
|
@@ -42,9 +45,10 @@
|
|
|
42
45
|
"url": "git+https://github.com/coderaiser/happy-sql.git"
|
|
43
46
|
},
|
|
44
47
|
"keywords": [
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
+
"sql",
|
|
49
|
+
"js",
|
|
50
|
+
"print",
|
|
51
|
+
"parse"
|
|
48
52
|
],
|
|
49
53
|
"author": "coderaiser <mnemonic.enemy@gmail.com> (http://coderaiser.github.io/)",
|
|
50
54
|
"license": "MIT",
|