prisma-sql 1.76.2 → 1.78.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prisma-sql",
3
- "version": "1.76.2",
3
+ "version": "1.78.0",
4
4
  "description": "Convert Prisma queries to optimized SQL with type safety. 2-7x faster than Prisma Client.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
package/readme.md CHANGED
@@ -534,82 +534,6 @@ const users = await prisma.user.findMany({
534
534
 
535
535
  That helps both the planner and the reducer keep result shapes predictable.
536
536
 
537
- #### 4) Avoid unbounded deep fan-out in a single query
538
-
539
- This is the biggest real-world improvement lever.
540
-
541
- Less ideal:
542
-
543
- ```ts
544
- await prisma.organization.findMany({
545
- include: {
546
- users: {
547
- include: {
548
- posts: {
549
- include: {
550
- comments: true,
551
- },
552
- },
553
- },
554
- },
555
- },
556
- })
557
- ```
558
-
559
- Usually better:
560
-
561
- - page parents
562
- - cap nested collections with `take`
563
- - add nested `where`
564
- - split unrelated heavy branches into `$batch`
565
-
566
- Example:
567
-
568
- ```ts
569
- const result = await prisma.$batch((batch) => ({
570
- orgs: batch.organization.findMany({
571
- take: 20,
572
- orderBy: { id: 'asc' },
573
- }),
574
- recentUsers: batch.user.findMany({
575
- take: 50,
576
- orderBy: { createdAt: 'desc' },
577
- }),
578
- }))
579
- ```
580
-
581
- #### 5) Use one-to-one uniqueness where it is actually one-to-one
582
-
583
- If the database guarantees one child row, encode that in Prisma.
584
-
585
- This can let the planner avoid unnecessarily defensive high-fanout strategies.
586
-
587
- #### 6) Keep nested filters sargable
588
-
589
- Prefer predicates that use indexed equality/range conditions.
590
-
591
- Better:
592
-
593
- ```ts
594
- {
595
- include: {
596
- comments: {
597
- where: {
598
- postId: 42,
599
- createdAt: { gte: someDate },
600
- },
601
- orderBy: { createdAt: 'desc' },
602
- },
603
- },
604
- }
605
- ```
606
-
607
- Less planner-friendly:
608
-
609
- - broad `contains` / `%term%` everywhere
610
- - unindexed OR-heavy nested filters
611
- - deep includes without limits
612
-
613
537
  ### What to configure
614
538
 
615
539
  Use the cardinality planner wherever your generator/runtime exposes it.
@@ -657,6 +581,65 @@ What good results look like:
657
581
  - high-fanout includes stop exploding row counts
658
582
  - moving a heavy include into `$batch` or splitting it improves latency materially
659
583
 
584
+ ## Deployment without database access at build time
585
+
586
+ The cardinality planner collects relation statistics and roundtrip cost measurements directly from the database during `prisma generate`. In CI/CD pipelines or containerized builds, the database is often unreachable.
587
+
588
+ ### Skip planner during generation
589
+
590
+ Set `PRISMA_SQL_SKIP_PLANNER=true` to skip stats collection at generate time. The generator will emit default planner values instead.
591
+
592
+ ```bash
593
+ PRISMA_SQL_SKIP_PLANNER=true npx prisma generate
594
+ ```
595
+
596
+ ### Collect stats before server start
597
+
598
+ Run `prisma-sql-collect-stats` as a pre-start step, after deployment, when the database is reachable.
599
+
600
+ ```bash
601
+ prisma-sql-collect-stats \
602
+ --output dist/prisma/generated/sql/planner.generated.js \
603
+ --prisma-client dist/prisma/generated/client/index.js
604
+ ```
605
+
606
+ | Flag | Default | Description |
607
+ | ----------------- | -------------------------------------------------- | -------------------------------------------------------------- |
608
+ | `--output` | `./dist/prisma/generated/sql/planner.generated.js` | Path to the generated planner module |
609
+ | `--prisma-client` | `@prisma/client` | Path to the compiled Prisma client (must expose `Prisma.dmmf`) |
610
+
611
+ The script reads `DATABASE_URL` from the environment (supports `.env` via `dotenv`). If the connection fails or times out, it exits silently without blocking startup.
612
+
613
+ ### Example scripts
614
+
615
+ ```json
616
+ {
617
+ "prisma:generate": "PRISMA_SQL_SKIP_PLANNER=true prisma generate",
618
+ "collect-planner-stats": "prisma-sql-collect-stats --output dist/prisma/generated/sql/planner.generated.js --prisma-client dist/prisma/generated/client/index.js",
619
+ "start:production": "yarn collect-planner-stats; node dist/src/index.js"
620
+ }
621
+ ```
622
+
623
+ The semicolon (`;`) after `collect-planner-stats` ensures the server starts even if stats collection fails. Use `&&` instead if you want startup to abort on failure.
624
+
625
+ ### What happens with default planner values
626
+
627
+ When stats are not collected, the planner uses conservative defaults:
628
+
629
+ - `roundtripRowEquivalent`: 73
630
+ - `jsonRowFactor`: 1.5
631
+ - `relationStats`: empty (all relations treated as unknown cardinality)
632
+
633
+ This means the planner cannot make informed decisions about join strategies. Queries still work correctly — the planner falls back to safe general-purpose strategies — but relation-heavy reads may not use the optimal execution plan.
634
+
635
+ ### Timeout control
636
+
637
+ Stats collection has a default timeout of 15 seconds. Override with:
638
+
639
+ ```bash
640
+ PRISMA_SQL_PLANNER_TIMEOUT_MS=5000 yarn collect-planner-stats
641
+ ```
642
+
660
643
  ### Practical recommendations
661
644
 
662
645
  For best results with the planner: