@dbsp/adapter-pgsql 1.2.1 → 1.4.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
@@ -42,6 +42,10 @@ const { sql, params } = orm.select('users').where(eq('active', true)).dump();
42
42
  // sql, params — no Pool needed
43
43
  ```
44
44
 
45
+ ### Direct NQL bundles
46
+
47
+ `adapter.compile(compiledNqlBundle, options)` trusts that direct `CompiledNqlQuery` bundles are well-formed and semantically validated by the NQL compiler. The adapter still enforces SQL safety for emitted binding names, including identifier validation and binding/table or binding/binding collision checks before emitting `WITH` CTEs.
48
+
45
49
  ## Key features
46
50
 
47
51
  - **Parameterized queries** — All user values use `$N` positional parameters; no SQL injection surface
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ExpressionRef, IndexInfo, TruncateOptions, VacuumOptions, AlterColumnOptions, CreateIndexOptions, DropIndexOptions, ModelIR as ModelIR$1 } from '@dbsp/core';
2
2
  export { normalizeSQL } from '@dbsp/core';
3
3
  import * as _dbsp_types from '@dbsp/types';
4
- import { ParamIntent, DialectCapabilities, ModelIR, DbCasing, ColumnIR, HierarchyIR, Adapter, AdapterLogger, AdapterCapabilities, PlanReport, CompiledNqlQuery, CompileOptions, CompiledQuery, CompileResultWithIncludes, SubqueryIncludeInfo, ExpressionIntent, InsertIntent, InsertFromIntent, UpdateIntent, BatchUpdateIntent, DeleteIntent, UpsertIntent, UpsertFromIntent, RecursivePlanReport, CteQueryIntent, SetOperationIntent, DumpMeta, Dump, AdapterStreamOptions, CompileOnlyAdapter, QueryIntent } from '@dbsp/types';
4
+ import { ParamIntent, DialectCapabilities, ModelIR, DbCasing, ColumnIR, HierarchyIR, WhereIntent, Adapter, AdapterLogger, AdapterCapabilities, PlanReport, CompiledNqlQuery, CompileOptions, CompiledQuery, CompileResultWithIncludes, SubqueryIncludeInfo, ExpressionIntent, InsertIntent, InsertFromIntent, UpdateIntent, BatchUpdateIntent, DeleteIntent, UpsertIntent, UpsertFromIntent, RecursivePlanReport, CteQueryIntent, SetOperationIntent, DumpMeta, Dump, AdapterStreamOptions, CompileOnlyAdapter, QueryIntent } from '@dbsp/types';
5
5
  import * as _pgsql_types from '@pgsql/types';
6
6
  import { Node, OnConflictClause, ParamRef } from '@pgsql/types';
7
7
  import { Pool, PoolClient } from 'pg';
@@ -89,6 +89,16 @@ declare const camelCaseNaming: CamelCaseNamingPlugin;
89
89
  */
90
90
  declare function getNamingPluginForDbCasing(casing: 'snake_case' | 'camelCase' | 'preserve'): NamingPlugin;
91
91
 
92
+ /**
93
+ * Internal registry of NQL `| bind name` CTE names visible to a compile pass.
94
+ *
95
+ * Binding names are query-local identifiers, not physical tables. When an active
96
+ * schema is present, real table FROM sources must be schema-qualified, while CTE
97
+ * binding names must remain unqualified.
98
+ */
99
+
100
+ type BindingNameRegistry = ReadonlySet<string>;
101
+
92
102
  /**
93
103
  * Simplified PlanDecision for the spike
94
104
  * (In production, import from @dbsp/core)
@@ -251,12 +261,15 @@ interface CompiledResult {
251
261
  interface CompilerOptions {
252
262
  readonly naming?: NamingPlugin;
253
263
  readonly schema?: string;
264
+ readonly dialectCapabilities?: DialectCapabilities;
254
265
  /** Default primary key column name convention (default: 'id') */
255
266
  readonly defaultPkColumnName?: string;
256
267
  /** Convention for deriving FK column names: (tableName, pkName) => fkColumnName */
257
268
  readonly deriveFkColumnName?: FkColumnDerivation;
258
269
  /** ModelIR for type-aware parameter casting in WHERE clauses */
259
270
  readonly model?: _dbsp_types.ModelIR;
271
+ /** Query-local CTE/binding names that must not be schema-qualified. */
272
+ readonly bindingNames?: BindingNameRegistry;
260
273
  }
261
274
  declare class PlanCompiler {
262
275
  private readonly naming;
@@ -264,6 +277,8 @@ declare class PlanCompiler {
264
277
  private readonly defaultPk;
265
278
  private readonly deriveFk;
266
279
  private readonly model;
280
+ private readonly dialectCapabilities;
281
+ private readonly bindingNames;
267
282
  /** Mutable state shared with extracted condition/value compilation functions */
268
283
  private state;
269
284
  /** Track root table for EXISTS FK correlation */
@@ -287,6 +302,7 @@ declare class PlanCompiler {
287
302
  */
288
303
  private usedJoinAliases;
289
304
  constructor(options?: CompilerOptions);
305
+ private childCompilerOptions;
290
306
  /** Build immutable context for handler-based WHERE compilation */
291
307
  private handlerCtx;
292
308
  private findAliasForLegacySourceTable;
@@ -330,6 +346,7 @@ declare class PlanCompiler {
330
346
  private createHandlerContext;
331
347
  /** Build a fresh HandlerCompilerState sharing the current parameter array. */
332
348
  private createHandlerState;
349
+ private schemaForRangeVar;
333
350
  private compileExpressionSubquery;
334
351
  private compileGenericNqlFunction;
335
352
  private compileNqlFunctionArg;
@@ -383,11 +400,21 @@ declare class PlanCompiler {
383
400
  * Returns undefined when neither expressionIntent nor column is present.
384
401
  */
385
402
  private compileOrderByDecision;
403
+ /**
404
+ * Compile a column reference that may use dotted 'relation.column' notation.
405
+ */
406
+ private compileRelationAwareColumnRef;
386
407
  /**
387
408
  * Compile a single groupBy decision into a ColumnRef AST node.
388
409
  * Supports dotted 'relation.column' notation for joined tables.
389
410
  */
390
411
  private compileGroupByDecision;
412
+ /**
413
+ * Compile a DISTINCT ON column into a ColumnRef AST node.
414
+ * Mirrors GROUP BY relation-alias resolution while preserving unqualified
415
+ * DISTINCT ON columns as unqualified references.
416
+ */
417
+ private compileDistinctOnColumn;
391
418
  /**
392
419
  * Assemble the final SelectStmt from all accumulated clause nodes.
393
420
  * Also handles the default SELECT *, CTEs, and row-level locking.
@@ -1044,6 +1071,8 @@ interface CompilerContext {
1044
1071
  readonly naming: NamingPlugin;
1045
1072
  /** Schema name for table qualification (optional) */
1046
1073
  readonly schema?: string;
1074
+ /** Dialect capabilities for adapter-layer SQL surface gates */
1075
+ readonly dialectCapabilities?: DialectCapabilities;
1047
1076
  /** Root table name for the query */
1048
1077
  readonly rootTable: string;
1049
1078
  /** Current table alias (for JOINs) */
@@ -1060,6 +1089,8 @@ interface CompilerContext {
1060
1089
  readonly deriveFkColumnName?: FkColumnDerivation;
1061
1090
  /** Alias of the outer (parent) query — used for FieldRef scope:'outer' resolution in EXISTS subqueries */
1062
1091
  readonly outerAlias?: string;
1092
+ /** Query-local CTE/binding names that must not be schema-qualified. */
1093
+ readonly bindingNames?: BindingNameRegistry;
1063
1094
  /**
1064
1095
  * Optional callback to compile a QueryIntent into an AST Node (SubLink subselect).
1065
1096
  * Set by PlanCompiler when compiling selectCustomExpression — enables SubqueryExpressionIntent
@@ -1413,6 +1444,12 @@ interface UpsertConfig {
1413
1444
  conflictAction: ConflictAction;
1414
1445
  /** Columns to update on conflict (for 'update' action) */
1415
1446
  updateColumns?: string[];
1447
+ /** Optional WHERE clause for ON CONFLICT DO UPDATE */
1448
+ actionWhere?: Decision[];
1449
+ /** Optional direct WHERE intent for ON CONFLICT DO UPDATE */
1450
+ actionWhereIntent?: WhereIntent;
1451
+ /** Compile the direct action WHERE intent using the caller's WHERE compiler */
1452
+ compileActionWhere?: (where: WhereIntent, state: CompilerState) => Node;
1416
1453
  /** Use EXCLUDED.column for update values (default: true) */
1417
1454
  useExcluded?: boolean;
1418
1455
  /** Columns to return (RETURNING clause) */
@@ -1616,6 +1653,7 @@ declare class PgsqlAdapter<DB = unknown> implements Adapter<DB> {
1616
1653
  private cloneOptions;
1617
1654
  private buildCompileDeps;
1618
1655
  private requireNqlCompileModel;
1656
+ private assertNqlBindingNamesDisjointFromTables;
1619
1657
  private compileNqlMutation;
1620
1658
  private compileNqlBundleLeaf;
1621
1659
  private compileNqlBundle;
@@ -1637,6 +1675,11 @@ declare class PgsqlAdapter<DB = unknown> implements Adapter<DB> {
1637
1675
  getPoolInstance(): Pool;
1638
1676
  /**
1639
1677
  * Compile a plan to executable SQL.
1678
+ *
1679
+ * When passed a direct `CompiledNqlQuery`, the adapter trusts that the bundle
1680
+ * has already been semantically validated by the NQL compiler. This method
1681
+ * still performs adapter-owned SQL safety checks for emitted binding names
1682
+ * before CTE emission.
1640
1683
  */
1641
1684
  compile<T = unknown>(plan: PlanReport | CompiledNqlQuery, options?: CompileOptions): CompiledQuery<T>;
1642
1685
  /**
@@ -1720,6 +1763,7 @@ declare class PgsqlAdapter<DB = unknown> implements Adapter<DB> {
1720
1763
  * Compile a set operation (UNION / INTERSECT / EXCEPT) to SQL.
1721
1764
  */
1722
1765
  compileSetOperation(intent: SetOperationIntent, model: ModelIR, options?: CompileOptions): CompiledQuery;
1766
+ private compileSetOperationWithBindings;
1723
1767
  /**
1724
1768
  * Create a dump for observability.
1725
1769
  */