@prisma-next/sql-runtime 0.3.0-dev.6 → 0.3.0-dev.63

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.
Files changed (166) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +137 -26
  3. package/dist/exports-BhZqJPVb.mjs +771 -0
  4. package/dist/exports-BhZqJPVb.mjs.map +1 -0
  5. package/dist/index-D59jqEKF.d.mts +159 -0
  6. package/dist/index-D59jqEKF.d.mts.map +1 -0
  7. package/dist/index.d.mts +2 -0
  8. package/dist/index.mjs +3 -0
  9. package/dist/test/utils.d.mts +82 -0
  10. package/dist/test/utils.d.mts.map +1 -0
  11. package/dist/test/utils.mjs +212 -0
  12. package/dist/test/utils.mjs.map +1 -0
  13. package/package.json +32 -25
  14. package/src/codecs/decoding.ts +84 -3
  15. package/src/codecs/encoding.ts +15 -2
  16. package/src/codecs/json-schema-validation.ts +61 -0
  17. package/src/exports/index.ts +14 -6
  18. package/src/lower-sql-plan.ts +8 -8
  19. package/src/plugins/lints.ts +204 -0
  20. package/src/sql-context.ts +385 -98
  21. package/src/sql-family-adapter.ts +9 -5
  22. package/src/sql-marker.ts +2 -2
  23. package/src/sql-runtime.ts +131 -31
  24. package/test/async-iterable-result.test.ts +42 -34
  25. package/test/context.types.test-d.ts +68 -0
  26. package/test/execution-stack.test.ts +166 -0
  27. package/test/json-schema-validation.test.ts +653 -0
  28. package/test/lints.test.ts +330 -0
  29. package/test/parameterized-types.test.ts +539 -0
  30. package/test/sql-context.test.ts +292 -117
  31. package/test/sql-family-adapter.test.ts +7 -6
  32. package/test/sql-runtime.test.ts +218 -30
  33. package/test/utils.ts +80 -51
  34. package/dist/accelerate-EEKAFGN3-P6A6XJWJ.js +0 -137863
  35. package/dist/accelerate-EEKAFGN3-P6A6XJWJ.js.map +0 -1
  36. package/dist/amcheck-24VY6X5V.js +0 -13
  37. package/dist/amcheck-24VY6X5V.js.map +0 -1
  38. package/dist/bloom-VS74NLHT.js +0 -13
  39. package/dist/bloom-VS74NLHT.js.map +0 -1
  40. package/dist/btree_gin-WBC4EAAI.js +0 -13
  41. package/dist/btree_gin-WBC4EAAI.js.map +0 -1
  42. package/dist/btree_gist-UNC6QD3M.js +0 -13
  43. package/dist/btree_gist-UNC6QD3M.js.map +0 -1
  44. package/dist/chunk-3KTOEDFX.js +0 -49
  45. package/dist/chunk-3KTOEDFX.js.map +0 -1
  46. package/dist/chunk-47DZBRQC.js +0 -1280
  47. package/dist/chunk-47DZBRQC.js.map +0 -1
  48. package/dist/chunk-52N6AFZM.js +0 -133
  49. package/dist/chunk-52N6AFZM.js.map +0 -1
  50. package/dist/chunk-7D4SUZUM.js +0 -38
  51. package/dist/chunk-7D4SUZUM.js.map +0 -1
  52. package/dist/chunk-C6I3V3DM.js +0 -455
  53. package/dist/chunk-C6I3V3DM.js.map +0 -1
  54. package/dist/chunk-ECWIHLAT.js +0 -37
  55. package/dist/chunk-ECWIHLAT.js.map +0 -1
  56. package/dist/chunk-EI626SDC.js +0 -105
  57. package/dist/chunk-EI626SDC.js.map +0 -1
  58. package/dist/chunk-UKKOYUGL.js +0 -578
  59. package/dist/chunk-UKKOYUGL.js.map +0 -1
  60. package/dist/chunk-XPLNMXQV.js +0 -1537
  61. package/dist/chunk-XPLNMXQV.js.map +0 -1
  62. package/dist/citext-T7MXGUY7.js +0 -13
  63. package/dist/citext-T7MXGUY7.js.map +0 -1
  64. package/dist/client-5FENX6AW.js +0 -299
  65. package/dist/client-5FENX6AW.js.map +0 -1
  66. package/dist/cube-TFDQBZCI.js +0 -13
  67. package/dist/cube-TFDQBZCI.js.map +0 -1
  68. package/dist/dict_int-AEUOPGWP.js +0 -13
  69. package/dist/dict_int-AEUOPGWP.js.map +0 -1
  70. package/dist/dict_xsyn-DAAYX3FL.js +0 -13
  71. package/dist/dict_xsyn-DAAYX3FL.js.map +0 -1
  72. package/dist/dist-AQ3LWXOX.js +0 -570
  73. package/dist/dist-AQ3LWXOX.js.map +0 -1
  74. package/dist/dist-LBVX6BJW.js +0 -189
  75. package/dist/dist-LBVX6BJW.js.map +0 -1
  76. package/dist/dist-WLKUVDN2.js +0 -5127
  77. package/dist/dist-WLKUVDN2.js.map +0 -1
  78. package/dist/earthdistance-KIGTF4LE.js +0 -13
  79. package/dist/earthdistance-KIGTF4LE.js.map +0 -1
  80. package/dist/file_fdw-5N55UP6I.js +0 -13
  81. package/dist/file_fdw-5N55UP6I.js.map +0 -1
  82. package/dist/fuzzystrmatch-KN3YWBFP.js +0 -13
  83. package/dist/fuzzystrmatch-KN3YWBFP.js.map +0 -1
  84. package/dist/hstore-YX726NKN.js +0 -13
  85. package/dist/hstore-YX726NKN.js.map +0 -1
  86. package/dist/http-exception-FZY2H4OF.js +0 -8
  87. package/dist/http-exception-FZY2H4OF.js.map +0 -1
  88. package/dist/index.js +0 -30
  89. package/dist/index.js.map +0 -1
  90. package/dist/intarray-NKVXNO2D.js +0 -13
  91. package/dist/intarray-NKVXNO2D.js.map +0 -1
  92. package/dist/isn-FTEMJGEV.js +0 -13
  93. package/dist/isn-FTEMJGEV.js.map +0 -1
  94. package/dist/lo-DB7L4NGI.js +0 -13
  95. package/dist/lo-DB7L4NGI.js.map +0 -1
  96. package/dist/logger-WQ7SHNDD.js +0 -68
  97. package/dist/logger-WQ7SHNDD.js.map +0 -1
  98. package/dist/ltree-Z32TZT6W.js +0 -13
  99. package/dist/ltree-Z32TZT6W.js.map +0 -1
  100. package/dist/nodefs-NM46ACH7.js +0 -31
  101. package/dist/nodefs-NM46ACH7.js.map +0 -1
  102. package/dist/opfs-ahp-NJO33LVZ.js +0 -332
  103. package/dist/opfs-ahp-NJO33LVZ.js.map +0 -1
  104. package/dist/pageinspect-YP3IZR4X.js +0 -13
  105. package/dist/pageinspect-YP3IZR4X.js.map +0 -1
  106. package/dist/pg_buffercache-7TD5J2FB.js +0 -13
  107. package/dist/pg_buffercache-7TD5J2FB.js.map +0 -1
  108. package/dist/pg_dump-SG4KYBUB.js +0 -2492
  109. package/dist/pg_dump-SG4KYBUB.js.map +0 -1
  110. package/dist/pg_freespacemap-DZDNCPZK.js +0 -13
  111. package/dist/pg_freespacemap-DZDNCPZK.js.map +0 -1
  112. package/dist/pg_surgery-J2MUEWEP.js +0 -13
  113. package/dist/pg_surgery-J2MUEWEP.js.map +0 -1
  114. package/dist/pg_trgm-7VNQOYS6.js +0 -13
  115. package/dist/pg_trgm-7VNQOYS6.js.map +0 -1
  116. package/dist/pg_visibility-TTSIPHFL.js +0 -13
  117. package/dist/pg_visibility-TTSIPHFL.js.map +0 -1
  118. package/dist/pg_walinspect-KPFHSHRJ.js +0 -13
  119. package/dist/pg_walinspect-KPFHSHRJ.js.map +0 -1
  120. package/dist/proxy-signals-GUDAMDHV.js +0 -39
  121. package/dist/proxy-signals-GUDAMDHV.js.map +0 -1
  122. package/dist/seg-IYVDLE4O.js +0 -13
  123. package/dist/seg-IYVDLE4O.js.map +0 -1
  124. package/dist/src/codecs/decoding.d.ts +0 -4
  125. package/dist/src/codecs/decoding.d.ts.map +0 -1
  126. package/dist/src/codecs/encoding.d.ts +0 -5
  127. package/dist/src/codecs/encoding.d.ts.map +0 -1
  128. package/dist/src/codecs/validation.d.ts +0 -6
  129. package/dist/src/codecs/validation.d.ts.map +0 -1
  130. package/dist/src/exports/index.d.ts +0 -11
  131. package/dist/src/exports/index.d.ts.map +0 -1
  132. package/dist/src/index.d.ts +0 -2
  133. package/dist/src/index.d.ts.map +0 -1
  134. package/dist/src/lower-sql-plan.d.ts +0 -15
  135. package/dist/src/lower-sql-plan.d.ts.map +0 -1
  136. package/dist/src/sql-context.d.ts +0 -65
  137. package/dist/src/sql-context.d.ts.map +0 -1
  138. package/dist/src/sql-family-adapter.d.ts +0 -10
  139. package/dist/src/sql-family-adapter.d.ts.map +0 -1
  140. package/dist/src/sql-marker.d.ts +0 -22
  141. package/dist/src/sql-marker.d.ts.map +0 -1
  142. package/dist/src/sql-runtime.d.ts +0 -25
  143. package/dist/src/sql-runtime.d.ts.map +0 -1
  144. package/dist/tablefunc-EF4RCS7S.js +0 -13
  145. package/dist/tablefunc-EF4RCS7S.js.map +0 -1
  146. package/dist/tcn-3VT5BQYW.js +0 -13
  147. package/dist/tcn-3VT5BQYW.js.map +0 -1
  148. package/dist/test/utils.d.ts +0 -59
  149. package/dist/test/utils.d.ts.map +0 -1
  150. package/dist/test/utils.js +0 -24634
  151. package/dist/test/utils.js.map +0 -1
  152. package/dist/tiny-CW6F4GX6.js +0 -10
  153. package/dist/tiny-CW6F4GX6.js.map +0 -1
  154. package/dist/tsm_system_rows-ES7KNUQH.js +0 -13
  155. package/dist/tsm_system_rows-ES7KNUQH.js.map +0 -1
  156. package/dist/tsm_system_time-76WEIMBG.js +0 -13
  157. package/dist/tsm_system_time-76WEIMBG.js.map +0 -1
  158. package/dist/unaccent-7RYF3R64.js +0 -13
  159. package/dist/unaccent-7RYF3R64.js.map +0 -1
  160. package/dist/utility-Q5A254LJ-J4HTKZPT.js +0 -347
  161. package/dist/utility-Q5A254LJ-J4HTKZPT.js.map +0 -1
  162. package/dist/uuid_ossp-4ETE4FPE.js +0 -13
  163. package/dist/uuid_ossp-4ETE4FPE.js.map +0 -1
  164. package/dist/vector-74GPNV7V.js +0 -13
  165. package/dist/vector-74GPNV7V.js.map +0 -1
  166. package/src/index.ts +0 -1
@@ -0,0 +1,330 @@
1
+ import type { ExecutionPlan, PlanMeta } from '@prisma-next/contract/types';
2
+ import type { PluginContext } from '@prisma-next/runtime-executor';
3
+ import type {
4
+ BinaryExpr,
5
+ DeleteAst,
6
+ SelectAst,
7
+ UpdateAst,
8
+ } from '@prisma-next/sql-relational-core/ast';
9
+ import {
10
+ createColumnRef,
11
+ createDeleteAst,
12
+ createSelectAst,
13
+ createTableRef,
14
+ createUpdateAst,
15
+ } from '@prisma-next/sql-relational-core/ast';
16
+ import { timeouts } from '@prisma-next/test-utils';
17
+ import { describe, expect, it, vi } from 'vitest';
18
+ import { lints } from '../src/plugins/lints';
19
+
20
+ function createPluginContext(): PluginContext<unknown, unknown, unknown> {
21
+ return {
22
+ contract: {},
23
+ adapter: {},
24
+ driver: {},
25
+ mode: 'strict' as const,
26
+ now: () => Date.now(),
27
+ log: {
28
+ info: vi.fn(),
29
+ warn: vi.fn(),
30
+ error: vi.fn(),
31
+ },
32
+ };
33
+ }
34
+
35
+ const baseMeta: PlanMeta = {
36
+ target: 'postgres',
37
+ storageHash: 'sha256:test',
38
+ lane: 'dsl',
39
+ paramDescriptors: [],
40
+ };
41
+
42
+ type PlanOverrides = Partial<Omit<ExecutionPlan, 'meta'>> & { meta?: Partial<PlanMeta> };
43
+
44
+ function createPlan(overrides: PlanOverrides): ExecutionPlan {
45
+ const { meta: metaOverrides, ...rest } = overrides;
46
+ return {
47
+ sql: 'SELECT 1',
48
+ params: [],
49
+ meta: { ...baseMeta, ...(metaOverrides ?? {}) } as PlanMeta,
50
+ ...rest,
51
+ } as ExecutionPlan;
52
+ }
53
+
54
+ const userTable = createTableRef('user');
55
+ const idCol = createColumnRef('user', 'id');
56
+
57
+ describe('lints plugin', () => {
58
+ describe('DELETE without WHERE', () => {
59
+ it('blocks execution when ast is delete without where', async () => {
60
+ const deleteAst: DeleteAst = {
61
+ kind: 'delete',
62
+ table: userTable,
63
+ };
64
+ const plan = createPlan({ ast: deleteAst });
65
+ const plugin = lints();
66
+ const ctx = createPluginContext();
67
+
68
+ await expect(plugin.beforeExecute?.(plan, ctx)).rejects.toMatchObject({
69
+ code: 'LINT.DELETE_WITHOUT_WHERE',
70
+ message: expect.stringContaining('DELETE without WHERE'),
71
+ details: { table: 'user' },
72
+ });
73
+ });
74
+
75
+ it('allows delete with where clause', async () => {
76
+ const where: BinaryExpr = {
77
+ kind: 'bin',
78
+ op: 'eq',
79
+ left: idCol,
80
+ right: { kind: 'param', index: 1 },
81
+ };
82
+ const deleteAst = createDeleteAst({ table: userTable, where });
83
+ const plan = createPlan({ ast: deleteAst });
84
+ const plugin = lints();
85
+ const ctx = createPluginContext();
86
+
87
+ await plugin.beforeExecute?.(plan, ctx);
88
+ expect(ctx.log.warn).not.toHaveBeenCalled();
89
+ });
90
+ });
91
+
92
+ describe('UPDATE without WHERE', () => {
93
+ it('blocks execution when ast is update without where', async () => {
94
+ const updateAst: UpdateAst = {
95
+ kind: 'update',
96
+ table: userTable,
97
+ set: { email: { kind: 'param', index: 1 } },
98
+ };
99
+ const plan = createPlan({ ast: updateAst });
100
+ const plugin = lints();
101
+ const ctx = createPluginContext();
102
+
103
+ await expect(plugin.beforeExecute?.(plan, ctx)).rejects.toMatchObject({
104
+ code: 'LINT.UPDATE_WITHOUT_WHERE',
105
+ message: expect.stringContaining('UPDATE without WHERE'),
106
+ details: { table: 'user' },
107
+ });
108
+ });
109
+
110
+ it('allows update with where clause', async () => {
111
+ const where: BinaryExpr = {
112
+ kind: 'bin',
113
+ op: 'eq',
114
+ left: idCol,
115
+ right: { kind: 'param', index: 1 },
116
+ };
117
+ const updateAst = createUpdateAst({
118
+ table: userTable,
119
+ set: { email: { kind: 'param', index: 2 } },
120
+ where,
121
+ });
122
+ const plan = createPlan({ ast: updateAst });
123
+ const plugin = lints();
124
+ const ctx = createPluginContext();
125
+
126
+ await plugin.beforeExecute?.(plan, ctx);
127
+ expect(ctx.log.warn).not.toHaveBeenCalled();
128
+ });
129
+ });
130
+
131
+ describe('Unbounded SELECT', () => {
132
+ it('warns when select lacks limit', async () => {
133
+ const selectAst: SelectAst = createSelectAst({
134
+ from: userTable,
135
+ project: [{ alias: 'id', expr: idCol }],
136
+ });
137
+ const plan = createPlan({ ast: selectAst });
138
+ const plugin = lints();
139
+ const ctx = createPluginContext();
140
+
141
+ await plugin.beforeExecute?.(plan, ctx);
142
+ expect(ctx.log.warn).toHaveBeenCalledWith(
143
+ expect.objectContaining({
144
+ code: 'LINT.NO_LIMIT',
145
+ message: expect.stringContaining('Unbounded SELECT'),
146
+ }),
147
+ );
148
+ });
149
+
150
+ it('allows select with limit', async () => {
151
+ const selectAst = createSelectAst({
152
+ from: userTable,
153
+ project: [{ alias: 'id', expr: idCol }],
154
+ limit: 10,
155
+ });
156
+ const plan = createPlan({ ast: selectAst });
157
+ const plugin = lints();
158
+ const ctx = createPluginContext();
159
+
160
+ await plugin.beforeExecute?.(plan, ctx);
161
+ expect(ctx.log.warn).not.toHaveBeenCalled();
162
+ });
163
+
164
+ it('throws when noLimit severity is error', async () => {
165
+ const selectAst = createSelectAst({
166
+ from: userTable,
167
+ project: [{ alias: 'id', expr: idCol }],
168
+ });
169
+ const plan = createPlan({ ast: selectAst });
170
+ const plugin = lints({ severities: { noLimit: 'error' } });
171
+ const ctx = createPluginContext();
172
+
173
+ await expect(plugin.beforeExecute?.(plan, ctx)).rejects.toMatchObject({
174
+ code: 'LINT.NO_LIMIT',
175
+ message: expect.stringContaining('Unbounded SELECT'),
176
+ });
177
+ });
178
+ });
179
+
180
+ describe('SELECT * intent', () => {
181
+ it('warns when selectAllIntent present on ast', async () => {
182
+ const selectAst = createSelectAst({
183
+ from: userTable,
184
+ project: [{ alias: 'id', expr: idCol }],
185
+ limit: 1,
186
+ selectAllIntent: { table: 'user' },
187
+ });
188
+ const plan = createPlan({ ast: selectAst });
189
+ const plugin = lints();
190
+ const ctx = createPluginContext();
191
+
192
+ await plugin.beforeExecute?.(plan, ctx);
193
+ expect(ctx.log.warn).toHaveBeenCalledWith(
194
+ expect.objectContaining({
195
+ code: 'LINT.SELECT_STAR',
196
+ message: expect.stringContaining('selectAll intent'),
197
+ details: { table: 'user' },
198
+ }),
199
+ );
200
+ });
201
+
202
+ it('warns when selectAllIntent in meta.annotations', async () => {
203
+ const selectAst = createSelectAst({
204
+ from: userTable,
205
+ project: [{ alias: 'id', expr: idCol }],
206
+ limit: 1,
207
+ });
208
+ const plan = createPlan({
209
+ ast: selectAst,
210
+ meta: { annotations: { selectAllIntent: { table: 'user' } } },
211
+ });
212
+ const plugin = lints();
213
+ const ctx = createPluginContext();
214
+
215
+ await plugin.beforeExecute?.(plan, ctx);
216
+ expect(ctx.log.warn).toHaveBeenCalledWith(
217
+ expect.objectContaining({
218
+ code: 'LINT.SELECT_STAR',
219
+ message: expect.stringContaining('selectAll intent'),
220
+ }),
221
+ );
222
+ });
223
+
224
+ it('allows select without selectAll intent', async () => {
225
+ const selectAst = createSelectAst({
226
+ from: userTable,
227
+ project: [{ alias: 'id', expr: idCol }],
228
+ limit: 1,
229
+ });
230
+ const plan = createPlan({ ast: selectAst });
231
+ const plugin = lints();
232
+ const ctx = createPluginContext();
233
+
234
+ await plugin.beforeExecute?.(plan, ctx);
235
+ expect(ctx.log.warn).not.toHaveBeenCalled();
236
+ });
237
+
238
+ it('throws when selectStar severity is error', async () => {
239
+ const selectAst = createSelectAst({
240
+ from: userTable,
241
+ project: [{ alias: 'id', expr: idCol }],
242
+ limit: 1,
243
+ selectAllIntent: { table: 'user' },
244
+ });
245
+ const plan = createPlan({ ast: selectAst });
246
+ const plugin = lints({ severities: { selectStar: 'error' } });
247
+ const ctx = createPluginContext();
248
+
249
+ await expect(plugin.beforeExecute?.(plan, ctx)).rejects.toMatchObject({
250
+ code: 'LINT.SELECT_STAR',
251
+ message: expect.stringContaining('selectAll intent'),
252
+ });
253
+ });
254
+ });
255
+
256
+ describe('fallback when plan.ast missing', () => {
257
+ it(
258
+ 'runs raw heuristic when fallbackWhenAstMissing is raw',
259
+ async () => {
260
+ const plan = createPlan({
261
+ ast: undefined,
262
+ sql: 'SELECT id FROM user',
263
+ params: [],
264
+ meta: {},
265
+ });
266
+ const plugin = lints({ fallbackWhenAstMissing: 'raw' });
267
+ const ctx = createPluginContext();
268
+
269
+ await plugin.beforeExecute?.(plan, ctx);
270
+ expect(ctx.log.warn).toHaveBeenCalledWith(
271
+ expect.objectContaining({
272
+ code: 'LINT.NO_LIMIT',
273
+ message: expect.stringContaining('omits LIMIT'),
274
+ }),
275
+ );
276
+ },
277
+ timeouts.default,
278
+ );
279
+
280
+ it('skips linting when fallbackWhenAstMissing is skip', async () => {
281
+ const plan = createPlan({
282
+ ast: undefined,
283
+ sql: 'SELECT * FROM user',
284
+ params: [],
285
+ meta: {},
286
+ });
287
+ const plugin = lints({ fallbackWhenAstMissing: 'skip' });
288
+ const ctx = createPluginContext();
289
+
290
+ await plugin.beforeExecute?.(plan, ctx);
291
+ expect(ctx.log.warn).not.toHaveBeenCalled();
292
+ });
293
+
294
+ it('defaults to raw fallback when ast missing', async () => {
295
+ const plan = createPlan({
296
+ ast: undefined,
297
+ sql: 'SELECT id FROM user',
298
+ params: [],
299
+ meta: {},
300
+ });
301
+ const plugin = lints();
302
+ const ctx = createPluginContext();
303
+
304
+ await plugin.beforeExecute?.(plan, ctx);
305
+ expect(ctx.log.warn).toHaveBeenCalledWith(
306
+ expect.objectContaining({
307
+ code: 'LINT.NO_LIMIT',
308
+ message: expect.stringContaining('omits LIMIT'),
309
+ }),
310
+ );
311
+ });
312
+ });
313
+
314
+ describe('INSERT', () => {
315
+ it('passes when ast is insert', async () => {
316
+ const plan = createPlan({
317
+ ast: {
318
+ kind: 'insert',
319
+ table: userTable,
320
+ values: { email: { kind: 'param', index: 1 } },
321
+ },
322
+ });
323
+ const plugin = lints();
324
+ const ctx = createPluginContext();
325
+
326
+ await plugin.beforeExecute?.(plan, ctx);
327
+ expect(ctx.log.warn).not.toHaveBeenCalled();
328
+ });
329
+ });
330
+ });