sql-typechecker 0.0.1 → 0.0.5
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/out/cli.js +221844 -0
- package/out/cli.js.map +7 -0
- package/package.json +3 -3
- package/school/out.ts +7 -7
- package/school/sql.sql +1 -1
- package/src/cli.ts +60 -19
- package/src/typecheck.ts +17 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sql-typechecker",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"main": "out/cli.js",
|
|
5
5
|
"bin": "out/cli.js",
|
|
6
6
|
"dependencies": {
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"build": "node esbuild.js",
|
|
13
13
|
"install": "npm run build",
|
|
14
14
|
"test": "alsatian ./test/**/*.ts",
|
|
15
|
-
"start": "npm run build && node --enable-source-maps ./out/cli.js ./sample -o ./sample/out.ts",
|
|
16
|
-
"start_school": "npm run build && node --enable-source-maps ./out/cli.js ./school -o ./school/out.ts",
|
|
15
|
+
"start": "npm run build && node --enable-source-maps ./out/cli.js -d ./sample -o ./sample/out.ts",
|
|
16
|
+
"start_school": "npm run build && node --enable-source-maps ./out/cli.js -d ./school -o ./school/out.ts",
|
|
17
17
|
"debug": "npm run build && node --inspect-brk --enable-source-maps ./out/cli.js ",
|
|
18
18
|
"test_debug": "node --inspect-brk ./node_modules/alsatian/dist/cli/alsatian-cli.js ./test/**/*.ts -t 20000000"
|
|
19
19
|
},
|
package/school/out.ts
CHANGED
|
@@ -4,7 +4,7 @@ export function getemailstosend(
|
|
|
4
4
|
args: {}
|
|
5
5
|
): Promise<
|
|
6
6
|
{
|
|
7
|
-
uw_id:
|
|
7
|
+
uw_id: number;
|
|
8
8
|
uw_from: string;
|
|
9
9
|
uw_replyto: string | null;
|
|
10
10
|
uw_address: string;
|
|
@@ -17,8 +17,8 @@ export function getemailstosend(
|
|
|
17
17
|
uw_content: Buffer | null;
|
|
18
18
|
}[]
|
|
19
19
|
> {
|
|
20
|
-
return pg`select getemailstosend()`;
|
|
21
|
-
/*
|
|
20
|
+
return pg`select getemailstosend() AS getemailstosend(uw_id bigint, uw_from text, uw_replyto text, uw_address text, uw_addressee text, uw_subject text, uw_text text, uw_html text, uw_externalid text, uw_filename text, uw_content bytea)`;
|
|
21
|
+
/* -- ORIGINAL --
|
|
22
22
|
CREATE FUNCTION getemailstosend() RETURNS SETOF __todo__ AS
|
|
23
23
|
$$
|
|
24
24
|
SELECT
|
|
@@ -44,10 +44,10 @@ $$ LANGUAGE sql;
|
|
|
44
44
|
export function insertnewemailstatus(
|
|
45
45
|
pg: postgres.Sql<any>,
|
|
46
46
|
args: { uw_emailid: number; version: number; uw_status: string }
|
|
47
|
-
): Promise<{ uw_emailid:
|
|
48
|
-
return pg`select insertnewemailstatus(${args.uw_emailid}, ${args.version}, ${args.uw_status})
|
|
49
|
-
/*
|
|
50
|
-
CREATE FUNCTION insertnewemailstatus(uw_emailidinteger, versioninteger, uw_statustext) RETURNS
|
|
47
|
+
): Promise<{ uw_emailid: number } | undefined> {
|
|
48
|
+
return pg`select insertnewemailstatus(${args.uw_emailid}, ${args.version}, ${args.uw_status}) AS insertnewemailstatus(uw_emailid bigint)`[0];
|
|
49
|
+
/* -- ORIGINAL --
|
|
50
|
+
CREATE FUNCTION insertnewemailstatus(uw_emailidinteger, versioninteger, uw_statustext) RETURNS __todo__ AS
|
|
51
51
|
$$
|
|
52
52
|
INSERT INTO uw_email_statusses
|
|
53
53
|
(uw_emailid, uw_version, uw_status, uw_stamp, uw_islastversion)
|
package/school/sql.sql
CHANGED
|
@@ -956,7 +956,7 @@ CREATE TABLE uw_Payenrollment_onlinepayments(
|
|
|
956
956
|
CONSTRAINT uw_Payenrollment_onlinepayments_EnrollmentOrRequest
|
|
957
957
|
CHECK (((uw_enrollmentId IS NULL) AND (NOT (uw_enrollmentrequestId IS NULL))) OR ((uw_enrollmentrequestId IS NULL) AND (NOT (uw_enrollmentId IS NULL)))));
|
|
958
958
|
|
|
959
|
-
CREATE OR REPLACE FUNCTION getEmailsToSend() RETURNS SETOF AS $$
|
|
959
|
+
CREATE OR REPLACE FUNCTION getEmailsToSend() RETURNS SETOF RECORD AS $$
|
|
960
960
|
SELECT
|
|
961
961
|
e.uw_id,
|
|
962
962
|
e.uw_from,
|
package/src/cli.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
import * as fs from "fs/promises";
|
|
2
3
|
import * as path from "path";
|
|
3
4
|
import { CreateFunctionStatement, parse, Statement } from "pgsql-ast-parser";
|
|
@@ -9,12 +10,13 @@ import {
|
|
|
9
10
|
showSqlType,
|
|
10
11
|
showType,
|
|
11
12
|
Type,
|
|
13
|
+
SimpleT,
|
|
12
14
|
} from "./typecheck";
|
|
13
15
|
import * as prettier from "prettier";
|
|
14
16
|
|
|
15
17
|
go();
|
|
16
18
|
|
|
17
|
-
async function
|
|
19
|
+
async function findSqlFilesInDir(dir: string): Promise<string[]> {
|
|
18
20
|
const inThisDir = await fs.readdir(dir);
|
|
19
21
|
const res: string[] = [];
|
|
20
22
|
for (let p of inThisDir) {
|
|
@@ -24,7 +26,7 @@ async function findSqlFiles(dir: string): Promise<string[]> {
|
|
|
24
26
|
} else {
|
|
25
27
|
const stat = await fs.stat(fullP);
|
|
26
28
|
if (stat.isDirectory()) {
|
|
27
|
-
const inSubFolder = await
|
|
29
|
+
const inSubFolder = await findSqlFilesInDir(fullP);
|
|
28
30
|
res.push(...inSubFolder);
|
|
29
31
|
} else {
|
|
30
32
|
// not a sql file, not a directory
|
|
@@ -88,7 +90,8 @@ function functionToTypescript(f: functionType): string {
|
|
|
88
90
|
const returnTypeAsString =
|
|
89
91
|
f.returns.kind === "void"
|
|
90
92
|
? "void"
|
|
91
|
-
: showTypeAsTypescriptType(f.returns) +
|
|
93
|
+
: showTypeAsTypescriptType(f.returns) +
|
|
94
|
+
(f.multipleRows ? "[]" : " | undefined");
|
|
92
95
|
|
|
93
96
|
const argsType =
|
|
94
97
|
"{" +
|
|
@@ -110,12 +113,37 @@ function functionToTypescript(f: functionType): string {
|
|
|
110
113
|
.map((k) => k.name.name + showSqlType(k.type))
|
|
111
114
|
.join(", ");
|
|
112
115
|
|
|
116
|
+
function showTypeDroppingNullable(t: SimpleT): string {
|
|
117
|
+
if (t.kind === "nullable") {
|
|
118
|
+
return showTypeDroppingNullable(t.typevar);
|
|
119
|
+
} else if (t.kind === "array") {
|
|
120
|
+
return showTypeDroppingNullable(t.typevar) + "[]";
|
|
121
|
+
} else if (t.kind === "anyscalar") {
|
|
122
|
+
return "anyscalar";
|
|
123
|
+
} else if (t.kind === "scalar") {
|
|
124
|
+
return t.name.name;
|
|
125
|
+
} else {
|
|
126
|
+
return "";
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const asExpression =
|
|
131
|
+
f.returns.kind === "set"
|
|
132
|
+
? ` AS ${f.name.name}(${f.returns.fields
|
|
133
|
+
.map(
|
|
134
|
+
(f) => (f.name?.name || "") + " " + showTypeDroppingNullable(f.type)
|
|
135
|
+
)
|
|
136
|
+
.join(", ")})`
|
|
137
|
+
: "";
|
|
138
|
+
|
|
113
139
|
return `
|
|
114
140
|
export function ${
|
|
115
141
|
f.name.name
|
|
116
142
|
}(pg: postgres.Sql<any>, args: ${argsType}): Promise<${returnTypeAsString}>{
|
|
117
|
-
return pg\`select ${f.name.name}(${argsAsList})
|
|
118
|
-
|
|
143
|
+
return pg\`select ${f.name.name}(${argsAsList})${asExpression}\`${
|
|
144
|
+
f.multipleRows ? "" : "[0]"
|
|
145
|
+
};
|
|
146
|
+
/* -- ORIGINAL --
|
|
119
147
|
CREATE FUNCTION ${f.name.name}(${argsForCreateFunction}) RETURNS ${
|
|
120
148
|
f.multipleRows ? "SETOF " : ""
|
|
121
149
|
}__todo__ AS
|
|
@@ -126,18 +154,28 @@ $$${f.code}$$ LANGUAGE ${f.language};
|
|
|
126
154
|
}
|
|
127
155
|
|
|
128
156
|
async function go() {
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
throw new Error("Please provide directory with SQL files");
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const outArg = findOutArg(process.argv);
|
|
157
|
+
const outArgs = findInArgs({ argv: process.argv, flags: ["-o", "--out"] });
|
|
158
|
+
const outArg = outArgs[0];
|
|
135
159
|
if (!outArg) {
|
|
136
160
|
throw new Error("Please provide -o/--out parameter");
|
|
137
161
|
}
|
|
138
|
-
const allSqlFiles = await findSqlFiles(path.resolve(process.cwd(), dir));
|
|
139
162
|
|
|
140
|
-
|
|
163
|
+
const dirs = findInArgs({ argv: process.argv, flags: ["-d", "--dir"] });
|
|
164
|
+
const files = findInArgs({ argv: process.argv, flags: ["-f", "--file"] });
|
|
165
|
+
|
|
166
|
+
const allSqlFiles = (
|
|
167
|
+
await Promise.all(
|
|
168
|
+
dirs.map((dir) => findSqlFilesInDir(path.resolve(process.cwd(), dir)))
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
.flat()
|
|
172
|
+
.concat(files);
|
|
173
|
+
|
|
174
|
+
if (allSqlFiles.length === 0) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
"Please provide at least one SQL file with flags -f/--file or -d/--dir"
|
|
177
|
+
);
|
|
178
|
+
}
|
|
141
179
|
|
|
142
180
|
const allStatements: Statement[] = [];
|
|
143
181
|
for (let sqlFile of allSqlFiles) {
|
|
@@ -177,11 +215,14 @@ async function prepOutFile(path: string): Promise<string> {
|
|
|
177
215
|
return path;
|
|
178
216
|
}
|
|
179
217
|
|
|
180
|
-
function
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
218
|
+
function findInArgs(opts: { argv: string[]; flags: string[] }): string[] {
|
|
219
|
+
let i = 0;
|
|
220
|
+
let res = [];
|
|
221
|
+
for (let arg of opts.argv) {
|
|
222
|
+
if (opts.flags.includes(arg) && opts.argv[i + 1]) {
|
|
223
|
+
res.push(opts.argv[i + 1]);
|
|
224
|
+
}
|
|
225
|
+
i = i + 1;
|
|
186
226
|
}
|
|
227
|
+
return res;
|
|
187
228
|
}
|
package/src/typecheck.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
SelectStatement,
|
|
25
25
|
Statement,
|
|
26
26
|
toSql,
|
|
27
|
+
UpdateStatement,
|
|
27
28
|
} from "pgsql-ast-parser";
|
|
28
29
|
import { builtincasts } from "./builtincasts";
|
|
29
30
|
import { builtinoperators } from "./builtinoperators";
|
|
@@ -740,21 +741,26 @@ function elabInsert(g: Global, c: Context, s: InsertStatement): VoidT | SetT {
|
|
|
740
741
|
// TODO: typecheck s.onConflict
|
|
741
742
|
}
|
|
742
743
|
|
|
743
|
-
function
|
|
744
|
-
|
|
744
|
+
function elabDeleteOrUpdate(
|
|
745
|
+
g: Global,
|
|
746
|
+
c: Context,
|
|
747
|
+
s: DeleteStatement | UpdateStatement
|
|
748
|
+
): VoidT | SetT {
|
|
749
|
+
const tableName = s.type === "delete" ? s.from : s.table;
|
|
750
|
+
const tableDef: null | {
|
|
745
751
|
readonly name: QName;
|
|
746
752
|
readonly rel: SetT;
|
|
747
|
-
} = g.tables.find((t) => eqQNames(t.name,
|
|
753
|
+
} = g.tables.find((t) => eqQNames(t.name, tableName)) || null;
|
|
748
754
|
|
|
749
|
-
if (!
|
|
750
|
-
throw new UnknownIdentifier(s,
|
|
755
|
+
if (!tableDef) {
|
|
756
|
+
throw new UnknownIdentifier(s, tableName);
|
|
751
757
|
}
|
|
752
|
-
const nameToAddInContext =
|
|
758
|
+
const nameToAddInContext = tableName.alias || tableName.name;
|
|
753
759
|
const newContext = {
|
|
754
760
|
...c,
|
|
755
761
|
froms: c.froms.concat({
|
|
756
762
|
name: { name: nameToAddInContext },
|
|
757
|
-
type:
|
|
763
|
+
type: tableDef.rel,
|
|
758
764
|
}),
|
|
759
765
|
};
|
|
760
766
|
|
|
@@ -850,6 +856,7 @@ export function doCreateFunction(
|
|
|
850
856
|
const body = parse(s.code);
|
|
851
857
|
|
|
852
858
|
if (body.length === 0) {
|
|
859
|
+
// empty function body
|
|
853
860
|
return {
|
|
854
861
|
name,
|
|
855
862
|
inputs,
|
|
@@ -867,7 +874,7 @@ export function doCreateFunction(
|
|
|
867
874
|
name,
|
|
868
875
|
inputs,
|
|
869
876
|
returns: returnType,
|
|
870
|
-
multipleRows:
|
|
877
|
+
multipleRows: (s.returns && s.returns.setof) || false,
|
|
871
878
|
code: s.code,
|
|
872
879
|
language: s.language.name,
|
|
873
880
|
};
|
|
@@ -1770,8 +1777,8 @@ function elabStatement(g: Global, c: Context, s: Statement): VoidT | Type {
|
|
|
1770
1777
|
return elabExpr(g, c, s);
|
|
1771
1778
|
} else if (s.type === "insert") {
|
|
1772
1779
|
return elabInsert(g, c, s);
|
|
1773
|
-
} else if (s.type === "delete") {
|
|
1774
|
-
return
|
|
1780
|
+
} else if (s.type === "delete" || s.type === "update") {
|
|
1781
|
+
return elabDeleteOrUpdate(g, c, s);
|
|
1775
1782
|
} else {
|
|
1776
1783
|
return notImplementedYet(s);
|
|
1777
1784
|
}
|