@dbsp/nql 1.1.0 → 1.2.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/README.md CHANGED
@@ -15,16 +15,22 @@ pnpm add @dbsp/nql
15
15
 
16
16
  ```typescript
17
17
  // doctest: skip — exec-only operation; compile from @dbsp/nql is not in doctest preamble and orm.from(intent).all() requires a real PostgreSQL connection
18
+ import { createPgsqlCompileOnlyAdapter } from '@dbsp/adapter-pgsql';
18
19
  import { compile } from '@dbsp/nql';
19
20
 
20
- // Compile an NQL query to IntentAST
21
- const intent = compile(
21
+ // Compile an NQL query to a public intent bundle
22
+ const compiled = compile(
22
23
  "users | where active = true | select name, email | order name asc | limit 20",
23
- schema
24
+ db.model
24
25
  );
25
26
 
26
- // Pass the intent to the ORM
27
- const users = await orm.from(intent).all();
27
+ if (!compiled.success || !compiled.ast?.query) {
28
+ throw new Error(compiled.errors.map((e) => e.message).join(', '));
29
+ }
30
+
31
+ // Pass the whole bundle to the adapter; bound params are explicit public IR nodes
32
+ const adapter = createPgsqlCompileOnlyAdapter();
33
+ const query = adapter.compile(compiled.ast, { model: db.model });
28
34
  ```
29
35
 
30
36
  ## Syntax overview
@@ -51,12 +57,39 @@ orders | group customerId | select customerId, sum(total) as revenue
51
57
 
52
58
  - **Pipe syntax** — Readable left-to-right data flow (`table | filter | select | order`)
53
59
  - **SQL-style literals** — Single-quoted strings (`'value'`), not double-quoted
60
+ - **Named parameters** — Bind runtime values with `:name` in expression positions
54
61
  - **CTE support** — `WITH name AS (subquery)` for named subqueries
55
62
  - **Schema-aware** — Validates column names and relation paths against `ModelIR` at parse time
56
63
  - **LLM-friendly** — Concise syntax designed for AI-generated queries
57
64
  - **Chevrotain-based** — Robust lexer + parser with structured error recovery
58
65
  - **Composable** — Output `IntentAST` is the same type used by the TypeScript fluent builders
59
66
 
67
+ ## Named parameters
68
+
69
+ Use `:name` placeholders for runtime values and pass a `params` map to the compiler:
70
+
71
+ ```typescript
72
+ // doctest: skip — illustrative direct compiler params example
73
+ import { createPgsqlCompileOnlyAdapter } from '@dbsp/adapter-pgsql';
74
+ import { compile } from '@dbsp/nql';
75
+
76
+ const compiled = compile(
77
+ 'users | where id = :id and active = :active | limit :limit',
78
+ db.model,
79
+ undefined,
80
+ { params: { id: 42, active: true, limit: 10 } },
81
+ );
82
+
83
+ if (!compiled.success || !compiled.ast?.query) {
84
+ throw new Error(compiled.errors.map((e) => e.message).join(', '));
85
+ }
86
+
87
+ const adapter = createPgsqlCompileOnlyAdapter();
88
+ const query = adapter.compile(compiled.ast, { model: db.model });
89
+ ```
90
+
91
+ Missing params fail compilation. `null` binds SQL `NULL`; `undefined`, `NaN`, and `Infinity` are rejected. The `@dbsp/core` `orm.nql` template tag builds on the same mechanism for `${value}` interpolation. See [Named Parameters and Template Binding](https://oorabona.github.io/db-semantic-planner/nql/#named-parameters-and-template-binding) for the full contract.
92
+
60
93
  ## Documentation
61
94
 
62
95
  - [Guides](https://oorabona.github.io/db-semantic-planner/guide/)
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { QueryIntent, CteQueryIntent, MutationIntent, SetOperationIntent } from '@dbsp/types';
2
- export { DeleteIntent, ExpressionIntent, IncludeIntent, InsertIntent, MutationIntent, OrderByIntent, QueryIntent, SelectAllIntent, SelectFieldsIntent, SelectIntent, SelectWithExpressionsIntent, UpdateIntent, UpsertIntent, WhereAndIntent, WhereComparisonIntent, WhereInIntent, WhereIntent, WhereLikeIntent, WhereNotIntent, WhereNullIntent, WhereOrIntent, WhereRangeIntent } from '@dbsp/types';
1
+ import { CompiledNqlQuery } from '@dbsp/types';
2
+ export { CompiledNqlQuery, DeleteIntent, ExpressionIntent, IncludeIntent, InsertIntent, MutationIntent, OrderByIntent, QueryIntent, SelectAllIntent, SelectFieldsIntent, SelectIntent, SelectWithExpressionsIntent, UpdateIntent, UpsertIntent, WhereAndIntent, WhereComparisonIntent, WhereInIntent, WhereIntent, WhereLikeIntent, WhereNotIntent, WhereNullIntent, WhereOrIntent, WhereRangeIntent } from '@dbsp/types';
3
3
  import * as chevrotain from 'chevrotain';
4
4
  import { Lexer, CstParser, CstNode, IToken } from 'chevrotain';
5
5
 
@@ -71,13 +71,14 @@ interface NqlOrderByClause {
71
71
  }
72
72
  interface NqlLimitClause {
73
73
  type: 'limit';
74
- count: number;
74
+ count: NqlLimitCount;
75
75
  relation?: string;
76
76
  }
77
77
  interface NqlOffsetClause {
78
78
  type: 'offset';
79
- count: number;
79
+ count: NqlLimitCount;
80
80
  }
81
+ type NqlLimitCount = number | NqlNamedParamExpr;
81
82
  /**
82
83
  * Capture mutation result into a variable (for chained mutations)
83
84
  * Used with `bind` keyword: `insert ... | bind result | ...`
@@ -135,7 +136,7 @@ interface NqlOrderItem {
135
136
  expression: NqlExpression;
136
137
  direction: 'asc' | 'desc';
137
138
  }
138
- type NqlExpression = NqlBinaryExpression | NqlUnaryExpression | NqlComparisonExpression | NqlRangeOpExpression | NqlInExpression | NqlAnyExpression | NqlBetweenExpression | NqlIsNullExpression | NqlExistsExpression | NqlRelationFilterExpression | NqlFunctionCall | NqlWindowExpression | NqlCaseExpression | NqlPathExpression | NqlJsonAccessExpression | NqlJsonComparisonExpression | NqlLiteral | NqlSubquery | NqlVariableRef;
139
+ type NqlExpression = NqlBinaryExpression | NqlUnaryExpression | NqlComparisonExpression | NqlRangeOpExpression | NqlInExpression | NqlAnyExpression | NqlBetweenExpression | NqlIsNullExpression | NqlExistsExpression | NqlRelationFilterExpression | NqlFunctionCall | NqlWindowExpression | NqlCaseExpression | NqlPathExpression | NqlJsonAccessExpression | NqlJsonComparisonExpression | NqlLiteral | NqlSubquery | NqlVariableRef | NqlNamedParamExpr;
139
140
  interface NqlBinaryExpression {
140
141
  type: 'binary';
141
142
  operator: 'and' | 'or' | '+' | '-' | '*' | '/' | '%';
@@ -162,7 +163,7 @@ interface NqlRangeOpExpression {
162
163
  operator: 'overlaps' | 'contains' | 'containedBy';
163
164
  left: NqlExpression;
164
165
  range?: NqlRangeLiteral;
165
- scalar?: NqlLiteral;
166
+ scalar?: NqlLiteral | NqlNamedParamExpr;
166
167
  }
167
168
  interface NqlInExpression {
168
169
  type: 'in';
@@ -297,6 +298,15 @@ interface NqlVariableRef {
297
298
  type: 'variable';
298
299
  name: string;
299
300
  }
301
+ /**
302
+ * Bound parameter reference (e.g. `:tenantId`).
303
+ * The leading colon is not part of `name`; compiler resolution uses
304
+ * `CompilerContext.params`.
305
+ */
306
+ interface NqlNamedParamExpr {
307
+ type: 'namedParam';
308
+ name: string;
309
+ }
300
310
  type NqlLiteral = NqlStringLiteral | NqlNumberLiteral | NqlBooleanLiteral | NqlNullLiteral | NqlDateRangeLiteral | NqlRangeLiteral;
301
311
  interface NqlStringLiteral {
302
312
  type: 'string';
@@ -354,7 +364,7 @@ interface NqlInsertFrom {
354
364
  /** WHERE clause to filter source rows */
355
365
  where?: NqlExpression | undefined;
356
366
  /** LIMIT clause to restrict rows */
357
- limit?: number | undefined;
367
+ limit?: NqlLimitCount | undefined;
358
368
  }
359
369
  interface NqlUpdate {
360
370
  type: 'update';
@@ -385,31 +395,15 @@ interface NqlUpsertFrom {
385
395
  source: string;
386
396
  columns?: string[] | undefined;
387
397
  where?: NqlExpression | undefined;
388
- limit?: number | undefined;
398
+ limit?: NqlLimitCount | undefined;
389
399
  }
390
400
  interface NqlAssignment {
391
401
  column: string;
392
402
  value: NqlExpression;
393
403
  }
394
404
 
395
- interface CompileResult {
396
- readonly query?: QueryIntent;
397
- /** CTE query (WITH clause): wraps outer QueryIntent in CteQueryIntent */
398
- readonly cteQuery?: CteQueryIntent;
399
- readonly mutation?: MutationIntent;
400
- readonly returning?: readonly string[];
401
- /** Named bindings from `| bind X` clauses (CTE source queries) */
402
- readonly bindings?: ReadonlyMap<string, QueryIntent>;
403
- /**
404
- * Named mutation bindings from `mutation | select cols | bind X` clauses.
405
- * When a mutation has RETURNING and is bound, the original MutationIntent is stored here
406
- * so the adapter can compile it as a CTE: `WITH X AS (INSERT ... RETURNING cols) ...`
407
- * The corresponding synthetic QueryIntent is also stored in `bindings` for reference resolution.
408
- */
409
- readonly mutationBindings?: ReadonlyMap<string, MutationIntent>;
410
- /** Set operation (UNION/INTERSECT/EXCEPT) wrapping two queries */
411
- readonly setOperation?: SetOperationIntent;
412
- }
405
+ type CompileResult = CompiledNqlQuery;
406
+
413
407
  /**
414
408
  * Duck-type interface for schema-based column validation.
415
409
  * Loose coupling: ModelIR from @dbsp/core satisfies this shape without direct import.
@@ -512,6 +506,11 @@ interface NqlWarning {
512
506
  declare const allTokens: chevrotain.TokenType[];
513
507
  declare const NqlLexer: Lexer;
514
508
 
509
+ /**
510
+ * Tokenize NQL source with the public lexer.
511
+ */
512
+ declare function tokenize(input: string): ReturnType<typeof NqlLexer.tokenize>;
513
+
515
514
  /**
516
515
  * NQL Parser using Chevrotain CstParser
517
516
  */
@@ -580,13 +579,23 @@ declare class NqlParser extends CstParser {
580
579
  */
581
580
  private orderClause;
582
581
  /**
583
- * limit_clause = "limit" [ident_segment ("." ident_segment)*] NUMBER ;
582
+ * limit_clause = "limit" [ident_segment ("." ident_segment)*] (NUMBER | NAMED_PARAM) ;
584
583
  */
585
584
  private limitClause;
586
585
  /**
587
- * offset_clause = "offset" NUMBER ;
586
+ * offset_clause = "offset" (NUMBER | NAMED_PARAM) ;
588
587
  */
589
588
  private offsetClause;
589
+ /**
590
+ * numeric_value_atom = NUMBER | NAMED_PARAM ;
591
+ *
592
+ * Structural-position durability rule: value positions that consume scalar
593
+ * values may add `NamedParam` here and validate the resolved value in the
594
+ * compiler. Structural positions such as identifiers, directions, lock modes,
595
+ * and traversal depth hints must not consume this helper; use nqlRaw()/builder
596
+ * APIs for trusted dynamic structure.
597
+ */
598
+ private numericValueAtom;
590
599
  /**
591
600
  * lock_clause = lock_strength [ lock_wait_policy ] ;
592
601
  * lock_strength = "for update" | "for share" | "for no key update" | "for key share" ;
@@ -664,7 +673,7 @@ declare class NqlParser extends CstParser {
664
673
  */
665
674
  private rangeOp;
666
675
  /**
667
- * range_op_suffix = range_op (range_literal | literal) ;
676
+ * range_op_suffix = range_op (range_literal | literal | named_param_expr) ;
668
677
  * Example: overlaps [1,10) or contains (0,100) or contains 25
669
678
  * Note: contains can take a scalar value (e.g., contains 25 checks if range contains the value)
670
679
  */
@@ -747,9 +756,13 @@ declare class NqlParser extends CstParser {
747
756
  */
748
757
  private jsonAccessExpr;
749
758
  /**
750
- * primary_expr = literal | case_expr | path_expr | func_call | "(" expr ")" | "(" scalar_subquery ")" ;
759
+ * primary_expr = named_param_expr | literal | case_expr | path_expr | func_call | "(" expr ")" | "(" scalar_subquery ")" ;
751
760
  */
752
761
  private primaryExpr;
762
+ /**
763
+ * named_param_expr = NAMED_PARAM ;
764
+ */
765
+ private namedParamExpr;
753
766
  /**
754
767
  * Check if at start of scalar subquery (ident | ...)
755
768
  */
@@ -866,7 +879,7 @@ declare class NqlParser extends CstParser {
866
879
  */
867
880
  private insertStmt;
868
881
  /**
869
- * insert_from_stmt = "insert" "into" ident_segment "from" ident_segment [ "where" boolean_expr ] [ "limit" number ] ;
882
+ * insert_from_stmt = "insert" "into" ident_segment "from" ident_segment [ "where" boolean_expr ] [ "limit" numeric_value_atom ] ;
870
883
  * @example insert into archived_users from users where active = false limit 100
871
884
  */
872
885
  private insertFromStmt;
@@ -885,7 +898,7 @@ declare class NqlParser extends CstParser {
885
898
  */
886
899
  private upsertStmt;
887
900
  /**
888
- * upsert_from_stmt = "upsert" "into" ident_segment "on" ( "(" ident_list ")" | ident_segment ) "from" ident_segment [ "where" boolean_expr ] [ "limit" number ] ;
901
+ * upsert_from_stmt = "upsert" "into" ident_segment "on" ( "(" ident_list ")" | ident_segment ) "from" ident_segment [ "where" boolean_expr ] [ "limit" numeric_value_atom ] ;
889
902
  * @example upsert into authors on id from counts
890
903
  * @example upsert into authors on (id, email) from counts where active = true
891
904
  */
@@ -956,6 +969,7 @@ declare class NqlCstVisitor extends BaseCstVisitor {
956
969
  orderClause(ctx: CstContext): NqlClause;
957
970
  limitClause(ctx: CstContext): NqlClause;
958
971
  offsetClause(ctx: CstContext): NqlClause;
972
+ numericValueAtom(ctx: CstContext): NqlLimitCount;
959
973
  selectList(ctx: CstContext): NqlSelectItem[];
960
974
  selectItem(ctx: CstContext): NqlSelectItem;
961
975
  relationStarExpr(ctx: CstContext): NqlSelectItem;
@@ -982,6 +996,7 @@ declare class NqlCstVisitor extends BaseCstVisitor {
982
996
  unaryExpr(ctx: CstContext): NqlExpression;
983
997
  jsonAccessExpr(ctx: CstContext): NqlExpression;
984
998
  primaryExpr(ctx: CstContext): NqlExpression;
999
+ namedParamExpr(ctx: CstContext): NqlNamedParamExpr;
985
1000
  caseExpr(ctx: CstContext): NqlCaseExpression;
986
1001
  searchedCaseBody(_ctx: CstContext): void;
987
1002
  simpleCaseBody(_ctx: CstContext): void;
@@ -1056,4 +1071,4 @@ declare function parse(input: string, _options?: ParseOptions): ParseResult<NqlP
1056
1071
  declare function compile(input: string, schema: unknown, // ModelIR from @dbsp/core — satisfies ColumnValidatorSchema
1057
1072
  options?: ParseOptions, compilerOptions?: NqlCompilerOptions): ParseResult<CompileResult>;
1058
1073
 
1059
- export { type ColumnValidatorSchema, type CompileResult, type NqlAnyExpression, type NqlAssignment, type NqlBetweenExpression, type NqlBinaryExpression, type NqlBindClause, type NqlBooleanLiteral, type NqlCaseExpression, type NqlClause, type NqlComparisonExpression, NqlCompiler, type NqlCompilerOptions, NqlCstVisitor, type NqlCteItem, type NqlDateRangeLiteral, type NqlDelete, type NqlError, type NqlExistsExpression, type NqlExpression, type NqlFlatClause, type NqlFunctionCall, type NqlGroupByClause, type NqlInExpression, type NqlInsert, type NqlInsertFrom, type NqlIsNullExpression, type NqlJoinParam, type NqlJoinSpec, type NqlJsonAccessExpression, type NqlJsonComparisonExpression, NqlLexer, type NqlLimitClause, type NqlLiteral, type NqlLockClause, type NqlMutation, type NqlMutationClause, type NqlMutationPipeline, type NqlNullLiteral, type NqlNumberLiteral, type NqlOffsetClause, type NqlOrderByClause, type NqlOrderItem, type NqlParseError, NqlParser, type NqlPathExpression, type NqlProgram, type NqlQuery, type NqlRangeLiteral, type NqlRangeOpExpression, type NqlRelationFilterExpression, type NqlSelectClause, type NqlSelectExpression, type NqlSelectItem, type NqlSelectRelationStar, type NqlSelectStar, type NqlSemanticError, type NqlSetClause, type NqlStatement, type NqlStringLiteral, type NqlSubquery, type NqlUnaryExpression, type NqlUpdate, type NqlUpsert, type NqlUpsertFrom, type NqlVariableRef, type NqlWarning, type NqlWhereClause, type NqlWindowExpression, type NqlWithQuery, type ParseOptions, type ParseResult, allTokens, compile, createCompiler, cstToAst, nqlVisitor, parse, parseCst };
1074
+ export { type ColumnValidatorSchema, type CompileResult, type NqlAnyExpression, type NqlAssignment, type NqlBetweenExpression, type NqlBinaryExpression, type NqlBindClause, type NqlBooleanLiteral, type NqlCaseExpression, type NqlClause, type NqlComparisonExpression, NqlCompiler, type NqlCompilerOptions, NqlCstVisitor, type NqlCteItem, type NqlDateRangeLiteral, type NqlDelete, type NqlError, type NqlExistsExpression, type NqlExpression, type NqlFlatClause, type NqlFunctionCall, type NqlGroupByClause, type NqlInExpression, type NqlInsert, type NqlInsertFrom, type NqlIsNullExpression, type NqlJoinParam, type NqlJoinSpec, type NqlJsonAccessExpression, type NqlJsonComparisonExpression, NqlLexer, type NqlLimitClause, type NqlLimitCount, type NqlLiteral, type NqlLockClause, type NqlMutation, type NqlMutationClause, type NqlMutationPipeline, type NqlNamedParamExpr, type NqlNullLiteral, type NqlNumberLiteral, type NqlOffsetClause, type NqlOrderByClause, type NqlOrderItem, type NqlParseError, NqlParser, type NqlPathExpression, type NqlProgram, type NqlQuery, type NqlRangeLiteral, type NqlRangeOpExpression, type NqlRelationFilterExpression, type NqlSelectClause, type NqlSelectExpression, type NqlSelectItem, type NqlSelectRelationStar, type NqlSelectStar, type NqlSemanticError, type NqlSetClause, type NqlStatement, type NqlStringLiteral, type NqlSubquery, type NqlUnaryExpression, type NqlUpdate, type NqlUpsert, type NqlUpsertFrom, type NqlVariableRef, type NqlWarning, type NqlWhereClause, type NqlWindowExpression, type NqlWithQuery, type ParseOptions, type ParseResult, allTokens, compile, createCompiler, cstToAst, nqlVisitor, parse, parseCst, tokenize };