@deterministic-code/datasources-sqlserver 0.1.18 → 0.1.29
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/dist/DefaultValueConverter.d.ts +25 -0
- package/dist/DefaultValueConverter.js +172 -0
- package/dist/DefaultValueOptions.d.ts +11 -0
- package/dist/DefaultValueOptions.js +1 -0
- package/dist/SqlServerAlterTableStatementRenderer.d.ts +4 -1
- package/dist/SqlServerAlterTableStatementRenderer.js +14 -0
- package/dist/SqlServerCreateTableStatementRenderer.d.ts +3 -0
- package/dist/SqlServerCreateTableStatementRenderer.js +14 -0
- package/dist/SqlServerCreateTriggerStatementRenderer.js +7 -3
- package/dist/SqlServerDriver.d.ts +15 -2
- package/dist/SqlServerDriver.js +49 -35
- package/dist/SqlServerDropIndexStatementRenderer.js +2 -1
- package/dist/SqlServerQuotedRenderers.d.ts +13 -0
- package/dist/SqlServerQuotedRenderers.js +28 -0
- package/dist/SqlServerSqlSeedStatementRenderer.d.ts +4 -0
- package/dist/SqlServerSqlSeedStatementRenderer.js +52 -0
- package/dist/SqlServerStatementRenderer.d.ts +5 -1
- package/dist/SqlServerStatementRenderer.js +14 -0
- package/dist/SqlServerStoredProcedureStatementRenderer.js +8 -6
- package/dist/TypeMap.d.ts +9 -0
- package/dist/TypeMap.js +35 -0
- package/dist/applyKeywords.d.ts +1 -0
- package/dist/applyKeywords.js +14 -0
- package/dist/procedureSpecs.d.ts +10 -0
- package/dist/procedureSpecs.js +17 -0
- package/package.json +2 -2
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DefaultValueOptions } from "./DefaultValueOptions.js";
|
|
2
|
+
export declare const EMPTY_UUID = "00000000-0000-0000-0000-000000000000";
|
|
3
|
+
export type SpecToken = {
|
|
4
|
+
spec: RegExp;
|
|
5
|
+
specText?: (arg: string) => string;
|
|
6
|
+
};
|
|
7
|
+
export type DefaultEntry = SpecToken & {
|
|
8
|
+
native: (arg: string) => string;
|
|
9
|
+
reverse: RegExp;
|
|
10
|
+
};
|
|
11
|
+
type DefaultToken = {
|
|
12
|
+
token: string;
|
|
13
|
+
arg?: string | boolean;
|
|
14
|
+
};
|
|
15
|
+
export declare const sqlStringLiteral: (value: string | number | boolean | null | undefined) => string;
|
|
16
|
+
export declare class DefaultValueConverter {
|
|
17
|
+
static readonly spec: Record<string, Record<string, SpecToken>>;
|
|
18
|
+
readonly dictionary: Record<string, Record<string, DefaultEntry>>;
|
|
19
|
+
constructor(options: DefaultValueOptions);
|
|
20
|
+
static parse(specType: string, value: unknown): DefaultToken;
|
|
21
|
+
convert: (specType: string, text: string) => string | null;
|
|
22
|
+
reverseConvert: (specType: string, native: string) => string;
|
|
23
|
+
private static dictionaryOf;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
export const EMPTY_UUID = "00000000-0000-0000-0000-000000000000";
|
|
2
|
+
const NUMERIC_TYPES = [
|
|
3
|
+
"integer",
|
|
4
|
+
"unsignedinteger",
|
|
5
|
+
"smallinteger",
|
|
6
|
+
"unsignedsmallinteger",
|
|
7
|
+
"biginteger",
|
|
8
|
+
"unsignedbiginteger",
|
|
9
|
+
"float",
|
|
10
|
+
"decimal",
|
|
11
|
+
];
|
|
12
|
+
const NUMERIC_LITERAL_TYPES = new Set([...NUMERIC_TYPES, "reference"]);
|
|
13
|
+
const CALL_FORM = /^([A-Za-z]+)\('(.*)'\)$/;
|
|
14
|
+
const quotedReverse = /^'(.*)'$/;
|
|
15
|
+
const identity = (arg) => arg;
|
|
16
|
+
const intLit = { spec: /^-?[0-9]+$/, specText: identity };
|
|
17
|
+
const uintLit = { spec: /^[0-9]+$/, specText: identity };
|
|
18
|
+
const floatLit = { spec: /^-?[0-9]+(\.[0-9]+)?$/, specText: identity };
|
|
19
|
+
export const sqlStringLiteral = (value) => `'${String(value).replace(/'/g, "''")}'`;
|
|
20
|
+
const escapeRe = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
21
|
+
const exact = (text) => new RegExp(`^${escapeRe(text)}$`);
|
|
22
|
+
const quoted = (arg) => sqlStringLiteral(arg);
|
|
23
|
+
const callTokenName = (name) => (name === "uuid" ? "Uuid" : name);
|
|
24
|
+
const token = (specToken, native, reverse) => ({
|
|
25
|
+
...specToken,
|
|
26
|
+
native,
|
|
27
|
+
reverse,
|
|
28
|
+
});
|
|
29
|
+
export class DefaultValueConverter {
|
|
30
|
+
static spec = {
|
|
31
|
+
string: { String: { spec: /^.*$/, specText: identity } },
|
|
32
|
+
character: { String: { spec: /^.+$/, specText: identity } },
|
|
33
|
+
integer: { Numeric: intLit },
|
|
34
|
+
unsignedinteger: { Numeric: uintLit },
|
|
35
|
+
smallinteger: { Numeric: intLit },
|
|
36
|
+
unsignedsmallinteger: { Numeric: uintLit },
|
|
37
|
+
biginteger: { Numeric: intLit },
|
|
38
|
+
unsignedbiginteger: { Numeric: uintLit },
|
|
39
|
+
float: { Numeric: floatLit },
|
|
40
|
+
decimal: { Numeric: floatLit, String: { spec: /^.*$/, specText: identity } },
|
|
41
|
+
boolean: { Boolean: { spec: /^(true|false)$/, specText: identity } },
|
|
42
|
+
datetime: {
|
|
43
|
+
Now: { spec: /^Now$/ },
|
|
44
|
+
UtcNow: { spec: /^UtcNow$/ },
|
|
45
|
+
DateTime: {
|
|
46
|
+
spec: /^DateTime\('[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-9]{2}:[0-9]{2})'\)$/,
|
|
47
|
+
specText: (arg) => `DateTime('${arg}')`,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
binary: {
|
|
51
|
+
Hex: { spec: /^Hex\('([0-9a-fA-F]{2})*'\)$/, specText: (arg) => `Hex('${arg}')` },
|
|
52
|
+
},
|
|
53
|
+
uuid: {
|
|
54
|
+
NewId: { spec: /^NewId$/ },
|
|
55
|
+
Empty: { spec: /^Empty$/ },
|
|
56
|
+
Uuid: {
|
|
57
|
+
spec: /^uuid\('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'\)$/,
|
|
58
|
+
specText: (arg) => `uuid('${arg}')`,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
reference: {},
|
|
62
|
+
};
|
|
63
|
+
dictionary;
|
|
64
|
+
constructor(options) {
|
|
65
|
+
this.dictionary = DefaultValueConverter.dictionaryOf(options);
|
|
66
|
+
}
|
|
67
|
+
static parse(specType, value) {
|
|
68
|
+
if (value === undefined || value === null)
|
|
69
|
+
return { token: "None" };
|
|
70
|
+
const v = String(value);
|
|
71
|
+
const call = CALL_FORM.exec(v);
|
|
72
|
+
if (call !== null)
|
|
73
|
+
return { token: callTokenName(call[1]), arg: call[2] };
|
|
74
|
+
if (specType === "boolean") {
|
|
75
|
+
return { token: "Boolean", arg: typeof value === "boolean" ? value : v === "true" };
|
|
76
|
+
}
|
|
77
|
+
if (NUMERIC_LITERAL_TYPES.has(specType))
|
|
78
|
+
return { token: "Numeric", arg: v };
|
|
79
|
+
if (specType === "datetime") {
|
|
80
|
+
if (v === "Now")
|
|
81
|
+
return { token: "Now" };
|
|
82
|
+
if (v === "UtcNow")
|
|
83
|
+
return { token: "UtcNow" };
|
|
84
|
+
return { token: "DateTime", arg: v };
|
|
85
|
+
}
|
|
86
|
+
if (specType === "uuid") {
|
|
87
|
+
if (v === "NewId")
|
|
88
|
+
return { token: "NewId" };
|
|
89
|
+
if (v === "Empty")
|
|
90
|
+
return { token: "Empty" };
|
|
91
|
+
return { token: "Uuid", arg: v };
|
|
92
|
+
}
|
|
93
|
+
if (specType === "binary")
|
|
94
|
+
return { token: "Hex", arg: v };
|
|
95
|
+
return { token: "String", arg: v };
|
|
96
|
+
}
|
|
97
|
+
convert = (specType, text) => {
|
|
98
|
+
const parsed = DefaultValueConverter.parse(specType, text);
|
|
99
|
+
if (parsed.token === "None")
|
|
100
|
+
return null;
|
|
101
|
+
const entries = this.dictionary[specType];
|
|
102
|
+
if (entries === undefined)
|
|
103
|
+
throw new Error(`Unknown spec field type: ${specType}`);
|
|
104
|
+
const entry = entries[parsed.token];
|
|
105
|
+
if (entry === undefined)
|
|
106
|
+
return null;
|
|
107
|
+
if (!entry.spec.test(text)) {
|
|
108
|
+
throw new Error(`default_value '${text}' is not a valid ${specType} ${parsed.token} default`);
|
|
109
|
+
}
|
|
110
|
+
const expr = entry.native(parsed.arg === undefined ? "" : String(parsed.arg));
|
|
111
|
+
return expr === "" ? null : expr;
|
|
112
|
+
};
|
|
113
|
+
reverseConvert = (specType, native) => {
|
|
114
|
+
const entries = this.dictionary[specType];
|
|
115
|
+
if (entries === undefined)
|
|
116
|
+
throw new Error(`Unknown spec field type: ${specType}`);
|
|
117
|
+
for (const [name, entry] of Object.entries(entries)) {
|
|
118
|
+
const match = entry.reverse.exec(native);
|
|
119
|
+
if (match === null)
|
|
120
|
+
continue;
|
|
121
|
+
const arg = match.slice(1).find((group) => group !== undefined) ?? "";
|
|
122
|
+
return entry.specText === undefined ? name : entry.specText(arg);
|
|
123
|
+
}
|
|
124
|
+
throw new Error(`Native default '${native}' is not a valid ${specType} default`);
|
|
125
|
+
};
|
|
126
|
+
static dictionaryOf = (opts) => {
|
|
127
|
+
const spec = DefaultValueConverter.spec;
|
|
128
|
+
const newId = opts.newId();
|
|
129
|
+
const now = opts.now();
|
|
130
|
+
const utcNow = opts.utcNow();
|
|
131
|
+
const numericEntries = (type) => {
|
|
132
|
+
const numeric = spec[type].Numeric;
|
|
133
|
+
return {
|
|
134
|
+
Numeric: token(numeric, (arg) => arg, numeric.spec),
|
|
135
|
+
String: token(numeric, (arg) => arg, numeric.spec),
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
const quote = opts.quote ?? quoted;
|
|
139
|
+
const quoteReverse = opts.quoteReverse ?? quotedReverse;
|
|
140
|
+
const quotedString = {
|
|
141
|
+
String: token(spec.string.String, quote, quoteReverse),
|
|
142
|
+
Numeric: token(spec.string.String, quote, quoteReverse),
|
|
143
|
+
};
|
|
144
|
+
const dictionary = {
|
|
145
|
+
string: quotedString,
|
|
146
|
+
character: {
|
|
147
|
+
String: token(spec.character.String, quote, quoteReverse),
|
|
148
|
+
Numeric: token(spec.character.String, quote, quoteReverse),
|
|
149
|
+
},
|
|
150
|
+
boolean: {
|
|
151
|
+
Boolean: token(spec.boolean.Boolean, (arg) => (arg === "true" ? opts.booleanOn : opts.booleanOff), new RegExp(`^(${escapeRe(opts.booleanOn)}|${escapeRe(opts.booleanOff)})$`)),
|
|
152
|
+
},
|
|
153
|
+
datetime: {
|
|
154
|
+
Now: token(spec.datetime.Now, () => now, exact(now)),
|
|
155
|
+
UtcNow: token(spec.datetime.UtcNow, () => utcNow, exact(utcNow)),
|
|
156
|
+
DateTime: token(spec.datetime.DateTime, quoted, quotedReverse),
|
|
157
|
+
},
|
|
158
|
+
binary: {
|
|
159
|
+
Hex: token(spec.binary.Hex, opts.hex, opts.hexReverse),
|
|
160
|
+
},
|
|
161
|
+
uuid: {
|
|
162
|
+
NewId: token(spec.uuid.NewId, () => newId, exact(newId)),
|
|
163
|
+
Empty: token(spec.uuid.Empty, () => quoted(EMPTY_UUID), exact(quoted(EMPTY_UUID))),
|
|
164
|
+
Uuid: token(spec.uuid.Uuid, quoted, quotedReverse),
|
|
165
|
+
},
|
|
166
|
+
reference: {},
|
|
167
|
+
};
|
|
168
|
+
for (const type of NUMERIC_TYPES)
|
|
169
|
+
dictionary[type] = numericEntries(type);
|
|
170
|
+
return dictionary;
|
|
171
|
+
};
|
|
172
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type DefaultValueOptions = {
|
|
2
|
+
newId: () => string;
|
|
3
|
+
now: () => string;
|
|
4
|
+
utcNow: () => string;
|
|
5
|
+
booleanOn: string;
|
|
6
|
+
booleanOff: string;
|
|
7
|
+
hex: (arg: string) => string;
|
|
8
|
+
hexReverse: RegExp;
|
|
9
|
+
quote?: (arg: string) => string;
|
|
10
|
+
quoteReverse?: RegExp;
|
|
11
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { AlterTableStatementTextRenderer, type AlterAction, type AlterTableStatement } from "@deterministic-code/datasources";
|
|
1
|
+
import { AlterTableStatementTextRenderer, type AlterAction, type AlterTableStatement, type Column } from "@deterministic-code/datasources";
|
|
2
2
|
export declare class SqlServerAlterTableStatementRenderer extends AlterTableStatementTextRenderer {
|
|
3
|
+
protected quote(name: string): string;
|
|
4
|
+
protected typeName(column: Column): string;
|
|
5
|
+
protected columnDefault(column: Column): string;
|
|
3
6
|
renderAlter(statement: AlterTableStatement): string;
|
|
4
7
|
protected actionSql(action: AlterAction): string;
|
|
5
8
|
}
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { AlterTableStatementTextRenderer, } from "@deterministic-code/datasources";
|
|
2
|
+
import { SqlServerDriver } from "./SqlServerDriver.js";
|
|
2
3
|
export class SqlServerAlterTableStatementRenderer extends AlterTableStatementTextRenderer {
|
|
4
|
+
quote(name) {
|
|
5
|
+
return SqlServerDriver.quote(name);
|
|
6
|
+
}
|
|
7
|
+
typeName(column) {
|
|
8
|
+
return SqlServerDriver.nativeType(column);
|
|
9
|
+
}
|
|
10
|
+
columnDefault(column) {
|
|
11
|
+
return column.default == null
|
|
12
|
+
? ""
|
|
13
|
+
: column.default.name == null
|
|
14
|
+
? ` DEFAULT ${SqlServerDriver.nativeDefault(column)}`
|
|
15
|
+
: ` CONSTRAINT ${this.quote(column.default.name)} DEFAULT ${SqlServerDriver.nativeDefault(column)}`;
|
|
16
|
+
}
|
|
3
17
|
renderAlter(statement) {
|
|
4
18
|
return statement.action.kind === "renameColumn"
|
|
5
19
|
? `EXEC sp_rename '${statement.table}.${statement.action.from}', '${statement.action.to}', 'COLUMN'`
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { CreateTableStatementTextRenderer, type Column } from "@deterministic-code/datasources";
|
|
2
2
|
export declare class SqlServerCreateTableStatementRenderer extends CreateTableStatementTextRenderer {
|
|
3
|
+
protected quote(name: string): string;
|
|
4
|
+
protected typeName(column: Column): string;
|
|
5
|
+
protected columnDefault(column: Column): string;
|
|
3
6
|
protected identity(column: Column): string;
|
|
4
7
|
}
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { CreateTableStatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
+
import { SqlServerDriver } from "./SqlServerDriver.js";
|
|
2
3
|
export class SqlServerCreateTableStatementRenderer extends CreateTableStatementTextRenderer {
|
|
4
|
+
quote(name) {
|
|
5
|
+
return SqlServerDriver.quote(name);
|
|
6
|
+
}
|
|
7
|
+
typeName(column) {
|
|
8
|
+
return SqlServerDriver.nativeType(column);
|
|
9
|
+
}
|
|
10
|
+
columnDefault(column) {
|
|
11
|
+
return column.default == null
|
|
12
|
+
? ""
|
|
13
|
+
: column.default.name == null
|
|
14
|
+
? ` DEFAULT ${SqlServerDriver.nativeDefault(column)}`
|
|
15
|
+
: ` CONSTRAINT ${this.quote(column.default.name)} DEFAULT ${SqlServerDriver.nativeDefault(column)}`;
|
|
16
|
+
}
|
|
3
17
|
identity(column) {
|
|
4
18
|
return column.identity ? " IDENTITY(1,1)" : "";
|
|
5
19
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { CreateTriggerStatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
+
import { SqlServerDriver } from "./SqlServerDriver.js";
|
|
2
3
|
export class SqlServerCreateTriggerStatementRenderer extends CreateTriggerStatementTextRenderer {
|
|
3
4
|
render(statement) {
|
|
4
|
-
|
|
5
|
+
const table = SqlServerDriver.quote(statement.table);
|
|
6
|
+
const updated = SqlServerDriver.quote(statement.updatedColumn);
|
|
7
|
+
const key = SqlServerDriver.quote(statement.keyColumn);
|
|
8
|
+
return `CREATE TRIGGER ${SqlServerDriver.quote(statement.name)} ON ${table}
|
|
5
9
|
AFTER UPDATE AS
|
|
6
10
|
BEGIN
|
|
7
11
|
SET NOCOUNT ON;
|
|
8
|
-
UPDATE ${
|
|
9
|
-
FROM ${
|
|
12
|
+
UPDATE ${table} SET ${updated} = ${SqlServerDriver.utcNow()}
|
|
13
|
+
FROM ${table} t INNER JOIN inserted i ON t.${key} = i.${key};
|
|
10
14
|
END`;
|
|
11
15
|
}
|
|
12
16
|
}
|
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StatementTextRenderer, type Cell, type Column, type DatasourceOptions, type ICommand, type IConnection, type IDriver, type Information, type ITransaction, type IsolationLevel, type RoutineParameter, type TypeConverter } from "@deterministic-code/datasources";
|
|
2
|
+
import { TypeMap } from "./TypeMap.js";
|
|
3
|
+
import { DefaultValueConverter } from "./DefaultValueConverter.js";
|
|
2
4
|
export declare class SqlServerDriver implements IDriver {
|
|
5
|
+
static readonly procedurePayloadType = "NVARCHAR(MAX)";
|
|
6
|
+
static procedureSpecs: (entityName: string, byGroups: string[][], casing: {
|
|
7
|
+
routineName: (stem: string) => string;
|
|
8
|
+
pluralStem: (entity: string) => string;
|
|
9
|
+
}) => import("./procedureSpecs.js").ProcSpec[];
|
|
10
|
+
static applyKeywords: (sql: string, keyword: (word: string) => string) => string;
|
|
11
|
+
static quote: (name: string) => string;
|
|
12
|
+
static nativeType: (column: Column) => string;
|
|
13
|
+
static nativeDefault: (column: Column) => string;
|
|
14
|
+
static utcNow: () => string;
|
|
15
|
+
static nativeParam: (parameter: RoutineParameter) => string;
|
|
3
16
|
readonly statementRenderer: StatementTextRenderer;
|
|
4
17
|
readonly information: Information;
|
|
5
18
|
rowTypeConverters: Record<string, TypeConverter<Cell, Cell>>;
|
|
6
19
|
readonly quoteLeft = "[";
|
|
7
20
|
readonly quoteRight = "]";
|
|
8
|
-
readonly typeConverter:
|
|
21
|
+
readonly typeConverter: TypeMap;
|
|
9
22
|
readonly defaultValueConverter: DefaultValueConverter;
|
|
10
23
|
columnType: (name: string) => string;
|
|
11
24
|
connection: (options?: DatasourceOptions) => IConnection;
|
package/dist/SqlServerDriver.js
CHANGED
|
@@ -1,8 +1,54 @@
|
|
|
1
|
-
import { columnType as identityColumnType,
|
|
1
|
+
import { columnType as identityColumnType, StatementTextRenderer, StubTransaction, } from "@deterministic-code/datasources";
|
|
2
2
|
import { SqlServerConnection } from "./connection.js";
|
|
3
3
|
import { SqlServerStatementRenderer } from "./SqlServerStatementRenderer.js";
|
|
4
|
+
import { procedureSpecs } from "./procedureSpecs.js";
|
|
5
|
+
import { applyKeywords } from "./applyKeywords.js";
|
|
6
|
+
import { TypeMap } from "./TypeMap.js";
|
|
7
|
+
import { DefaultValueConverter } from "./DefaultValueConverter.js";
|
|
4
8
|
const statements = new StatementTextRenderer(t => new SqlServerStatementRenderer(t));
|
|
9
|
+
const types = new TypeMap({
|
|
10
|
+
string: "NVARCHAR(MAX)",
|
|
11
|
+
character: "NCHAR(1)",
|
|
12
|
+
integer: "INT",
|
|
13
|
+
unsignedinteger: "BIGINT",
|
|
14
|
+
smallinteger: "SMALLINT",
|
|
15
|
+
unsignedsmallinteger: "INT",
|
|
16
|
+
biginteger: "BIGINT",
|
|
17
|
+
unsignedbiginteger: "DECIMAL(20)",
|
|
18
|
+
float: "FLOAT",
|
|
19
|
+
decimal: "DECIMAL",
|
|
20
|
+
boolean: "BIT",
|
|
21
|
+
datetime: "DATETIME2",
|
|
22
|
+
binary: "VARBINARY(MAX)",
|
|
23
|
+
uuid: "UNIQUEIDENTIFIER",
|
|
24
|
+
reference: "INT",
|
|
25
|
+
});
|
|
26
|
+
const defaults = new DefaultValueConverter({
|
|
27
|
+
newId: () => "NEWID()",
|
|
28
|
+
now: () => "GETDATE()",
|
|
29
|
+
utcNow: () => "GETUTCDATE()",
|
|
30
|
+
booleanOn: "1",
|
|
31
|
+
booleanOff: "0",
|
|
32
|
+
hex: (arg) => `0x${arg}`,
|
|
33
|
+
hexReverse: /^0x(.*)$/,
|
|
34
|
+
});
|
|
5
35
|
export class SqlServerDriver {
|
|
36
|
+
static procedurePayloadType = "NVARCHAR(MAX)";
|
|
37
|
+
static procedureSpecs = procedureSpecs;
|
|
38
|
+
static applyKeywords = applyKeywords;
|
|
39
|
+
static quote = (name) => `[${name}]`;
|
|
40
|
+
static nativeType = (column) => column.field ? types.convertField(column.field) : column.type;
|
|
41
|
+
static nativeDefault = (column) => column.default == null
|
|
42
|
+
? ""
|
|
43
|
+
: column.field === undefined
|
|
44
|
+
? column.default.value
|
|
45
|
+
: defaults.convert(column.field.type, column.default.value) ?? column.default.value;
|
|
46
|
+
static utcNow = () => defaults.convert("datetime", "UtcNow") ?? "";
|
|
47
|
+
static nativeParam = (parameter) => parameter.type === "payload"
|
|
48
|
+
? SqlServerDriver.procedurePayloadType
|
|
49
|
+
: parameter.field
|
|
50
|
+
? types.convertField(parameter.field)
|
|
51
|
+
: types.dictionary[parameter.type] ?? parameter.type;
|
|
6
52
|
statementRenderer = statements;
|
|
7
53
|
information = {
|
|
8
54
|
descriptions: ["SQL Server driver."],
|
|
@@ -20,40 +66,8 @@ export class SqlServerDriver {
|
|
|
20
66
|
rowTypeConverters = {};
|
|
21
67
|
quoteLeft = "[";
|
|
22
68
|
quoteRight = "]";
|
|
23
|
-
typeConverter =
|
|
24
|
-
|
|
25
|
-
character: "NCHAR(1)",
|
|
26
|
-
integer: "INT",
|
|
27
|
-
unsignedinteger: "BIGINT",
|
|
28
|
-
smallinteger: "SMALLINT",
|
|
29
|
-
unsignedsmallinteger: "INT",
|
|
30
|
-
biginteger: "BIGINT",
|
|
31
|
-
unsignedbiginteger: "DECIMAL(20)",
|
|
32
|
-
float: "FLOAT",
|
|
33
|
-
decimal: "DECIMAL",
|
|
34
|
-
boolean: "BIT",
|
|
35
|
-
datetime: "DATETIME2",
|
|
36
|
-
binary: "VARBINARY(MAX)",
|
|
37
|
-
uuid: "UNIQUEIDENTIFIER",
|
|
38
|
-
reference: "INT",
|
|
39
|
-
}, {
|
|
40
|
-
dialectName: "sqlserver",
|
|
41
|
-
stringUnlimited: "NVARCHAR(MAX)",
|
|
42
|
-
stringSized: (n) => `NVARCHAR(${n})`,
|
|
43
|
-
character: "NCHAR",
|
|
44
|
-
decimal: { kind: "required", type: "DECIMAL" },
|
|
45
|
-
binaryUnlimited: "VARBINARY(MAX)",
|
|
46
|
-
binarySized: (n) => `VARBINARY(${n})`,
|
|
47
|
-
});
|
|
48
|
-
defaultValueConverter = new DefaultValueConverter({
|
|
49
|
-
newId: () => "NEWID()",
|
|
50
|
-
now: () => "GETDATE()",
|
|
51
|
-
utcNow: () => "GETUTCDATE()",
|
|
52
|
-
booleanOn: "1",
|
|
53
|
-
booleanOff: "0",
|
|
54
|
-
hex: (arg) => `0x${arg}`,
|
|
55
|
-
hexReverse: /^0x(.*)$/,
|
|
56
|
-
});
|
|
69
|
+
typeConverter = types;
|
|
70
|
+
defaultValueConverter = defaults;
|
|
57
71
|
columnType = identityColumnType;
|
|
58
72
|
connection = (options) => SqlServerConnection.from(options);
|
|
59
73
|
command = (connection) => connection.createCommand();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DropIndexStatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
+
import { SqlServerDriver } from "./SqlServerDriver.js";
|
|
2
3
|
export class SqlServerDropIndexStatementRenderer extends DropIndexStatementTextRenderer {
|
|
3
4
|
render(statement) {
|
|
4
|
-
return `DROP INDEX ${statement.name} ON ${statement.table}`;
|
|
5
|
+
return `DROP INDEX ${SqlServerDriver.quote(statement.name)} ON ${SqlServerDriver.quote(statement.table)}`;
|
|
5
6
|
}
|
|
6
7
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CreateIndexStatementTextRenderer, DropTableStatementTextRenderer, DropTriggerStatementTextRenderer, type ForeignKeyStatement } from "@deterministic-code/datasources";
|
|
2
|
+
export declare class SqlServerDropTableStatementRenderer extends DropTableStatementTextRenderer {
|
|
3
|
+
protected quote(name: string): string;
|
|
4
|
+
}
|
|
5
|
+
export declare class SqlServerCreateIndexStatementRenderer extends CreateIndexStatementTextRenderer {
|
|
6
|
+
protected quote(name: string): string;
|
|
7
|
+
}
|
|
8
|
+
export declare class SqlServerDropTriggerStatementRenderer extends DropTriggerStatementTextRenderer {
|
|
9
|
+
protected quote(name: string): string;
|
|
10
|
+
}
|
|
11
|
+
export declare class SqlServerForeignKeyStatementRenderer {
|
|
12
|
+
render(statement: ForeignKeyStatement): string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { CreateIndexStatementTextRenderer, DropTableStatementTextRenderer, DropTriggerStatementTextRenderer, } from "@deterministic-code/datasources";
|
|
2
|
+
import { SqlServerDriver } from "./SqlServerDriver.js";
|
|
3
|
+
export class SqlServerDropTableStatementRenderer extends DropTableStatementTextRenderer {
|
|
4
|
+
quote(name) {
|
|
5
|
+
return SqlServerDriver.quote(name);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class SqlServerCreateIndexStatementRenderer extends CreateIndexStatementTextRenderer {
|
|
9
|
+
quote(name) {
|
|
10
|
+
return SqlServerDriver.quote(name);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class SqlServerDropTriggerStatementRenderer extends DropTriggerStatementTextRenderer {
|
|
14
|
+
quote(name) {
|
|
15
|
+
return SqlServerDriver.quote(name);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class SqlServerForeignKeyStatementRenderer {
|
|
19
|
+
render(statement) {
|
|
20
|
+
const table = SqlServerDriver.quote(statement.table);
|
|
21
|
+
const name = SqlServerDriver.quote(statement.name);
|
|
22
|
+
if (statement.kind === "drop")
|
|
23
|
+
return `ALTER TABLE ${table} DROP CONSTRAINT ${name}`;
|
|
24
|
+
const cols = statement.columns.map(SqlServerDriver.quote).join(", ");
|
|
25
|
+
const refs = statement.referencesColumns.map(SqlServerDriver.quote).join(", ");
|
|
26
|
+
return `ALTER TABLE ${table} ADD CONSTRAINT ${name} FOREIGN KEY (${cols}) REFERENCES ${SqlServerDriver.quote(statement.referencesTable)} (${refs})`;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const quote = (ident) => `[${ident}]`;
|
|
2
|
+
const quoteString = (value) => `'${String(value).replace(/'/g, "''")}'`;
|
|
3
|
+
const truthy = (value) => value === true || value === "true" || value === "True" || value === 1;
|
|
4
|
+
const numeric = new Set([
|
|
5
|
+
"integer",
|
|
6
|
+
"unsignedinteger",
|
|
7
|
+
"smallinteger",
|
|
8
|
+
"unsignedsmallinteger",
|
|
9
|
+
"biginteger",
|
|
10
|
+
"unsignedbiginteger",
|
|
11
|
+
"reference",
|
|
12
|
+
"float",
|
|
13
|
+
"decimal",
|
|
14
|
+
]);
|
|
15
|
+
const defaultSql = (type, def) => {
|
|
16
|
+
const text = String(def);
|
|
17
|
+
if (type === "datetime" && text === "UtcNow")
|
|
18
|
+
return "GETUTCDATE()";
|
|
19
|
+
if (type === "datetime" && text === "Now")
|
|
20
|
+
return "GETDATE()";
|
|
21
|
+
if (type === "uuid" && text === "NewId")
|
|
22
|
+
return "NEWID()";
|
|
23
|
+
if (type === "boolean")
|
|
24
|
+
return text === "true" || text === "True" ? "1" : "0";
|
|
25
|
+
return quoteString(text);
|
|
26
|
+
};
|
|
27
|
+
const literal = (column) => {
|
|
28
|
+
if (column.value == null)
|
|
29
|
+
return column.field.defaultValue != null ? defaultSql(column.field.type, column.field.defaultValue) : "NULL";
|
|
30
|
+
if (column.field.type === "boolean")
|
|
31
|
+
return truthy(column.value) ? "1" : "0";
|
|
32
|
+
if (numeric.has(column.field.type))
|
|
33
|
+
return String(column.value);
|
|
34
|
+
if (column.field.type === "binary")
|
|
35
|
+
return `0x${column.value}`;
|
|
36
|
+
return quoteString(column.value);
|
|
37
|
+
};
|
|
38
|
+
export class SqlServerSqlSeedStatementRenderer {
|
|
39
|
+
render(statement) {
|
|
40
|
+
const table = quote(statement.table);
|
|
41
|
+
if (statement.kind === "delete") {
|
|
42
|
+
const key = statement.columns[0];
|
|
43
|
+
return `DELETE FROM ${table} WHERE ${quote(key.column)} = ${literal(key)}`;
|
|
44
|
+
}
|
|
45
|
+
const cols = statement.columns.map((c) => quote(c.column)).join(", ");
|
|
46
|
+
const values = statement.columns.map(literal).join(", ");
|
|
47
|
+
const insert = `INSERT INTO ${table} (${cols}) VALUES (${values})`;
|
|
48
|
+
return !statement.identity
|
|
49
|
+
? insert
|
|
50
|
+
: `SET IDENTITY_INSERT ${table} ON;\n${insert};\nSET IDENTITY_INSERT ${table} OFF`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AlterTableStatement, CreateBulkCreateStoredProcedureStatement, CreateBulkDeleteStoredProcedureStatement, CreateBulkUpdateStoredProcedureStatement, CreateCreateStoredProcedureStatement, CreateDeleteStoredProcedureStatement, CreateNestedReadStoredProcedureStatement, CreateReadStoredProcedureStatement, CreateTableStatement, CreateTriggerStatement, CreateUpdateStoredProcedureStatement, DropIndexStatement, DropRoutineStatement, IdentityInsertStatement, StatementRenderer, type StatementTextRenderer } from "@deterministic-code/datasources";
|
|
1
|
+
import { AlterTableStatement, CreateBulkCreateStoredProcedureStatement, CreateBulkDeleteStoredProcedureStatement, CreateBulkUpdateStoredProcedureStatement, CreateCreateStoredProcedureStatement, CreateDeleteStoredProcedureStatement, CreateNestedReadStoredProcedureStatement, CreateReadStoredProcedureStatement, CreateIndexStatement, CreateTableStatement, CreateTriggerStatement, CreateUpdateStoredProcedureStatement, DropIndexStatement, DropRoutineStatement, DropTableStatement, ForeignKeyStatement, IdentityInsertStatement, SqlSeedStatement, StatementRenderer, type StatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
2
|
export declare class SqlServerStatementRenderer extends StatementRenderer {
|
|
3
3
|
constructor(text: StatementTextRenderer);
|
|
4
4
|
protected visitCreateTable(create: CreateTableStatement): string;
|
|
@@ -15,4 +15,8 @@ export declare class SqlServerStatementRenderer extends StatementRenderer {
|
|
|
15
15
|
protected visitCreateBulkCreateStoredProcedureStatement(statement: CreateBulkCreateStoredProcedureStatement): string;
|
|
16
16
|
protected visitCreateBulkUpdateStoredProcedureStatement(statement: CreateBulkUpdateStoredProcedureStatement): string;
|
|
17
17
|
protected visitCreateBulkDeleteStoredProcedureStatement(statement: CreateBulkDeleteStoredProcedureStatement): string;
|
|
18
|
+
protected visitSqlSeed(seed: SqlSeedStatement): string;
|
|
19
|
+
protected visitDropTable(drop: DropTableStatement): string;
|
|
20
|
+
protected visitCreateIndex(index: CreateIndexStatement): string;
|
|
21
|
+
protected visitForeignKey(key: ForeignKeyStatement): string;
|
|
18
22
|
}
|
|
@@ -6,6 +6,8 @@ import { SqlServerDropIndexStatementRenderer } from "./SqlServerDropIndexStateme
|
|
|
6
6
|
import { SqlServerDropRoutineStatementRenderer } from "./SqlServerDropRoutineStatementRenderer.js";
|
|
7
7
|
import { SqlServerStoredProcedureStatementRenderer } from "./SqlServerStoredProcedureStatementRenderer.js";
|
|
8
8
|
import { SqlServerIdentityInsertStatementRenderer } from "./SqlServerIdentityInsertStatementRenderer.js";
|
|
9
|
+
import { SqlServerSqlSeedStatementRenderer } from "./SqlServerSqlSeedStatementRenderer.js";
|
|
10
|
+
import { SqlServerCreateIndexStatementRenderer, SqlServerDropTableStatementRenderer, SqlServerForeignKeyStatementRenderer, } from "./SqlServerQuotedRenderers.js";
|
|
9
11
|
export class SqlServerStatementRenderer extends StatementRenderer {
|
|
10
12
|
constructor(text) {
|
|
11
13
|
super(text);
|
|
@@ -52,4 +54,16 @@ export class SqlServerStatementRenderer extends StatementRenderer {
|
|
|
52
54
|
visitCreateBulkDeleteStoredProcedureStatement(statement) {
|
|
53
55
|
return new SqlServerStoredProcedureStatementRenderer().renderBulkDelete(statement);
|
|
54
56
|
}
|
|
57
|
+
visitSqlSeed(seed) {
|
|
58
|
+
return new SqlServerSqlSeedStatementRenderer().render(seed);
|
|
59
|
+
}
|
|
60
|
+
visitDropTable(drop) {
|
|
61
|
+
return new SqlServerDropTableStatementRenderer().render(drop);
|
|
62
|
+
}
|
|
63
|
+
visitCreateIndex(index) {
|
|
64
|
+
return new SqlServerCreateIndexStatementRenderer().render(index);
|
|
65
|
+
}
|
|
66
|
+
visitForeignKey(key) {
|
|
67
|
+
return new SqlServerForeignKeyStatementRenderer().render(key);
|
|
68
|
+
}
|
|
55
69
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { DefaultStatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
-
|
|
3
|
-
const
|
|
2
|
+
import { SqlServerDriver } from "./SqlServerDriver.js";
|
|
3
|
+
const typeOf = (parameter) => SqlServerDriver.nativeParam(parameter);
|
|
4
|
+
const params = (list) => list.map((p) => ` @${p.name} ${typeOf(p)}`).join(",\n");
|
|
5
|
+
const openjson = (columns) => columns.map((c) => `${c.name} ${typeOf(c)} '$.${c.name}'`).join(", ");
|
|
4
6
|
export class SqlServerStoredProcedureStatementRenderer {
|
|
5
7
|
renderCreate(statement) {
|
|
6
8
|
return `CREATE OR ALTER PROCEDURE ${statement.name}
|
|
@@ -16,7 +18,7 @@ GO`;
|
|
|
16
18
|
renderNestedRead(statement) {
|
|
17
19
|
const select = DefaultStatementTextRenderer.instance.render(statement.select);
|
|
18
20
|
const signature = statement.arguments
|
|
19
|
-
.map((argument) => ` @${argument.parameter.name} ${argument.parameter
|
|
21
|
+
.map((argument) => ` @${argument.parameter.name} ${typeOf(argument.parameter)}`)
|
|
20
22
|
.join(",\n");
|
|
21
23
|
return `CREATE OR ALTER PROCEDURE ${statement.name}
|
|
22
24
|
${signature}
|
|
@@ -37,7 +39,7 @@ END;
|
|
|
37
39
|
GO`;
|
|
38
40
|
}
|
|
39
41
|
const signature = statement.byProperties
|
|
40
|
-
.map((c) => `@${c} ${statement.parameters.find((p) => p.name === c)
|
|
42
|
+
.map((c) => `@${c} ${typeOf(statement.parameters.find((p) => p.name === c) ?? { name: c, type: statement.keyType })}`)
|
|
41
43
|
.join(", ");
|
|
42
44
|
const pred = statement.byProperties.map((c) => `${c} = @${c}`).join(" AND ");
|
|
43
45
|
return `CREATE OR ALTER PROCEDURE ${statement.name} ${signature}
|
|
@@ -71,7 +73,7 @@ GO`;
|
|
|
71
73
|
renderDelete(statement) {
|
|
72
74
|
const by = statement.byProperties.length > 0 ? statement.byProperties : [statement.key];
|
|
73
75
|
const signature = by
|
|
74
|
-
.map((c) => `@${c} ${statement.parameters.find((p) => p.name === c)
|
|
76
|
+
.map((c) => `@${c} ${typeOf(statement.parameters.find((p) => p.name === c) ?? { name: c, type: statement.keyType })}`)
|
|
75
77
|
.join(", ");
|
|
76
78
|
const pred = by.map((c) => `${c} = @${c}`).join(" AND ");
|
|
77
79
|
return `CREATE OR ALTER PROCEDURE ${statement.name} ${signature}
|
|
@@ -118,7 +120,7 @@ GO`;
|
|
|
118
120
|
AS
|
|
119
121
|
BEGIN
|
|
120
122
|
DELETE t FROM ${statement.table} t
|
|
121
|
-
JOIN OPENJSON(@${statement.payload.name}) WITH (${statement.key} ${statement.keyType} '$.${statement.key}') AS payload_rows
|
|
123
|
+
JOIN OPENJSON(@${statement.payload.name}) WITH (${statement.key} ${SqlServerDriver.nativeParam({ name: statement.key, type: statement.keyType })} '$.${statement.key}') AS payload_rows
|
|
122
124
|
ON t.${statement.key} = payload_rows.${statement.key};
|
|
123
125
|
END;
|
|
124
126
|
GO`;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IMapping, SpecField } from "@deterministic-code/datasources";
|
|
2
|
+
export declare class TypeMap implements IMapping<string, string> {
|
|
3
|
+
readonly dictionary: Record<string, string>;
|
|
4
|
+
private readonly reverse;
|
|
5
|
+
constructor(dictionary: Record<string, string>);
|
|
6
|
+
convert: (from: string) => string;
|
|
7
|
+
convertField: (field: SpecField) => string;
|
|
8
|
+
reverseConvert: (native: string) => string;
|
|
9
|
+
}
|
package/dist/TypeMap.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export class TypeMap {
|
|
2
|
+
dictionary;
|
|
3
|
+
reverse = new Map();
|
|
4
|
+
constructor(dictionary) {
|
|
5
|
+
this.dictionary = dictionary;
|
|
6
|
+
for (const [spec, native] of Object.entries(dictionary)) {
|
|
7
|
+
if (!this.reverse.has(native))
|
|
8
|
+
this.reverse.set(native, spec);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
convert = (from) => this.dictionary[from] ??
|
|
12
|
+
(() => {
|
|
13
|
+
throw new Error(`Unknown spec field type: ${from}`);
|
|
14
|
+
})();
|
|
15
|
+
convertField = (field) => {
|
|
16
|
+
if (field.type === "string")
|
|
17
|
+
return field.size == null || field.size === "unlimited" ? "NVARCHAR(MAX)" : `NVARCHAR(${field.size})`;
|
|
18
|
+
if (field.type === "character")
|
|
19
|
+
return `NCHAR(${field.size == null ? 1 : Number(field.size)})`;
|
|
20
|
+
if (field.type === "decimal") {
|
|
21
|
+
if (field.size == null)
|
|
22
|
+
return "DECIMAL";
|
|
23
|
+
if (Array.isArray(field.size) && field.size.length === 2)
|
|
24
|
+
return `DECIMAL(${field.size[0]}, ${field.size[1]})`;
|
|
25
|
+
throw new Error(`decimal field "${field.name}" size must be [precision, scale] tuple; got ${field.size}`);
|
|
26
|
+
}
|
|
27
|
+
if (field.type === "binary")
|
|
28
|
+
return field.size == null || field.size === "unlimited" ? "VARBINARY(MAX)" : `VARBINARY(${field.size})`;
|
|
29
|
+
return this.convert(field.type);
|
|
30
|
+
};
|
|
31
|
+
reverseConvert = (native) => this.reverse.get(native) ??
|
|
32
|
+
(() => {
|
|
33
|
+
throw new Error(`Unknown native field type: ${native}`);
|
|
34
|
+
})();
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const applyKeywords: (sql: string, keyword: (word: string) => string) => string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const KEYWORDS = new Set([
|
|
2
|
+
"AFTER", "ALTER", "AND", "AS", "AT", "BEGIN", "BETWEEN", "BIGINT", "BIT", "BY", "CASCADE",
|
|
3
|
+
"CAST", "CHAR", "CONSTRAINT", "CREATE", "DATETIME", "DATETIME2", "DECIMAL", "DECLARE",
|
|
4
|
+
"DEFAULT", "DELETE", "DROP", "EACH", "ELSE", "END", "EXECUTE", "EXISTS", "FALSE", "FLOAT",
|
|
5
|
+
"FOR", "FOREIGN", "FROM", "GET", "GETDATE", "GETUTCDATE", "GO", "IDENTITY", "IDENTITY_INSERT",
|
|
6
|
+
"IF", "IN", "INDEX", "INNER", "INSERT", "INT", "INTEGER", "INTO", "JOIN", "KEY", "MAX",
|
|
7
|
+
"NCHAR", "NEWID", "NOCOUNT", "NOT", "NULL", "NVARCHAR", "OFF", "ON", "OR", "ORDER",
|
|
8
|
+
"PRECISION", "PRIMARY", "PROCEDURE", "REFERENCES", "RETURN", "ROW", "ROWCOUNT",
|
|
9
|
+
"SCOPE_IDENTITY", "SELECT", "SET", "SMALLINT", "TABLE", "TEXT", "THEN", "TIME", "TIMESTAMP",
|
|
10
|
+
"TINYINT", "TRIGGER", "TRUE", "UNIQUE", "UNIQUEIDENTIFIER", "UPDATE", "VALUES", "VARBINARY",
|
|
11
|
+
"VARCHAR", "WHEN", "WHERE",
|
|
12
|
+
]);
|
|
13
|
+
const TOKEN = /('(?:''|[^'])*')|(\[[^\]]*\])|(--[^\n]*)|(\/\*[\s\S]*?\*\/)|(?<word>[A-Za-z_][A-Za-z0-9_]*)/g;
|
|
14
|
+
export const applyKeywords = (sql, keyword) => sql.replace(TOKEN, (match, _s, _br, _lc, _bc, word) => word !== undefined && KEYWORDS.has(word.toUpperCase()) ? keyword(word) : match);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type ProcOp = "create" | "read" | "update" | "delete" | "bulkCreate" | "bulkUpdate" | "bulkDelete";
|
|
2
|
+
export type ProcSpec = {
|
|
3
|
+
op: ProcOp;
|
|
4
|
+
name: string;
|
|
5
|
+
byProperties: string[];
|
|
6
|
+
};
|
|
7
|
+
export declare const procedureSpecs: (entityName: string, byGroups: string[][], casing: {
|
|
8
|
+
routineName: (stem: string) => string;
|
|
9
|
+
pluralStem: (entity: string) => string;
|
|
10
|
+
}) => ProcSpec[];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const procedureSpecs = (entityName, byGroups, casing) => {
|
|
2
|
+
const name = (stem) => casing.routineName(stem);
|
|
3
|
+
const plural = casing.pluralStem(entityName);
|
|
4
|
+
const bySuffix = (props) => props.length === 0 ? "" : `_by_${props.join("_")}`;
|
|
5
|
+
return [
|
|
6
|
+
{ op: "create", name: name(`create_${plural}`), byProperties: [] },
|
|
7
|
+
{ op: "read", name: name(`read_${plural}`), byProperties: [] },
|
|
8
|
+
...byGroups.flatMap((props) => [
|
|
9
|
+
{ op: "read", name: name(`read_${plural}${bySuffix(props)}`), byProperties: props },
|
|
10
|
+
{ op: "update", name: name(`update_${plural}${bySuffix(props)}`), byProperties: props },
|
|
11
|
+
{ op: "delete", name: name(`delete_${plural}${bySuffix(props)}`), byProperties: props },
|
|
12
|
+
]),
|
|
13
|
+
{ op: "bulkCreate", name: name(`bulk_create_${plural}`), byProperties: [] },
|
|
14
|
+
{ op: "bulkUpdate", name: name(`bulk_update_${plural}`), byProperties: [] },
|
|
15
|
+
{ op: "bulkDelete", name: name(`bulk_delete_${plural}`), byProperties: [] },
|
|
16
|
+
];
|
|
17
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deterministic-code/datasources-sqlserver",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.js",
|
|
@@ -18,6 +18,6 @@
|
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@deterministic-code/datasources": "^0.1.
|
|
21
|
+
"@deterministic-code/datasources": "^0.1.29"
|
|
22
22
|
}
|
|
23
23
|
}
|