@warlock.js/cascade 4.4.0 → 4.6.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +29 -3
  2. package/cjs/index.cjs +933 -90
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/contracts/database-driver.contract.d.mts +27 -4
  5. package/esm/contracts/database-driver.contract.d.mts.map +1 -1
  6. package/esm/contracts/database-id-generator.contract.d.mts +38 -0
  7. package/esm/contracts/database-id-generator.contract.d.mts.map +1 -1
  8. package/esm/contracts/index.d.mts +1 -1
  9. package/esm/contracts/query-builder.contract.d.mts +27 -0
  10. package/esm/contracts/query-builder.contract.d.mts.map +1 -1
  11. package/esm/data-source/data-source.d.mts +21 -1
  12. package/esm/data-source/data-source.d.mts.map +1 -1
  13. package/esm/data-source/data-source.mjs +22 -0
  14. package/esm/data-source/data-source.mjs.map +1 -1
  15. package/esm/drivers/mongodb/mongodb-driver.d.mts +2 -2
  16. package/esm/drivers/mongodb/mongodb-driver.d.mts.map +1 -1
  17. package/esm/drivers/mongodb/mongodb-driver.mjs.map +1 -1
  18. package/esm/drivers/mongodb/mongodb-id-generator.d.mts +98 -48
  19. package/esm/drivers/mongodb/mongodb-id-generator.d.mts.map +1 -1
  20. package/esm/drivers/mongodb/mongodb-id-generator.mjs +153 -59
  21. package/esm/drivers/mongodb/mongodb-id-generator.mjs.map +1 -1
  22. package/esm/drivers/mongodb/mongodb-query-builder.d.mts +25 -0
  23. package/esm/drivers/mongodb/mongodb-query-builder.d.mts.map +1 -1
  24. package/esm/drivers/mongodb/mongodb-query-builder.mjs +32 -0
  25. package/esm/drivers/mongodb/mongodb-query-builder.mjs.map +1 -1
  26. package/esm/drivers/mongodb/mongodb-query-parser.d.mts +36 -0
  27. package/esm/drivers/mongodb/mongodb-query-parser.d.mts.map +1 -1
  28. package/esm/drivers/mongodb/mongodb-query-parser.mjs +80 -1
  29. package/esm/drivers/mongodb/mongodb-query-parser.mjs.map +1 -1
  30. package/esm/drivers/postgres/postgres-dialect.d.mts +32 -4
  31. package/esm/drivers/postgres/postgres-dialect.d.mts.map +1 -1
  32. package/esm/drivers/postgres/postgres-dialect.mjs +57 -4
  33. package/esm/drivers/postgres/postgres-dialect.mjs.map +1 -1
  34. package/esm/drivers/postgres/postgres-driver.d.mts +42 -0
  35. package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
  36. package/esm/drivers/postgres/postgres-driver.mjs +55 -6
  37. package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
  38. package/esm/drivers/postgres/postgres-query-builder.d.mts +32 -0
  39. package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
  40. package/esm/drivers/postgres/postgres-query-builder.mjs +47 -1
  41. package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
  42. package/esm/drivers/postgres/postgres-query-parser.d.mts +13 -2
  43. package/esm/drivers/postgres/postgres-query-parser.d.mts.map +1 -1
  44. package/esm/drivers/postgres/postgres-query-parser.mjs +21 -4
  45. package/esm/drivers/postgres/postgres-query-parser.mjs.map +1 -1
  46. package/esm/drivers/postgres/types.d.mts +15 -0
  47. package/esm/drivers/postgres/types.d.mts.map +1 -1
  48. package/esm/drivers/sql/sql-dialect.contract.d.mts +20 -0
  49. package/esm/drivers/sql/sql-dialect.contract.d.mts.map +1 -1
  50. package/esm/expressions/aggregate-expressions.d.mts +71 -34
  51. package/esm/expressions/aggregate-expressions.d.mts.map +1 -1
  52. package/esm/expressions/aggregate-expressions.mjs +80 -7
  53. package/esm/expressions/aggregate-expressions.mjs.map +1 -1
  54. package/esm/expressions/column-expressions.d.mts +193 -0
  55. package/esm/expressions/column-expressions.d.mts.map +1 -0
  56. package/esm/expressions/column-expressions.mjs +152 -0
  57. package/esm/expressions/column-expressions.mjs.map +1 -0
  58. package/esm/index.d.mts +3 -2
  59. package/esm/index.mjs +2 -1
  60. package/esm/model/methods/write-methods.d.mts +29 -0
  61. package/esm/model/methods/write-methods.d.mts.map +1 -0
  62. package/esm/model/methods/write-methods.mjs +164 -2
  63. package/esm/model/methods/write-methods.mjs.map +1 -1
  64. package/esm/model/model.d.mts +64 -3
  65. package/esm/model/model.d.mts.map +1 -1
  66. package/esm/model/model.mjs +65 -3
  67. package/esm/model/model.mjs.map +1 -1
  68. package/esm/writer/database-writer.d.mts.map +1 -1
  69. package/esm/writer/database-writer.mjs +4 -3
  70. package/esm/writer/database-writer.mjs.map +1 -1
  71. package/llms-full.txt +106 -7
  72. package/llms.txt +3 -3
  73. package/package.json +4 -4
  74. package/skills/README.md +3 -3
  75. package/skills/aggregate-data/SKILL.md +43 -3
  76. package/skills/manage-transactions/SKILL.md +40 -1
  77. package/skills/perform-atomic-ops/SKILL.md +23 -3
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-query-parser.mjs","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/postgres/postgres-query-parser.ts"],"sourcesContent":["/**\n * PostgreSQL Query Parser\n *\n * Translates Cascade query operations into PostgreSQL SQL queries.\n * Mirrors the MongoQueryParser pattern but generates SQL instead\n * of MongoDB aggregation pipelines.\n *\n * @module cascade/drivers/postgres\n */\n\nimport type {\n DriverQuery,\n JoinOptions,\n WhereOperator,\n} from \"../../contracts/query-builder.contract\";\nimport type { SqlDialectContract } from \"../sql/sql-dialect.contract\";\n\nimport { PostgresDialect } from \"./postgres-dialect\";\n\n/**\n * Operation types supported by the query parser.\n */\nexport type PostgresOperationType =\n // WHERE operations\n | \"where\"\n | \"orWhere\"\n | \"whereRaw\"\n | \"orWhereRaw\"\n | \"whereIn\"\n | \"whereNotIn\"\n | \"whereNull\"\n | \"whereNotNull\"\n | \"whereBetween\"\n | \"whereNotBetween\"\n | \"whereLike\"\n | \"whereNotLike\"\n | \"whereColumn\"\n | \"orWhereColumn\"\n | \"whereExists\"\n | \"whereNotExists\"\n | \"whereDate\"\n | \"whereDateBefore\"\n | \"whereDateAfter\"\n | \"whereDateBetween\"\n | \"whereJsonContains\"\n | \"whereJsonDoesntContain\"\n | \"whereFullText\"\n // SELECT operations\n | \"select\"\n | \"selectRaw\"\n | \"deselect\"\n // JOIN operations\n | \"join\"\n | \"leftJoin\"\n | \"rightJoin\"\n | \"innerJoin\"\n | \"fullJoin\"\n | \"crossJoin\"\n | \"joinRaw\"\n // ORDER operations\n | \"orderBy\"\n | \"orderByRaw\"\n // GROUP operations\n | \"groupBy\"\n | \"having\"\n | \"havingRaw\"\n // LIMIT operations\n | \"limit\"\n | \"offset\"\n // RELATION operations\n | \"has\"\n | \"whereHas\"\n | \"doesntHave\"\n | \"whereDoesntHave\"\n // joinWith operations\n | \"selectRelatedColumns\"\n // Other\n | \"distinct\";\n\n/**\n * Internal operation representation.\n */\nexport type PostgresParserOperation = {\n /** Operation type */\n readonly type: PostgresOperationType;\n /** Operation data/parameters */\n readonly data: Record<string, unknown>;\n};\n\n/**\n * Parser configuration options.\n */\nexport type PostgresParserOptions = {\n /** Target table name */\n readonly table: string;\n /** Table alias (optional) */\n readonly alias?: string;\n /** Operations to parse */\n readonly operations: PostgresParserOperation[];\n /** SQL dialect for syntax generation */\n readonly dialect?: SqlDialectContract;\n /** Factory for creating sub-parsers (for nested queries) */\n readonly createSubParser?: (table: string) => PostgresQueryParser;\n};\n\n/**\n * PostgreSQL Query Parser.\n *\n * Converts a list of query operations into a SQL query string with parameters.\n * Handles SELECT, WHERE, JOIN, ORDER BY, GROUP BY, LIMIT/OFFSET clauses.\n *\n * @example\n * ```typescript\n * const parser = new PostgresQueryParser({\n * table: 'users',\n * operations: [\n * { type: 'where', data: { field: 'name', operator: '=', value: 'Alice' } },\n * { type: 'orderBy', data: { field: 'createdAt', direction: 'desc' } },\n * { type: 'limit', data: { value: 10 } }\n * ]\n * });\n *\n * const { sql, params } = parser.parse();\n * // sql: 'SELECT * FROM \"users\" WHERE \"name\" = $1 ORDER BY \"createdAt\" DESC LIMIT 10'\n * // params: ['Alice']\n * ```\n */\nexport class PostgresQueryParser {\n /**\n * Target table name.\n */\n private readonly table: string;\n\n /**\n * Table alias.\n */\n private readonly alias?: string;\n\n /**\n * Operations to process.\n */\n private readonly operations: PostgresParserOperation[];\n\n /**\n * SQL dialect for syntax.\n */\n private readonly dialect: SqlDialectContract;\n\n /**\n * Current parameter index (1-based for PostgreSQL).\n */\n private paramIndex = 1;\n\n /**\n * Collected parameters.\n */\n private readonly params: unknown[] = [];\n\n /**\n * SELECT columns.\n */\n public selectColumns: string[] = [];\n\n /**\n * Deselected (excluded) columns.\n */\n private deselectColumns: string[] = [];\n\n /**\n * Raw SELECT expressions.\n */\n private selectRaw: string[] = [];\n\n /**\n * WHERE clauses.\n */\n public whereClauses: string[] = [];\n\n /**\n * JOIN clauses.\n */\n private joinClauses: string[] = [];\n\n /**\n * ORDER BY clauses.\n */\n public orderClauses: string[] = [];\n\n /**\n * GROUP BY columns.\n */\n private groupColumns: string[] = [];\n\n /**\n * HAVING clauses.\n */\n private havingClauses: string[] = [];\n\n /**\n * LIMIT value.\n */\n public limitValue?: number;\n\n /**\n * OFFSET value.\n */\n public offsetValue?: number;\n\n /**\n * DISTINCT flag.\n */\n private isDistinct = false;\n\n /**\n * Whether the query has any JOIN operations (pre-scanned before processing).\n * Used by qualifyColumn() to decide whether to prefix columns with the main table.\n */\n private hasJoins = false;\n\n /**\n * Tracked joined tables (for table reference detection).\n */\n private readonly joinedTables = new Set<string>();\n\n /**\n * Create a new query parser.\n *\n * @param options - Parser configuration\n */\n public constructor(options: PostgresParserOptions) {\n this.table = options.table;\n this.alias = options.alias;\n this.operations = options.operations;\n this.dialect = options.dialect ?? new PostgresDialect();\n }\n\n /**\n * Parse all operations and build the SQL query.\n *\n * @returns DriverQuery with `query` (SQL string) and `bindings` (parameter values)\n */\n public parse(): DriverQuery {\n // Pre-scan for any join operations so qualifyColumn() can prefix columns\n // correctly even when WHERE clauses appear before JOINs in the operations list.\n const JOIN_TYPES = new Set([\n \"join\",\n \"leftJoin\",\n \"rightJoin\",\n \"innerJoin\",\n \"fullJoin\",\n \"crossJoin\",\n \"joinRaw\",\n ]);\n\n this.hasJoins = false;\n\n // First pass: locate all joins and populate joinedTables for accurate JSON path detection\n for (const operation of this.operations) {\n if (JOIN_TYPES.has(operation.type)) {\n this.hasJoins = true;\n const data = operation.data as any;\n const joinTable = data.table as string;\n const alias = data.alias as string;\n\n if (joinTable) this.joinedTables.add(joinTable);\n if (alias) this.joinedTables.add(alias);\n }\n }\n\n // Process each operation\n for (const operation of this.operations) {\n this.processOperation(operation);\n }\n\n // Build the final SQL query\n const query = this.buildSql();\n\n return { query, bindings: this.params };\n }\n\n /**\n * Get a formatted string representation of the query.\n *\n * @returns Formatted SQL with bindings\n */\n public toPrettyString(): string {\n const { query = \"\", bindings } = this.parse();\n return `${query}\\n-- Bindings: ${JSON.stringify(bindings ?? [])}`;\n }\n\n /**\n * Process a single operation.\n *\n * @param operation - The operation to process\n */\n private processOperation(operation: PostgresParserOperation): void {\n const { type, data } = operation;\n\n switch (type) {\n // WHERE operations\n case \"where\":\n this.processWhere(data, \"AND\");\n break;\n case \"orWhere\":\n this.processWhere(data, \"OR\");\n break;\n case \"whereRaw\":\n this.processWhereRaw(data, \"AND\");\n break;\n case \"orWhereRaw\":\n this.processWhereRaw(data, \"OR\");\n break;\n case \"whereIn\":\n this.processWhereIn(data, false);\n break;\n case \"whereNotIn\":\n this.processWhereIn(data, true);\n break;\n case \"whereNull\":\n this.processWhereNull(data, false);\n break;\n case \"whereNotNull\":\n this.processWhereNull(data, true);\n break;\n case \"whereBetween\":\n this.processWhereBetween(data, false);\n break;\n case \"whereNotBetween\":\n this.processWhereBetween(data, true);\n break;\n case \"whereLike\":\n this.processWhereLike(data, false);\n break;\n case \"whereNotLike\":\n this.processWhereLike(data, true);\n break;\n case \"whereColumn\":\n this.processWhereColumn(data, \"AND\");\n break;\n case \"orWhereColumn\":\n this.processWhereColumn(data, \"OR\");\n break;\n case \"whereJsonContains\":\n this.processWhereJsonContains(data, false);\n break;\n case \"whereJsonDoesntContain\":\n this.processWhereJsonContains(data, true);\n break;\n case \"whereFullText\":\n this.processWhereFullText(data);\n break;\n\n // SELECT operations\n case \"select\":\n this.processSelect(data);\n break;\n case \"selectRaw\":\n this.processSelectRaw(data);\n break;\n case \"deselect\":\n this.processDeselect(data);\n break;\n\n // JOIN operations\n case \"join\":\n case \"innerJoin\":\n this.processJoin(data, \"INNER\");\n break;\n case \"leftJoin\":\n this.processJoin(data, \"LEFT\");\n break;\n case \"rightJoin\":\n this.processJoin(data, \"RIGHT\");\n break;\n case \"fullJoin\":\n this.processJoin(data, \"FULL OUTER\");\n break;\n case \"crossJoin\":\n this.processCrossJoin(data);\n break;\n case \"joinRaw\":\n this.processJoinRaw(data);\n break;\n\n // ORDER operations\n case \"orderBy\":\n this.processOrderBy(data);\n break;\n case \"orderByRaw\":\n this.processOrderByRaw(data);\n break;\n\n // GROUP operations\n case \"groupBy\":\n this.processGroupBy(data);\n break;\n case \"having\":\n this.processHaving(data);\n break;\n case \"havingRaw\":\n this.processHavingRaw(data);\n break;\n\n // LIMIT operations\n case \"limit\":\n this.limitValue = data.value as number;\n break;\n case \"offset\":\n this.offsetValue = data.value as number;\n break;\n\n // Other\n case \"distinct\":\n this.isDistinct = true;\n break;\n\n // joinWith select related columns\n case \"selectRelatedColumns\":\n this.processSelectRelatedColumns(data);\n break;\n\n default:\n // Unknown operation - ignore or throw\n break;\n }\n }\n\n /**\n * Build the final SQL query from collected clauses.\n *\n * @returns Complete SQL query string\n */\n private buildSql(): string {\n const parts: string[] = [];\n\n // SELECT clause\n parts.push(this.buildSelectClause());\n\n // FROM clause\n const quotedTable = this.dialect.quoteIdentifier(this.table);\n const fromClause = this.alias\n ? `FROM ${quotedTable} AS ${this.dialect.quoteIdentifier(this.alias)}`\n : `FROM ${quotedTable}`;\n parts.push(fromClause);\n\n // JOIN clauses\n if (this.joinClauses.length > 0) {\n parts.push(this.joinClauses.join(\" \"));\n }\n\n // WHERE clause\n if (this.whereClauses.length > 0) {\n parts.push(`WHERE ${this.whereClauses.join(\" \")}`);\n }\n\n // GROUP BY clause\n if (this.groupColumns.length > 0) {\n const quotedCols = this.groupColumns.map((c) => this.dialect.quoteIdentifier(c));\n parts.push(`GROUP BY ${quotedCols.join(\", \")}`);\n }\n\n // HAVING clause\n if (this.havingClauses.length > 0) {\n parts.push(`HAVING ${this.havingClauses.join(\" AND \")}`);\n }\n\n // ORDER BY clause\n if (this.orderClauses.length > 0) {\n parts.push(`ORDER BY ${this.orderClauses.join(\", \")}`);\n }\n\n // LIMIT/OFFSET\n const limitOffset = this.dialect.limitOffset(this.limitValue, this.offsetValue);\n if (limitOffset) {\n parts.push(limitOffset);\n }\n\n return parts.join(\" \");\n }\n\n /**\n * Build the SELECT clause.\n *\n * @returns SELECT clause string\n */\n private buildSelectClause(): string {\n const distinct = this.isDistinct ? \"DISTINCT \" : \"\";\n\n // If no specific columns, select all — qualify with main table when joins present\n if (this.selectColumns.length === 0 && this.selectRaw.length === 0) {\n return this.hasJoins\n ? `SELECT ${distinct}${this.dialect.quoteIdentifier(this.table)}.*`\n : `SELECT ${distinct}*`;\n }\n\n const columns: string[] = [];\n\n // Add selected columns — prefix with main table when joins present to avoid ambiguity\n for (const col of this.selectColumns) {\n if (!this.deselectColumns.includes(col)) {\n columns.push(this.parseColumnIdentifier(col, this.table, this.alias));\n }\n }\n\n // Add raw expressions\n columns.push(...this.selectRaw);\n\n return `SELECT ${distinct}${columns.join(\", \")}`;\n }\n\n /**\n * Add a placeholder and parameter.\n *\n * @param value - Parameter value\n * @returns Placeholder string ($1, $2, etc.)\n */\n private addParam(value: unknown): string {\n this.params.push(value);\n return this.dialect.placeholder(this.paramIndex++);\n }\n\n /**\n * Absorb a sub-parser's params into this parser, returning a rewriter\n * function that translates the sub-parser's `$N` placeholders into freshly\n * numbered placeholders in this parser's namespace.\n *\n * Required whenever sub-parser SQL fragments (e.g. WHERE clauses produced\n * for a `joinWith` constraint) are spliced into the outer query — without\n * the rewrite the embedded `$N` references point at parameters this\n * parser has no knowledge of, leaving them dangling in the final SQL.\n *\n * The sub-parser's `params[i]` corresponds to its `$(i+1)` placeholder\n * (its `paramIndex` always starts at 1), so we map sequentially.\n *\n * @example\n * const renumber = this.absorbSubParserParams(subParser);\n * const fragment = renumber(subParser.whereClauses.join(\" \"));\n */\n private absorbSubParserParams(subParser: PostgresQueryParser): (sql: string) => string {\n if (subParser.params.length === 0) {\n return (sql) => sql;\n }\n\n const oldToNew = new Map<string, string>();\n\n for (let index = 0; index < subParser.params.length; index++) {\n const newPlaceholder = this.addParam(subParser.params[index]);\n oldToNew.set(`$${index + 1}`, newPlaceholder);\n }\n\n return (sql) => sql.replace(/\\$\\d+/g, (match) => oldToNew.get(match) ?? match);\n }\n\n /**\n * Process a basic WHERE operation.\n *\n * Delegates to specialised processors for operators that require more than a\n * single placeholder (between, in, like-variants, exists, etc.).\n */\n private processWhere(data: Record<string, unknown>, boolean: \"AND\" | \"OR\"): void {\n const field = data.field as string;\n const operator = (data.operator as WhereOperator) ?? \"=\";\n const value = data.value;\n\n // Delegate to specialised processors for operators that need it\n switch (operator) {\n case \"between\":\n return this.processWhereBetween({ field, range: value as [unknown, unknown] }, false);\n case \"notBetween\":\n return this.processWhereBetween({ field, range: value as [unknown, unknown] }, true);\n case \"in\":\n return this.processWhereIn({ field, values: value as unknown[] }, false);\n case \"notIn\":\n return this.processWhereIn({ field, values: value as unknown[] }, true);\n case \"like\":\n case \"ilike\":\n case \"startsWith\":\n case \"endsWith\":\n return this.processWhereLike({ field, pattern: value as string }, false);\n case \"notLike\":\n case \"notStartsWith\":\n case \"notEndsWith\":\n return this.processWhereLike({ field, pattern: value as string }, true);\n case \"exists\":\n // EXISTS expects value to be a raw sub-query string\n return this.addWhereClause(`EXISTS (${value})`, boolean);\n }\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n\n if (value === null) {\n const nullOperator = operator === \"!=\" ? \"IS NOT NULL\" : \"IS NULL\";\n return this.addWhereClause(`${quotedField} ${nullOperator}`, boolean);\n }\n\n // Simple single-value operator — fall through to generic path\n const placeholder = this.addParam(value);\n this.addWhereClause(`${quotedField} ${this.mapOperator(operator)} ${placeholder}`, boolean);\n }\n\n /**\n * Process a raw WHERE operation.\n */\n private processWhereRaw(data: Record<string, unknown>, boolean: \"AND\" | \"OR\"): void {\n const expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n // Replace ? placeholders with $n\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n\n this.addWhereClause(processed, boolean);\n }\n\n /**\n * Process WHERE IN / NOT IN.\n */\n private processWhereIn(data: Record<string, unknown>, negate: boolean): void {\n const field = data.field as string;\n const values = data.values as unknown[];\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const operator = negate ? \"!= ALL\" : \"= ANY\";\n const placeholder = this.addParam(values);\n\n this.addWhereClause(`${quotedField} ${operator}(${placeholder})`, \"AND\");\n }\n\n /**\n * Process WHERE NULL / NOT NULL.\n */\n private processWhereNull(data: Record<string, unknown>, negate: boolean): void {\n const field = data.field as string;\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const clause = negate ? `${quotedField} IS NOT NULL` : `${quotedField} IS NULL`;\n this.addWhereClause(clause, \"AND\");\n }\n\n /**\n * Process WHERE BETWEEN / NOT BETWEEN.\n */\n private processWhereBetween(data: Record<string, unknown>, negate: boolean): void {\n const field = data.field as string;\n const range = data.range as [unknown, unknown];\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const placeholder1 = this.addParam(range[0]);\n const placeholder2 = this.addParam(range[1]);\n const keyword = negate ? \"NOT BETWEEN\" : \"BETWEEN\";\n\n this.addWhereClause(`${quotedField} ${keyword} ${placeholder1} AND ${placeholder2}`, \"AND\");\n }\n\n /**\n * Process WHERE LIKE / NOT LIKE.\n */\n private processWhereLike(data: Record<string, unknown>, negate: boolean): void {\n const field = data.field as string;\n const pattern = data.pattern as string;\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const { operator } = this.dialect.likePattern(pattern, true);\n const placeholder = this.addParam(pattern);\n const keyword = negate ? `NOT ${operator}` : operator;\n\n this.addWhereClause(`${quotedField} ${keyword} ${placeholder}`, \"AND\");\n }\n\n /**\n * Process WHERE column comparison.\n */\n private processWhereColumn(data: Record<string, unknown>, boolean: \"AND\" | \"OR\"): void {\n const first = data.first as string;\n const operator = (data.operator as string) ?? \"=\";\n const second = data.second as string;\n\n // Both sides may need qualification if unambiguous table is unclear\n const quotedFirst = this.parseColumnIdentifier(first, this.table, this.alias);\n const quotedSecond = this.parseColumnIdentifier(second, this.table, this.alias);\n\n this.addWhereClause(`${quotedFirst} ${operator} ${quotedSecond}`, boolean);\n }\n\n /**\n * Process WHERE JSON contains.\n */\n private processWhereJsonContains(data: Record<string, unknown>, negate: boolean): void {\n const path = data.path as string;\n const value = data.value;\n\n const quotedPath = this.parseColumnIdentifier(path, this.table, this.alias);\n const jsonValue = JSON.stringify(value);\n const operator = negate ? \"NOT @>\" : \"@>\";\n\n this.addWhereClause(`${quotedPath} ${operator} '${jsonValue}'::jsonb`, \"AND\");\n }\n\n /**\n * Process full-text search WHERE.\n */\n private processWhereFullText(data: Record<string, unknown>): void {\n const fields = data.fields as string[];\n const query = data.query as string;\n\n // Build tsvector from fields\n const tsVectors = fields\n .map((f) => `to_tsvector('english', ${this.dialect.quoteIdentifier(f)})`)\n .join(\" || \");\n\n const placeholder = this.addParam(query);\n this.addWhereClause(`(${tsVectors}) @@ plainto_tsquery('english', ${placeholder})`, \"AND\");\n }\n\n /**\n * Process SELECT operation.\n */\n private processSelect(data: Record<string, unknown>): void {\n const fields = data.fields as string[] | Record<string, string>;\n\n if (Array.isArray(fields)) {\n this.selectColumns.push(...fields);\n } else {\n // Handle aliases: { field: 'alias' }\n for (const [field, alias] of Object.entries(fields)) {\n const quotedField = this.dialect.quoteIdentifier(field);\n const quotedAlias = this.dialect.quoteIdentifier(alias);\n this.selectRaw.push(`${quotedField} AS ${quotedAlias}`);\n }\n }\n }\n\n /**\n * Process raw SELECT expression.\n */\n private processSelectRaw(data: Record<string, unknown>): void {\n const expression = data.expression as string | Record<string, unknown>;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n if (typeof expression === \"string\") {\n // Replace ? placeholders with $n positional params (same as processWhereRaw)\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n this.selectRaw.push(processed);\n } else {\n // Handle object expressions (for compatibility)\n for (const [alias, expr] of Object.entries(expression)) {\n this.selectRaw.push(`${expr} AS ${this.dialect.quoteIdentifier(alias)}`);\n }\n }\n }\n\n /**\n * Process DESELECT operation.\n */\n private processDeselect(data: Record<string, unknown>): void {\n const fields = data.fields as string[];\n this.deselectColumns.push(...fields);\n }\n\n /**\n * Process SELECT for related columns (joinWith).\n *\n * - hasOne / belongsTo → LEFT JOIN + row_to_json (single object)\n * - hasMany → correlated subquery with json_agg (array, no row explosion)\n *\n * @example hasMany correlated subquery:\n * (SELECT json_agg(row_to_json(a.*))\n * FROM \"chat_message_actions\" a\n * WHERE a.\"chat_message_id\" = \"chat_messages\".\"id\") AS \"actions\"\n *\n * @example hasOne/belongsTo row_to_json:\n * row_to_json(\"organizationAiModel\".*) AS \"organizationAiModel\"\n */\n private processSelectRelatedColumns(data: Record<string, unknown>): void {\n const alias = data.alias as string;\n const select = data.select as string[] | undefined;\n const relationType = data.type as string | undefined;\n const constraintOps = data.constraintOps as PostgresParserOperation[] | undefined;\n const quotedAlias = this.dialect.quoteIdentifier(alias);\n const quotedTable = this.dialect.quoteIdentifier(this.table);\n\n const hasExplicitSelect = this.selectColumns.length > 0;\n if (!hasExplicitSelect && !this.selectRaw.includes(`${quotedTable}.*`)) {\n this.selectRaw.unshift(`${quotedTable}.*`);\n }\n\n if (relationType === \"hasMany\") {\n // Correlated subquery — no JOIN, no row explosion, returns a JSON array.\n const relatedTable = data.table as string;\n const foreignKey = data.foreignKey as string;\n const localKey = data.localKey as string;\n const quotedRelatedTable = this.dialect.quoteIdentifier(relatedTable);\n const quotedForeignKey = this.dialect.quoteIdentifier(foreignKey);\n const quotedLocalKey = this.dialect.quoteIdentifier(localKey);\n const quotedMainTable = this.dialect.quoteIdentifier(this.table);\n\n let innerSelect: string;\n if (select && select.length > 0) {\n // Pick specific columns: json_agg(json_build_object('id', a.\"id\", ...))\n const fields = select\n .map((col) => `'${col}', a.${this.dialect.quoteIdentifier(col)}`)\n .join(\", \");\n innerSelect = `json_agg(json_build_object(${fields}))`;\n } else {\n innerSelect = `json_agg(row_to_json(a.*))`;\n }\n\n // Build the base FK condition\n const fkCondition = `a.${quotedForeignKey} = ${quotedMainTable}.${quotedLocalKey}`;\n\n // Merge constraint ops (where / order / limit) from a sub-parser.\n // The sub-parser numbers its own placeholders starting at $1; we must\n // absorb its params into this parser and renumber the embedded SQL,\n // otherwise $N references in the merged clauses dangle.\n let extraWhere = \"\";\n let orderBy = \"\";\n let limitClause = \"\";\n\n if (constraintOps && constraintOps.length > 0) {\n const subParser = new PostgresQueryParser({\n table: relatedTable,\n alias: \"a\",\n operations: constraintOps,\n });\n subParser.parse();\n\n const renumber = this.absorbSubParserParams(subParser);\n\n if (subParser.whereClauses.length > 0) {\n extraWhere = ` AND ${renumber(subParser.whereClauses.join(\" \"))}`;\n }\n if (subParser.orderClauses.length > 0) {\n orderBy = ` ORDER BY ${renumber(subParser.orderClauses.join(\", \"))}`;\n }\n if (subParser.limitValue !== undefined) {\n limitClause = ` LIMIT ${subParser.limitValue}`;\n }\n }\n\n // Wrap with ORDER/LIMIT only when constraints are present (json_agg ignores ORDER)\n const innerQuery =\n orderBy || limitClause\n ? `SELECT row_to_json(sub.*) FROM (SELECT * FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}${orderBy}${limitClause}) sub`\n : `SELECT ${innerSelect.replace(\"json_agg(\", \"\").replace(/\\)$/, \"\")} FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}`;\n\n const aggregated =\n orderBy || limitClause\n ? `(SELECT json_agg(row_to_json(sub.*)) FROM (SELECT * FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}${orderBy}${limitClause}) sub) AS ${quotedAlias}`\n : `(SELECT ${innerSelect} FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}) AS ${quotedAlias}`;\n\n this.selectRaw.push(aggregated);\n } else {\n // hasOne / belongsTo — single object via LEFT JOIN + row_to_json.\n // If constraint provides an explicit select list, prefer it over data.select\n let effectiveSelect = select;\n if (constraintOps && constraintOps.length > 0) {\n const subParser = new PostgresQueryParser({\n table: (data.table as string) ?? alias,\n alias,\n operations: constraintOps,\n });\n subParser.parse();\n if (subParser.selectColumns.length > 0) {\n effectiveSelect = subParser.selectColumns;\n }\n }\n\n if (effectiveSelect && effectiveSelect.length > 0) {\n const selectedColumns = effectiveSelect\n .map((col) => `${quotedAlias}.${this.dialect.quoteIdentifier(col)}`)\n .join(\", \");\n this.selectRaw.push(\n `row_to_json((SELECT d FROM (SELECT ${selectedColumns}) d)) AS ${quotedAlias}`,\n );\n } else {\n this.selectRaw.push(`row_to_json(${quotedAlias}.*) AS ${quotedAlias}`);\n }\n }\n }\n\n /**\n * Process JOIN operation with smart field detection.\n *\n * Handles both regular columns and JSONB nested paths:\n * - \"id\" → \"table\".\"id\" (auto-prefixed)\n * - \"users.id\" → \"users\".\"id\" (explicit table)\n * - \"createdBy.id\" → \"table\".\"createdBy\"->>'id' (JSONB path)\n * - \"posts.createdBy.id\" → \"posts\".\"createdBy\"->>'id' (JSONB with table)\n */\n private processJoin(data: Record<string, unknown>, type: string): void {\n const options = data as unknown as\n | JoinOptions\n | { table: string; localField: string; foreignField: string };\n\n const joinTable = \"table\" in options ? options.table : \"\";\n const localField = \"localField\" in options ? options.localField : \"\";\n const foreignField = \"foreignField\" in options ? options.foreignField : \"\";\n const alias = \"alias\" in options ? options.alias : undefined;\n const constraintOps = (data.constraintOps as PostgresParserOperation[] | undefined) ?? [];\n\n const quotedTable = this.dialect.quoteIdentifier(joinTable);\n const tableRef = alias\n ? `${quotedTable} AS ${this.dialect.quoteIdentifier(alias)}`\n : quotedTable;\n const tableAlias = alias ?? joinTable;\n\n // Track the joined table for reference detection\n this.joinedTables.add(joinTable);\n if (alias) {\n this.joinedTables.add(alias);\n }\n\n // Parse local field (belongs to main table)\n const quotedLocal = this.parseColumnIdentifier(localField!, this.table, this.alias);\n\n // Parse foreign field (belongs to join table)\n const quotedForeign = this.parseColumnIdentifier(foreignField!, joinTable, tableAlias);\n\n // Build the basic ON condition. For hasOne/belongsTo joinWith with a\n // constraint callback, append the constraint's where-clauses to the ON\n // clause (preserves LEFT JOIN semantics — main rows stay even when no\n // related row passes the constraint). orderBy/limit on the constraint\n // are silently dropped — they have no meaning on a single-row LEFT JOIN.\n let onClause = `${quotedLocal} = ${quotedForeign}`;\n\n if (constraintOps.length > 0) {\n const whereOps = constraintOps.filter(\n (op) => op.type.startsWith(\"where\") || op.type.startsWith(\"orWhere\"),\n );\n\n if (whereOps.length > 0) {\n const subParser = new PostgresQueryParser({\n table: joinTable,\n alias: tableAlias,\n operations: whereOps,\n });\n subParser.parse();\n\n if (subParser.whereClauses.length > 0) {\n const renumber = this.absorbSubParserParams(subParser);\n onClause = `${onClause} AND ${renumber(subParser.whereClauses.join(\" \"))}`;\n }\n }\n }\n\n this.joinClauses.push(`${type} JOIN ${tableRef} ON ${onClause}`);\n }\n\n /**\n * Parse a column identifier with smart detection for table prefixes and JSONB paths.\n *\n * Supports an optional explicit cast suffix using PostgreSQL's `::type` syntax,\n * which is the only way to coerce a JSONB-extracted value (always returned as\n * `text`) into another type. The parser does NOT infer casts from key names —\n * coercion is always opt-in by the caller.\n *\n * @param field - The field string. Examples:\n * - \"id\" → `\"table\".\"id\"`\n * - \"users.id\" → `\"users\".\"id\"`\n * - \"createdBy.id\" → `\"table\".\"createdBy\"->>'id'`\n * - \"createdBy.id::int\" → `(\"table\".\"createdBy\"->>'id')::int`\n * - \"meta.score::numeric\" → `(\"table\".\"meta\"->>'score')::numeric`\n * - \"posts.createdBy.id::uuid\" → `(\"posts\".\"createdBy\"->>'id')::uuid`\n * @param defaultTable - Default table to use if no prefix\n * @param tableAlias - Table alias to use if provided\n * @returns Properly quoted SQL expression\n */\n private parseColumnIdentifier(field: string, defaultTable: string, tableAlias?: string): string {\n if (!field) return \"\";\n\n // Extract optional explicit cast suffix: \"createdBy.id::int\" → path \"createdBy.id\", cast \"int\"\n let cast: string | undefined;\n const castIndex = field.indexOf(\"::\");\n if (castIndex !== -1) {\n cast = field.slice(castIndex + 2).trim();\n field = field.slice(0, castIndex);\n }\n\n const effectiveTable = tableAlias ?? defaultTable;\n const parts = field.split(\".\");\n\n let expression: string;\n\n if (parts.length === 1) {\n // Single part: just a column name, prefix with default table\n expression = `${this.dialect.quoteIdentifier(effectiveTable)}.${this.dialect.quoteIdentifier(field)}`;\n } else if (parts.length === 2) {\n // Two parts: could be \"table.column\" or \"jsonbColumn.key\"\n const [first, second] = parts;\n\n if (this.isTableReference(first)) {\n // It's table.column - regular column reference\n expression = `${this.dialect.quoteIdentifier(first)}.${this.dialect.quoteIdentifier(second)}`;\n } else {\n // It's jsonbColumn.key - JSONB path\n expression = this.buildJsonbPath(effectiveTable, first, [second]);\n }\n } else {\n // Three or more parts: \"table.jsonbColumn.key...\" or \"jsonbColumn.key1.key2...\"\n const [first, second, ...rest] = parts;\n\n if (this.isTableReference(first)) {\n // First part is table: \"posts.createdBy.id\" → table is \"posts\", JSONB is \"createdBy.id\"\n expression = this.buildJsonbPath(first, second, rest);\n } else {\n // No table prefix: \"createdBy.address.city\" → use default table\n expression = this.buildJsonbPath(effectiveTable, first, [second, ...rest]);\n }\n }\n\n return cast ? `(${expression})::${cast}` : expression;\n }\n\n /**\n * Check if a string is a table reference (main table or join table).\n */\n private isTableReference(name: string): boolean {\n if (name === this.table || name === this.alias) {\n return true;\n }\n\n if (this.joinedTables.has(name)) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Build a JSONB path expression.\n *\n * @param table - Table name\n * @param column - JSONB column name\n * @param path - Array of nested keys\n * @returns PostgreSQL JSONB path expression\n *\n * Returns the raw JSONB path expression — extracted values are always `text`\n * (`->>` returns text). Callers that need a different type must request an\n * explicit cast via the `::type` suffix in `parseColumnIdentifier`.\n *\n * @example\n * buildJsonbPath(\"posts\", \"createdBy\", [\"id\"])\n * // Returns: \"posts\".\"createdBy\"->>'id'\n *\n * buildJsonbPath(\"posts\", \"createdBy\", [\"address\", \"city\"])\n * // Returns: \"posts\".\"createdBy\"->'address'->>'city'\n */\n private buildJsonbPath(table: string, column: string, path: string[]): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column);\n\n if (path.length === 0) {\n return `${quotedTable}.${quotedColumn}`;\n }\n\n // Build JSONB path: use -> for intermediate keys, ->> for final key (returns text)\n let expression = `${quotedTable}.${quotedColumn}`;\n\n for (let i = 0; i < path.length; i++) {\n const isLast = i === path.length - 1;\n const operator = isLast ? \"->>\" : \"->\";\n expression += `${operator}'${path[i]}'`;\n }\n\n return expression;\n }\n\n /**\n * Process CROSS JOIN operation.\n */\n private processCrossJoin(data: Record<string, unknown>): void {\n const table = data.table as string;\n const quotedTable = this.dialect.quoteIdentifier(table);\n this.joinClauses.push(`CROSS JOIN ${quotedTable}`);\n }\n\n /**\n * Process raw JOIN expression.\n */\n private processJoinRaw(data: Record<string, unknown>): void {\n const expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n\n this.joinClauses.push(processed);\n }\n\n /**\n * Process ORDER BY operation.\n */\n private processOrderBy(data: Record<string, unknown>): void {\n const field = data.field as string;\n const direction = ((data.direction as string) ?? \"asc\").toUpperCase();\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n this.orderClauses.push(`${quotedField} ${direction}`);\n }\n\n /**\n * Process raw ORDER BY expression.\n */\n private processOrderByRaw(data: Record<string, unknown>): void {\n const expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n // Replace ? placeholders with $n positional params (same as processWhereRaw)\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n this.orderClauses.push(processed);\n }\n\n /**\n * Process GROUP BY operation.\n */\n private processGroupBy(data: Record<string, unknown>): void {\n const fields = data.fields as string | string[];\n const columns = Array.isArray(fields) ? fields : [fields];\n this.groupColumns.push(...columns);\n }\n\n /**\n * Process HAVING operation.\n */\n private processHaving(data: Record<string, unknown>): void {\n const field = data.field as string;\n const operator = (data.operator as string) ?? \"=\";\n const value = data.value;\n\n const quotedField = this.dialect.quoteIdentifier(field);\n const placeholder = this.addParam(value);\n\n this.havingClauses.push(`${quotedField} ${operator} ${placeholder}`);\n }\n\n /**\n * Process a raw HAVING expression, threading `?` placeholders into\n * positional params the same way `processWhereRaw` / `processSelectRaw` do.\n * Without this, bindings on a `havingRaw` op are silently dropped.\n */\n private processHavingRaw(data: Record<string, unknown>): void {\n let expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n for (const binding of bindings) {\n expression = expression.replace(\"?\", this.addParam(binding));\n }\n\n this.havingClauses.push(expression);\n }\n\n /**\n * Add a WHERE clause with boolean operator.\n */\n private addWhereClause(clause: string, boolean: \"AND\" | \"OR\"): void {\n if (this.whereClauses.length === 0) {\n this.whereClauses.push(clause);\n } else {\n this.whereClauses.push(`${boolean} ${clause}`);\n }\n }\n\n /**\n * Map simple Cascade operators to their SQL equivalents.\n *\n * Complex operators (between, in, like-variants, exists) are handled by\n * dedicated processors and should never reach this method.\n */\n private mapOperator(operator: WhereOperator): string {\n const mapping: Record<string, string> = {\n \"=\": \"=\",\n \"!=\": \"!=\",\n \"<>\": \"<>\",\n \">\": \">\",\n \">=\": \">=\",\n \"<\": \"<\",\n \"<=\": \"<=\",\n like: \"LIKE\",\n notlike: \"NOT LIKE\",\n ilike: \"ILIKE\",\n };\n\n return mapping[operator.toLowerCase()] ?? operator;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"postgres-query-parser.mjs","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/postgres/postgres-query-parser.ts"],"sourcesContent":["/**\n * PostgreSQL Query Parser\n *\n * Translates Cascade query operations into PostgreSQL SQL queries.\n * Mirrors the MongoQueryParser pattern but generates SQL instead\n * of MongoDB aggregation pipelines.\n *\n * @module cascade/drivers/postgres\n */\n\nimport type {\n DriverQuery,\n JoinOptions,\n WhereOperator,\n} from \"../../contracts/query-builder.contract\";\nimport type { SqlDialectContract } from \"../sql/sql-dialect.contract\";\n\nimport { PostgresDialect } from \"./postgres-dialect\";\n\n/**\n * Operation types supported by the query parser.\n */\nexport type PostgresOperationType =\n // WHERE operations\n | \"where\"\n | \"orWhere\"\n | \"whereRaw\"\n | \"orWhereRaw\"\n | \"whereIn\"\n | \"whereNotIn\"\n | \"whereNull\"\n | \"whereNotNull\"\n | \"whereBetween\"\n | \"whereNotBetween\"\n | \"whereLike\"\n | \"whereNotLike\"\n | \"whereColumn\"\n | \"orWhereColumn\"\n | \"whereExists\"\n | \"whereNotExists\"\n | \"whereDate\"\n | \"whereDateBefore\"\n | \"whereDateAfter\"\n | \"whereDateBetween\"\n | \"whereJsonContains\"\n | \"whereJsonDoesntContain\"\n | \"whereFullText\"\n // SELECT operations\n | \"select\"\n | \"selectRaw\"\n | \"deselect\"\n // JOIN operations\n | \"join\"\n | \"leftJoin\"\n | \"rightJoin\"\n | \"innerJoin\"\n | \"fullJoin\"\n | \"crossJoin\"\n | \"joinRaw\"\n // ORDER operations\n | \"orderBy\"\n | \"orderByRaw\"\n // GROUP operations\n | \"groupBy\"\n | \"groupByRaw\"\n | \"having\"\n | \"havingRaw\"\n // LIMIT operations\n | \"limit\"\n | \"offset\"\n // RELATION operations\n | \"has\"\n | \"whereHas\"\n | \"doesntHave\"\n | \"whereDoesntHave\"\n // joinWith operations\n | \"selectRelatedColumns\"\n // Other\n | \"distinct\";\n\n/**\n * Internal operation representation.\n */\nexport type PostgresParserOperation = {\n /** Operation type */\n readonly type: PostgresOperationType;\n /** Operation data/parameters */\n readonly data: Record<string, unknown>;\n};\n\n/**\n * Parser configuration options.\n */\nexport type PostgresParserOptions = {\n /** Target table name */\n readonly table: string;\n /** Table alias (optional) */\n readonly alias?: string;\n /** Operations to parse */\n readonly operations: PostgresParserOperation[];\n /** SQL dialect for syntax generation */\n readonly dialect?: SqlDialectContract;\n /** Factory for creating sub-parsers (for nested queries) */\n readonly createSubParser?: (table: string) => PostgresQueryParser;\n};\n\n/**\n * PostgreSQL Query Parser.\n *\n * Converts a list of query operations into a SQL query string with parameters.\n * Handles SELECT, WHERE, JOIN, ORDER BY, GROUP BY, LIMIT/OFFSET clauses.\n *\n * @example\n * ```typescript\n * const parser = new PostgresQueryParser({\n * table: 'users',\n * operations: [\n * { type: 'where', data: { field: 'name', operator: '=', value: 'Alice' } },\n * { type: 'orderBy', data: { field: 'createdAt', direction: 'desc' } },\n * { type: 'limit', data: { value: 10 } }\n * ]\n * });\n *\n * const { sql, params } = parser.parse();\n * // sql: 'SELECT * FROM \"users\" WHERE \"name\" = $1 ORDER BY \"createdAt\" DESC LIMIT 10'\n * // params: ['Alice']\n * ```\n */\nexport class PostgresQueryParser {\n /**\n * Target table name.\n */\n private readonly table: string;\n\n /**\n * Table alias.\n */\n private readonly alias?: string;\n\n /**\n * Operations to process.\n */\n private readonly operations: PostgresParserOperation[];\n\n /**\n * SQL dialect for syntax.\n */\n private readonly dialect: SqlDialectContract;\n\n /**\n * Current parameter index (1-based for PostgreSQL).\n */\n private paramIndex = 1;\n\n /**\n * Collected parameters.\n */\n private readonly params: unknown[] = [];\n\n /**\n * SELECT columns.\n */\n public selectColumns: string[] = [];\n\n /**\n * Deselected (excluded) columns.\n */\n private deselectColumns: string[] = [];\n\n /**\n * Raw SELECT expressions.\n */\n private selectRaw: string[] = [];\n\n /**\n * WHERE clauses.\n */\n public whereClauses: string[] = [];\n\n /**\n * JOIN clauses.\n */\n private joinClauses: string[] = [];\n\n /**\n * ORDER BY clauses.\n */\n public orderClauses: string[] = [];\n\n /**\n * GROUP BY columns (bare identifiers — get quoted).\n */\n private groupColumns: string[] = [];\n\n /**\n * Raw GROUP BY expressions (emitted verbatim — e.g. a `date_trunc(...)`\n * bucket). Kept separate from `groupColumns` so they bypass identifier\n * quoting while still landing in the same `GROUP BY` clause.\n */\n private groupRawExpressions: string[] = [];\n\n /**\n * HAVING clauses.\n */\n private havingClauses: string[] = [];\n\n /**\n * LIMIT value.\n */\n public limitValue?: number;\n\n /**\n * OFFSET value.\n */\n public offsetValue?: number;\n\n /**\n * DISTINCT flag.\n */\n private isDistinct = false;\n\n /**\n * Whether the query has any JOIN operations (pre-scanned before processing).\n * Used by qualifyColumn() to decide whether to prefix columns with the main table.\n */\n private hasJoins = false;\n\n /**\n * Tracked joined tables (for table reference detection).\n */\n private readonly joinedTables = new Set<string>();\n\n /**\n * Create a new query parser.\n *\n * @param options - Parser configuration\n */\n public constructor(options: PostgresParserOptions) {\n this.table = options.table;\n this.alias = options.alias;\n this.operations = options.operations;\n this.dialect = options.dialect ?? new PostgresDialect();\n }\n\n /**\n * Parse all operations and build the SQL query.\n *\n * @returns DriverQuery with `query` (SQL string) and `bindings` (parameter values)\n */\n public parse(): DriverQuery {\n // Pre-scan for any join operations so qualifyColumn() can prefix columns\n // correctly even when WHERE clauses appear before JOINs in the operations list.\n const JOIN_TYPES = new Set([\n \"join\",\n \"leftJoin\",\n \"rightJoin\",\n \"innerJoin\",\n \"fullJoin\",\n \"crossJoin\",\n \"joinRaw\",\n ]);\n\n this.hasJoins = false;\n\n // First pass: locate all joins and populate joinedTables for accurate JSON path detection\n for (const operation of this.operations) {\n if (JOIN_TYPES.has(operation.type)) {\n this.hasJoins = true;\n const data = operation.data as any;\n const joinTable = data.table as string;\n const alias = data.alias as string;\n\n if (joinTable) this.joinedTables.add(joinTable);\n if (alias) this.joinedTables.add(alias);\n }\n }\n\n // Process each operation\n for (const operation of this.operations) {\n this.processOperation(operation);\n }\n\n // Build the final SQL query\n const query = this.buildSql();\n\n return { query, bindings: this.params };\n }\n\n /**\n * Get a formatted string representation of the query.\n *\n * @returns Formatted SQL with bindings\n */\n public toPrettyString(): string {\n const { query = \"\", bindings } = this.parse();\n return `${query}\\n-- Bindings: ${JSON.stringify(bindings ?? [])}`;\n }\n\n /**\n * Process a single operation.\n *\n * @param operation - The operation to process\n */\n private processOperation(operation: PostgresParserOperation): void {\n const { type, data } = operation;\n\n switch (type) {\n // WHERE operations\n case \"where\":\n this.processWhere(data, \"AND\");\n break;\n case \"orWhere\":\n this.processWhere(data, \"OR\");\n break;\n case \"whereRaw\":\n this.processWhereRaw(data, \"AND\");\n break;\n case \"orWhereRaw\":\n this.processWhereRaw(data, \"OR\");\n break;\n case \"whereIn\":\n this.processWhereIn(data, false);\n break;\n case \"whereNotIn\":\n this.processWhereIn(data, true);\n break;\n case \"whereNull\":\n this.processWhereNull(data, false);\n break;\n case \"whereNotNull\":\n this.processWhereNull(data, true);\n break;\n case \"whereBetween\":\n this.processWhereBetween(data, false);\n break;\n case \"whereNotBetween\":\n this.processWhereBetween(data, true);\n break;\n case \"whereLike\":\n this.processWhereLike(data, false);\n break;\n case \"whereNotLike\":\n this.processWhereLike(data, true);\n break;\n case \"whereColumn\":\n this.processWhereColumn(data, \"AND\");\n break;\n case \"orWhereColumn\":\n this.processWhereColumn(data, \"OR\");\n break;\n case \"whereJsonContains\":\n this.processWhereJsonContains(data, false);\n break;\n case \"whereJsonDoesntContain\":\n this.processWhereJsonContains(data, true);\n break;\n case \"whereFullText\":\n this.processWhereFullText(data);\n break;\n\n // SELECT operations\n case \"select\":\n this.processSelect(data);\n break;\n case \"selectRaw\":\n this.processSelectRaw(data);\n break;\n case \"deselect\":\n this.processDeselect(data);\n break;\n\n // JOIN operations\n case \"join\":\n case \"innerJoin\":\n this.processJoin(data, \"INNER\");\n break;\n case \"leftJoin\":\n this.processJoin(data, \"LEFT\");\n break;\n case \"rightJoin\":\n this.processJoin(data, \"RIGHT\");\n break;\n case \"fullJoin\":\n this.processJoin(data, \"FULL OUTER\");\n break;\n case \"crossJoin\":\n this.processCrossJoin(data);\n break;\n case \"joinRaw\":\n this.processJoinRaw(data);\n break;\n\n // ORDER operations\n case \"orderBy\":\n this.processOrderBy(data);\n break;\n case \"orderByRaw\":\n this.processOrderByRaw(data);\n break;\n\n // GROUP operations\n case \"groupBy\":\n this.processGroupBy(data);\n break;\n case \"groupByRaw\":\n this.processGroupByRaw(data);\n break;\n case \"having\":\n this.processHaving(data);\n break;\n case \"havingRaw\":\n this.processHavingRaw(data);\n break;\n\n // LIMIT operations\n case \"limit\":\n this.limitValue = data.value as number;\n break;\n case \"offset\":\n this.offsetValue = data.value as number;\n break;\n\n // Other\n case \"distinct\":\n this.isDistinct = true;\n break;\n\n // joinWith select related columns\n case \"selectRelatedColumns\":\n this.processSelectRelatedColumns(data);\n break;\n\n default:\n // Unknown operation - ignore or throw\n break;\n }\n }\n\n /**\n * Build the final SQL query from collected clauses.\n *\n * @returns Complete SQL query string\n */\n private buildSql(): string {\n const parts: string[] = [];\n\n // SELECT clause\n parts.push(this.buildSelectClause());\n\n // FROM clause\n const quotedTable = this.dialect.quoteIdentifier(this.table);\n const fromClause = this.alias\n ? `FROM ${quotedTable} AS ${this.dialect.quoteIdentifier(this.alias)}`\n : `FROM ${quotedTable}`;\n parts.push(fromClause);\n\n // JOIN clauses\n if (this.joinClauses.length > 0) {\n parts.push(this.joinClauses.join(\" \"));\n }\n\n // WHERE clause\n if (this.whereClauses.length > 0) {\n parts.push(`WHERE ${this.whereClauses.join(\" \")}`);\n }\n\n // GROUP BY clause — bare columns are quoted; raw expressions (e.g.\n // date_trunc buckets) are emitted verbatim. Both share one clause.\n if (this.groupColumns.length > 0 || this.groupRawExpressions.length > 0) {\n const quotedCols = this.groupColumns.map((c) => this.dialect.quoteIdentifier(c));\n const allGroupTerms = [...quotedCols, ...this.groupRawExpressions];\n parts.push(`GROUP BY ${allGroupTerms.join(\", \")}`);\n }\n\n // HAVING clause\n if (this.havingClauses.length > 0) {\n parts.push(`HAVING ${this.havingClauses.join(\" AND \")}`);\n }\n\n // ORDER BY clause\n if (this.orderClauses.length > 0) {\n parts.push(`ORDER BY ${this.orderClauses.join(\", \")}`);\n }\n\n // LIMIT/OFFSET\n const limitOffset = this.dialect.limitOffset(this.limitValue, this.offsetValue);\n if (limitOffset) {\n parts.push(limitOffset);\n }\n\n return parts.join(\" \");\n }\n\n /**\n * Build the SELECT clause.\n *\n * @returns SELECT clause string\n */\n private buildSelectClause(): string {\n const distinct = this.isDistinct ? \"DISTINCT \" : \"\";\n\n // If no specific columns, select all — qualify with main table when joins present\n if (this.selectColumns.length === 0 && this.selectRaw.length === 0) {\n return this.hasJoins\n ? `SELECT ${distinct}${this.dialect.quoteIdentifier(this.table)}.*`\n : `SELECT ${distinct}*`;\n }\n\n const columns: string[] = [];\n\n // Add selected columns — prefix with main table when joins present to avoid ambiguity\n for (const col of this.selectColumns) {\n if (!this.deselectColumns.includes(col)) {\n columns.push(this.parseColumnIdentifier(col, this.table, this.alias));\n }\n }\n\n // Add raw expressions\n columns.push(...this.selectRaw);\n\n return `SELECT ${distinct}${columns.join(\", \")}`;\n }\n\n /**\n * Add a placeholder and parameter.\n *\n * @param value - Parameter value\n * @returns Placeholder string ($1, $2, etc.)\n */\n private addParam(value: unknown): string {\n this.params.push(value);\n return this.dialect.placeholder(this.paramIndex++);\n }\n\n /**\n * Absorb a sub-parser's params into this parser, returning a rewriter\n * function that translates the sub-parser's `$N` placeholders into freshly\n * numbered placeholders in this parser's namespace.\n *\n * Required whenever sub-parser SQL fragments (e.g. WHERE clauses produced\n * for a `joinWith` constraint) are spliced into the outer query — without\n * the rewrite the embedded `$N` references point at parameters this\n * parser has no knowledge of, leaving them dangling in the final SQL.\n *\n * The sub-parser's `params[i]` corresponds to its `$(i+1)` placeholder\n * (its `paramIndex` always starts at 1), so we map sequentially.\n *\n * @example\n * const renumber = this.absorbSubParserParams(subParser);\n * const fragment = renumber(subParser.whereClauses.join(\" \"));\n */\n private absorbSubParserParams(subParser: PostgresQueryParser): (sql: string) => string {\n if (subParser.params.length === 0) {\n return (sql) => sql;\n }\n\n const oldToNew = new Map<string, string>();\n\n for (let index = 0; index < subParser.params.length; index++) {\n const newPlaceholder = this.addParam(subParser.params[index]);\n oldToNew.set(`$${index + 1}`, newPlaceholder);\n }\n\n return (sql) => sql.replace(/\\$\\d+/g, (match) => oldToNew.get(match) ?? match);\n }\n\n /**\n * Process a basic WHERE operation.\n *\n * Delegates to specialised processors for operators that require more than a\n * single placeholder (between, in, like-variants, exists, etc.).\n */\n private processWhere(data: Record<string, unknown>, boolean: \"AND\" | \"OR\"): void {\n const field = data.field as string;\n const operator = (data.operator as WhereOperator) ?? \"=\";\n const value = data.value;\n\n // Delegate to specialised processors for operators that need it\n switch (operator) {\n case \"between\":\n return this.processWhereBetween({ field, range: value as [unknown, unknown] }, false);\n case \"notBetween\":\n return this.processWhereBetween({ field, range: value as [unknown, unknown] }, true);\n case \"in\":\n return this.processWhereIn({ field, values: value as unknown[] }, false);\n case \"notIn\":\n return this.processWhereIn({ field, values: value as unknown[] }, true);\n case \"like\":\n case \"ilike\":\n case \"startsWith\":\n case \"endsWith\":\n return this.processWhereLike({ field, pattern: value as string }, false);\n case \"notLike\":\n case \"notStartsWith\":\n case \"notEndsWith\":\n return this.processWhereLike({ field, pattern: value as string }, true);\n case \"exists\":\n // EXISTS expects value to be a raw sub-query string\n return this.addWhereClause(`EXISTS (${value})`, boolean);\n }\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n\n if (value === null) {\n const nullOperator = operator === \"!=\" ? \"IS NOT NULL\" : \"IS NULL\";\n return this.addWhereClause(`${quotedField} ${nullOperator}`, boolean);\n }\n\n // Simple single-value operator — fall through to generic path\n const placeholder = this.addParam(value);\n this.addWhereClause(`${quotedField} ${this.mapOperator(operator)} ${placeholder}`, boolean);\n }\n\n /**\n * Process a raw WHERE operation.\n */\n private processWhereRaw(data: Record<string, unknown>, boolean: \"AND\" | \"OR\"): void {\n const expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n // Replace ? placeholders with $n\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n\n this.addWhereClause(processed, boolean);\n }\n\n /**\n * Process WHERE IN / NOT IN.\n */\n private processWhereIn(data: Record<string, unknown>, negate: boolean): void {\n const field = data.field as string;\n const values = data.values as unknown[];\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const operator = negate ? \"!= ALL\" : \"= ANY\";\n const placeholder = this.addParam(values);\n\n this.addWhereClause(`${quotedField} ${operator}(${placeholder})`, \"AND\");\n }\n\n /**\n * Process WHERE NULL / NOT NULL.\n */\n private processWhereNull(data: Record<string, unknown>, negate: boolean): void {\n const field = data.field as string;\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const clause = negate ? `${quotedField} IS NOT NULL` : `${quotedField} IS NULL`;\n this.addWhereClause(clause, \"AND\");\n }\n\n /**\n * Process WHERE BETWEEN / NOT BETWEEN.\n */\n private processWhereBetween(data: Record<string, unknown>, negate: boolean): void {\n const field = data.field as string;\n const range = data.range as [unknown, unknown];\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const placeholder1 = this.addParam(range[0]);\n const placeholder2 = this.addParam(range[1]);\n const keyword = negate ? \"NOT BETWEEN\" : \"BETWEEN\";\n\n this.addWhereClause(`${quotedField} ${keyword} ${placeholder1} AND ${placeholder2}`, \"AND\");\n }\n\n /**\n * Process WHERE LIKE / NOT LIKE.\n */\n private processWhereLike(data: Record<string, unknown>, negate: boolean): void {\n const field = data.field as string;\n const pattern = data.pattern as string;\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n const { operator } = this.dialect.likePattern(pattern, true);\n const placeholder = this.addParam(pattern);\n const keyword = negate ? `NOT ${operator}` : operator;\n\n this.addWhereClause(`${quotedField} ${keyword} ${placeholder}`, \"AND\");\n }\n\n /**\n * Process WHERE column comparison.\n */\n private processWhereColumn(data: Record<string, unknown>, boolean: \"AND\" | \"OR\"): void {\n const first = data.first as string;\n const operator = (data.operator as string) ?? \"=\";\n const second = data.second as string;\n\n // Both sides may need qualification if unambiguous table is unclear\n const quotedFirst = this.parseColumnIdentifier(first, this.table, this.alias);\n const quotedSecond = this.parseColumnIdentifier(second, this.table, this.alias);\n\n this.addWhereClause(`${quotedFirst} ${operator} ${quotedSecond}`, boolean);\n }\n\n /**\n * Process WHERE JSON contains.\n */\n private processWhereJsonContains(data: Record<string, unknown>, negate: boolean): void {\n const path = data.path as string;\n const value = data.value;\n\n const quotedPath = this.parseColumnIdentifier(path, this.table, this.alias);\n const jsonValue = JSON.stringify(value);\n const operator = negate ? \"NOT @>\" : \"@>\";\n\n this.addWhereClause(`${quotedPath} ${operator} '${jsonValue}'::jsonb`, \"AND\");\n }\n\n /**\n * Process full-text search WHERE.\n */\n private processWhereFullText(data: Record<string, unknown>): void {\n const fields = data.fields as string[];\n const query = data.query as string;\n\n // Build tsvector from fields\n const tsVectors = fields\n .map((f) => `to_tsvector('english', ${this.dialect.quoteIdentifier(f)})`)\n .join(\" || \");\n\n const placeholder = this.addParam(query);\n this.addWhereClause(`(${tsVectors}) @@ plainto_tsquery('english', ${placeholder})`, \"AND\");\n }\n\n /**\n * Process SELECT operation.\n */\n private processSelect(data: Record<string, unknown>): void {\n const fields = data.fields as string[] | Record<string, string>;\n\n if (Array.isArray(fields)) {\n this.selectColumns.push(...fields);\n } else {\n // Handle aliases: { field: 'alias' }\n for (const [field, alias] of Object.entries(fields)) {\n const quotedField = this.dialect.quoteIdentifier(field);\n const quotedAlias = this.dialect.quoteIdentifier(alias);\n this.selectRaw.push(`${quotedField} AS ${quotedAlias}`);\n }\n }\n }\n\n /**\n * Process raw SELECT expression.\n */\n private processSelectRaw(data: Record<string, unknown>): void {\n const expression = data.expression as string | Record<string, unknown>;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n if (typeof expression === \"string\") {\n // Replace ? placeholders with $n positional params (same as processWhereRaw)\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n this.selectRaw.push(processed);\n } else {\n // Handle object expressions (for compatibility)\n for (const [alias, expr] of Object.entries(expression)) {\n this.selectRaw.push(`${expr} AS ${this.dialect.quoteIdentifier(alias)}`);\n }\n }\n }\n\n /**\n * Process DESELECT operation.\n */\n private processDeselect(data: Record<string, unknown>): void {\n const fields = data.fields as string[];\n this.deselectColumns.push(...fields);\n }\n\n /**\n * Process SELECT for related columns (joinWith).\n *\n * - hasOne / belongsTo → LEFT JOIN + row_to_json (single object)\n * - hasMany → correlated subquery with json_agg (array, no row explosion)\n *\n * @example hasMany correlated subquery:\n * (SELECT json_agg(row_to_json(a.*))\n * FROM \"chat_message_actions\" a\n * WHERE a.\"chat_message_id\" = \"chat_messages\".\"id\") AS \"actions\"\n *\n * @example hasOne/belongsTo row_to_json:\n * row_to_json(\"organizationAiModel\".*) AS \"organizationAiModel\"\n */\n private processSelectRelatedColumns(data: Record<string, unknown>): void {\n const alias = data.alias as string;\n const select = data.select as string[] | undefined;\n const relationType = data.type as string | undefined;\n const constraintOps = data.constraintOps as PostgresParserOperation[] | undefined;\n const quotedAlias = this.dialect.quoteIdentifier(alias);\n const quotedTable = this.dialect.quoteIdentifier(this.table);\n\n const hasExplicitSelect = this.selectColumns.length > 0;\n if (!hasExplicitSelect && !this.selectRaw.includes(`${quotedTable}.*`)) {\n this.selectRaw.unshift(`${quotedTable}.*`);\n }\n\n if (relationType === \"hasMany\") {\n // Correlated subquery — no JOIN, no row explosion, returns a JSON array.\n const relatedTable = data.table as string;\n const foreignKey = data.foreignKey as string;\n const localKey = data.localKey as string;\n const quotedRelatedTable = this.dialect.quoteIdentifier(relatedTable);\n const quotedForeignKey = this.dialect.quoteIdentifier(foreignKey);\n const quotedLocalKey = this.dialect.quoteIdentifier(localKey);\n const quotedMainTable = this.dialect.quoteIdentifier(this.table);\n\n let innerSelect: string;\n if (select && select.length > 0) {\n // Pick specific columns: json_agg(json_build_object('id', a.\"id\", ...))\n const fields = select\n .map((col) => `'${col}', a.${this.dialect.quoteIdentifier(col)}`)\n .join(\", \");\n innerSelect = `json_agg(json_build_object(${fields}))`;\n } else {\n innerSelect = `json_agg(row_to_json(a.*))`;\n }\n\n // Build the base FK condition\n const fkCondition = `a.${quotedForeignKey} = ${quotedMainTable}.${quotedLocalKey}`;\n\n // Merge constraint ops (where / order / limit) from a sub-parser.\n // The sub-parser numbers its own placeholders starting at $1; we must\n // absorb its params into this parser and renumber the embedded SQL,\n // otherwise $N references in the merged clauses dangle.\n let extraWhere = \"\";\n let orderBy = \"\";\n let limitClause = \"\";\n\n if (constraintOps && constraintOps.length > 0) {\n const subParser = new PostgresQueryParser({\n table: relatedTable,\n alias: \"a\",\n operations: constraintOps,\n });\n subParser.parse();\n\n const renumber = this.absorbSubParserParams(subParser);\n\n if (subParser.whereClauses.length > 0) {\n extraWhere = ` AND ${renumber(subParser.whereClauses.join(\" \"))}`;\n }\n if (subParser.orderClauses.length > 0) {\n orderBy = ` ORDER BY ${renumber(subParser.orderClauses.join(\", \"))}`;\n }\n if (subParser.limitValue !== undefined) {\n limitClause = ` LIMIT ${subParser.limitValue}`;\n }\n }\n\n // Wrap with ORDER/LIMIT only when constraints are present (json_agg ignores ORDER)\n const innerQuery =\n orderBy || limitClause\n ? `SELECT row_to_json(sub.*) FROM (SELECT * FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}${orderBy}${limitClause}) sub`\n : `SELECT ${innerSelect.replace(\"json_agg(\", \"\").replace(/\\)$/, \"\")} FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}`;\n\n const aggregated =\n orderBy || limitClause\n ? `(SELECT json_agg(row_to_json(sub.*)) FROM (SELECT * FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}${orderBy}${limitClause}) sub) AS ${quotedAlias}`\n : `(SELECT ${innerSelect} FROM ${quotedRelatedTable} a WHERE ${fkCondition}${extraWhere}) AS ${quotedAlias}`;\n\n this.selectRaw.push(aggregated);\n } else {\n // hasOne / belongsTo — single object via LEFT JOIN + row_to_json.\n // If constraint provides an explicit select list, prefer it over data.select\n let effectiveSelect = select;\n if (constraintOps && constraintOps.length > 0) {\n const subParser = new PostgresQueryParser({\n table: (data.table as string) ?? alias,\n alias,\n operations: constraintOps,\n });\n subParser.parse();\n if (subParser.selectColumns.length > 0) {\n effectiveSelect = subParser.selectColumns;\n }\n }\n\n if (effectiveSelect && effectiveSelect.length > 0) {\n const selectedColumns = effectiveSelect\n .map((col) => `${quotedAlias}.${this.dialect.quoteIdentifier(col)}`)\n .join(\", \");\n this.selectRaw.push(\n `row_to_json((SELECT d FROM (SELECT ${selectedColumns}) d)) AS ${quotedAlias}`,\n );\n } else {\n this.selectRaw.push(`row_to_json(${quotedAlias}.*) AS ${quotedAlias}`);\n }\n }\n }\n\n /**\n * Process JOIN operation with smart field detection.\n *\n * Handles both regular columns and JSONB nested paths:\n * - \"id\" → \"table\".\"id\" (auto-prefixed)\n * - \"users.id\" → \"users\".\"id\" (explicit table)\n * - \"createdBy.id\" → \"table\".\"createdBy\"->>'id' (JSONB path)\n * - \"posts.createdBy.id\" → \"posts\".\"createdBy\"->>'id' (JSONB with table)\n */\n private processJoin(data: Record<string, unknown>, type: string): void {\n const options = data as unknown as\n | JoinOptions\n | { table: string; localField: string; foreignField: string };\n\n const joinTable = \"table\" in options ? options.table : \"\";\n const localField = \"localField\" in options ? options.localField : \"\";\n const foreignField = \"foreignField\" in options ? options.foreignField : \"\";\n const alias = \"alias\" in options ? options.alias : undefined;\n const constraintOps = (data.constraintOps as PostgresParserOperation[] | undefined) ?? [];\n\n const quotedTable = this.dialect.quoteIdentifier(joinTable);\n const tableRef = alias\n ? `${quotedTable} AS ${this.dialect.quoteIdentifier(alias)}`\n : quotedTable;\n const tableAlias = alias ?? joinTable;\n\n // Track the joined table for reference detection\n this.joinedTables.add(joinTable);\n if (alias) {\n this.joinedTables.add(alias);\n }\n\n // Parse local field (belongs to main table)\n const quotedLocal = this.parseColumnIdentifier(localField!, this.table, this.alias);\n\n // Parse foreign field (belongs to join table)\n const quotedForeign = this.parseColumnIdentifier(foreignField!, joinTable, tableAlias);\n\n // Build the basic ON condition. For hasOne/belongsTo joinWith with a\n // constraint callback, append the constraint's where-clauses to the ON\n // clause (preserves LEFT JOIN semantics — main rows stay even when no\n // related row passes the constraint). orderBy/limit on the constraint\n // are silently dropped — they have no meaning on a single-row LEFT JOIN.\n let onClause = `${quotedLocal} = ${quotedForeign}`;\n\n if (constraintOps.length > 0) {\n const whereOps = constraintOps.filter(\n (op) => op.type.startsWith(\"where\") || op.type.startsWith(\"orWhere\"),\n );\n\n if (whereOps.length > 0) {\n const subParser = new PostgresQueryParser({\n table: joinTable,\n alias: tableAlias,\n operations: whereOps,\n });\n subParser.parse();\n\n if (subParser.whereClauses.length > 0) {\n const renumber = this.absorbSubParserParams(subParser);\n onClause = `${onClause} AND ${renumber(subParser.whereClauses.join(\" \"))}`;\n }\n }\n }\n\n this.joinClauses.push(`${type} JOIN ${tableRef} ON ${onClause}`);\n }\n\n /**\n * Parse a column identifier with smart detection for table prefixes and JSONB paths.\n *\n * Supports an optional explicit cast suffix using PostgreSQL's `::type` syntax,\n * which is the only way to coerce a JSONB-extracted value (always returned as\n * `text`) into another type. The parser does NOT infer casts from key names —\n * coercion is always opt-in by the caller.\n *\n * @param field - The field string. Examples:\n * - \"id\" → `\"table\".\"id\"`\n * - \"users.id\" → `\"users\".\"id\"`\n * - \"createdBy.id\" → `\"table\".\"createdBy\"->>'id'`\n * - \"createdBy.id::int\" → `(\"table\".\"createdBy\"->>'id')::int`\n * - \"meta.score::numeric\" → `(\"table\".\"meta\"->>'score')::numeric`\n * - \"posts.createdBy.id::uuid\" → `(\"posts\".\"createdBy\"->>'id')::uuid`\n * @param defaultTable - Default table to use if no prefix\n * @param tableAlias - Table alias to use if provided\n * @returns Properly quoted SQL expression\n */\n private parseColumnIdentifier(field: string, defaultTable: string, tableAlias?: string): string {\n if (!field) return \"\";\n\n // Extract optional explicit cast suffix: \"createdBy.id::int\" → path \"createdBy.id\", cast \"int\"\n let cast: string | undefined;\n const castIndex = field.indexOf(\"::\");\n if (castIndex !== -1) {\n cast = field.slice(castIndex + 2).trim();\n field = field.slice(0, castIndex);\n }\n\n const effectiveTable = tableAlias ?? defaultTable;\n const parts = field.split(\".\");\n\n let expression: string;\n\n if (parts.length === 1) {\n // Single part: just a column name, prefix with default table\n expression = `${this.dialect.quoteIdentifier(effectiveTable)}.${this.dialect.quoteIdentifier(field)}`;\n } else if (parts.length === 2) {\n // Two parts: could be \"table.column\" or \"jsonbColumn.key\"\n const [first, second] = parts;\n\n if (this.isTableReference(first)) {\n // It's table.column - regular column reference\n expression = `${this.dialect.quoteIdentifier(first)}.${this.dialect.quoteIdentifier(second)}`;\n } else {\n // It's jsonbColumn.key - JSONB path\n expression = this.buildJsonbPath(effectiveTable, first, [second]);\n }\n } else {\n // Three or more parts: \"table.jsonbColumn.key...\" or \"jsonbColumn.key1.key2...\"\n const [first, second, ...rest] = parts;\n\n if (this.isTableReference(first)) {\n // First part is table: \"posts.createdBy.id\" → table is \"posts\", JSONB is \"createdBy.id\"\n expression = this.buildJsonbPath(first, second, rest);\n } else {\n // No table prefix: \"createdBy.address.city\" → use default table\n expression = this.buildJsonbPath(effectiveTable, first, [second, ...rest]);\n }\n }\n\n return cast ? `(${expression})::${cast}` : expression;\n }\n\n /**\n * Check if a string is a table reference (main table or join table).\n */\n private isTableReference(name: string): boolean {\n if (name === this.table || name === this.alias) {\n return true;\n }\n\n if (this.joinedTables.has(name)) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Build a JSONB path expression.\n *\n * @param table - Table name\n * @param column - JSONB column name\n * @param path - Array of nested keys\n * @returns PostgreSQL JSONB path expression\n *\n * Returns the raw JSONB path expression — extracted values are always `text`\n * (`->>` returns text). Callers that need a different type must request an\n * explicit cast via the `::type` suffix in `parseColumnIdentifier`.\n *\n * @example\n * buildJsonbPath(\"posts\", \"createdBy\", [\"id\"])\n * // Returns: \"posts\".\"createdBy\"->>'id'\n *\n * buildJsonbPath(\"posts\", \"createdBy\", [\"address\", \"city\"])\n * // Returns: \"posts\".\"createdBy\"->'address'->>'city'\n */\n private buildJsonbPath(table: string, column: string, path: string[]): string {\n const quotedTable = this.dialect.quoteIdentifier(table);\n const quotedColumn = this.dialect.quoteIdentifier(column);\n\n if (path.length === 0) {\n return `${quotedTable}.${quotedColumn}`;\n }\n\n // Build JSONB path: use -> for intermediate keys, ->> for final key (returns text)\n let expression = `${quotedTable}.${quotedColumn}`;\n\n for (let i = 0; i < path.length; i++) {\n const isLast = i === path.length - 1;\n const operator = isLast ? \"->>\" : \"->\";\n expression += `${operator}'${path[i]}'`;\n }\n\n return expression;\n }\n\n /**\n * Process CROSS JOIN operation.\n */\n private processCrossJoin(data: Record<string, unknown>): void {\n const table = data.table as string;\n const quotedTable = this.dialect.quoteIdentifier(table);\n this.joinClauses.push(`CROSS JOIN ${quotedTable}`);\n }\n\n /**\n * Process raw JOIN expression.\n */\n private processJoinRaw(data: Record<string, unknown>): void {\n const expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n\n this.joinClauses.push(processed);\n }\n\n /**\n * Process ORDER BY operation.\n */\n private processOrderBy(data: Record<string, unknown>): void {\n const field = data.field as string;\n const direction = ((data.direction as string) ?? \"asc\").toUpperCase();\n\n const quotedField = this.parseColumnIdentifier(field, this.table, this.alias);\n this.orderClauses.push(`${quotedField} ${direction}`);\n }\n\n /**\n * Process raw ORDER BY expression.\n */\n private processOrderByRaw(data: Record<string, unknown>): void {\n const expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n // Replace ? placeholders with $n positional params (same as processWhereRaw)\n let processed = expression;\n for (const binding of bindings) {\n processed = processed.replace(\"?\", this.addParam(binding));\n }\n this.orderClauses.push(processed);\n }\n\n /**\n * Process GROUP BY operation.\n */\n private processGroupBy(data: Record<string, unknown>): void {\n const fields = data.fields as string | string[];\n const columns = Array.isArray(fields) ? fields : [fields];\n this.groupColumns.push(...columns);\n }\n\n /**\n * Process a raw GROUP BY expression (emitted verbatim, no quoting). Used by\n * `groupByDate` to add a `date_trunc(...)` bucket to the GROUP BY clause.\n */\n private processGroupByRaw(data: Record<string, unknown>): void {\n const expression = data.expression as string;\n this.groupRawExpressions.push(expression);\n }\n\n /**\n * Process HAVING operation.\n */\n private processHaving(data: Record<string, unknown>): void {\n const field = data.field as string;\n const operator = (data.operator as string) ?? \"=\";\n const value = data.value;\n\n const quotedField = this.dialect.quoteIdentifier(field);\n const placeholder = this.addParam(value);\n\n this.havingClauses.push(`${quotedField} ${operator} ${placeholder}`);\n }\n\n /**\n * Process a raw HAVING expression, threading `?` placeholders into\n * positional params the same way `processWhereRaw` / `processSelectRaw` do.\n * Without this, bindings on a `havingRaw` op are silently dropped.\n */\n private processHavingRaw(data: Record<string, unknown>): void {\n let expression = data.expression as string;\n const bindings = (data.bindings as unknown[]) ?? [];\n\n for (const binding of bindings) {\n expression = expression.replace(\"?\", this.addParam(binding));\n }\n\n this.havingClauses.push(expression);\n }\n\n /**\n * Add a WHERE clause with boolean operator.\n */\n private addWhereClause(clause: string, boolean: \"AND\" | \"OR\"): void {\n if (this.whereClauses.length === 0) {\n this.whereClauses.push(clause);\n } else {\n this.whereClauses.push(`${boolean} ${clause}`);\n }\n }\n\n /**\n * Map simple Cascade operators to their SQL equivalents.\n *\n * Complex operators (between, in, like-variants, exists) are handled by\n * dedicated processors and should never reach this method.\n */\n private mapOperator(operator: WhereOperator): string {\n const mapping: Record<string, string> = {\n \"=\": \"=\",\n \"!=\": \"!=\",\n \"<>\": \"<>\",\n \">\": \">\",\n \">=\": \">=\",\n \"<\": \"<\",\n \"<=\": \"<=\",\n like: \"LIKE\",\n notlike: \"NOT LIKE\",\n ilike: \"ILIKE\",\n };\n\n return mapping[operator.toLowerCase()] ?? operator;\n }\n}\n"],"mappings":"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"}
@@ -34,6 +34,21 @@ type PostgresPoolConfig = PostgresConnectionConfig & {
34
34
  readonly connectionTimeoutMillis?: number; /** Maximum times to use a connection before destroying it */
35
35
  readonly maxUses?: number; /** Application name for connection identification */
36
36
  readonly application_name?: string;
37
+ /**
38
+ * Column names whose values are native PostgreSQL arrays (e.g. `JSONB[]`,
39
+ * `TEXT[]`, `INTEGER[]`) rather than scalar `json` / `jsonb` columns.
40
+ *
41
+ * The serializer cannot see the table schema, so by default it
42
+ * `JSON.stringify`s every non-vector array (and plain object) so it binds
43
+ * as JSON text — the only correct form for a `json` / `jsonb` column. For a
44
+ * genuine native-array column (`arrayJson()` → `JSONB[]`, `arrayText()` →
45
+ * `TEXT[]`, …) that conversion is wrong: node-pg must receive the raw JS
46
+ * array so it emits a `{...}` array literal. List those columns here to
47
+ * opt them out of JSON-text encoding.
48
+ *
49
+ * @default undefined (no native-array columns)
50
+ */
51
+ readonly nativeArrayColumns?: readonly string[];
37
52
  };
38
53
  /**
39
54
  * PostgreSQL query result wrapper.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/postgres/types.ts"],"mappings":";;AAWA;;;;;;;;;KAAY,wBAAA;EAcD,oDAZA,IAAA,WAgBM;EAAA,SAdN,IAAA,WAgBM;EAAA,SAdN,QAAA,UAiBO;EAAA,SAfP,IAAA,WAqBC;EAAA,SAnBD,QAAA;WAEA,gBAAA,WAiBsB;EAAA,SAftB,GAAA;IAAA,SAGM,kBAAA;IAAA,SACA,EAAA;IAAA,SACA,IAAA;IAAA,SACA,GAAA;EAAA,GAqBU;EAAA,SAlBhB,OAAA;AAAA;;;;KAMC,kBAAA,GAAqB,wBAAwB;EAwBtC,mEAtBR,GAAA,WAsBa;EAAA,SApBb,GAAA,WAcyB;EAAA,SAZzB,iBAAA,WAcM;EAAA,SAZN,uBAAA,WAgBA;EAAA,SAdA,OAAA,WAeE;EAAA,SAbF,gBAAA;AAAA;;AAiBO;AAMlB;KAjBY,mBAAA,KAAwB,MAAA;qDAEzB,IAAA,EAAM,CAAA,IAeiB;EAAA,SAbvB,QAAA,iBAsB2B;EAAA,SApB3B,MAAA,EAAQ,KAAA;IAAA,SACN,IAAA;IAAA,SACA,UAAA;EAAA,IAsBF;EAAA,SAnBA,OAAA;AAAA;AAqBU;AAMrB;;AANqB,KAfT,sBAAA;;;;KASA,0BAAA;EA2BK,mDAzBN,cAAA,GAAiB,sBAAsB,EAyB3B;EAAA,SAvBZ,QAAA,YA6BoB;EAAA,SA3BpB,UAAA;AAAA;;;;KAMC,iBAAA;EA2CD,2DAzCA,KAAA,gGA6CA;EAAA,SAlCA,IAAA,UAoCS;EAAA,SAlCT,IAAA,EAAM,MAAM;AAAA;AAwCvB;;;AAAA,KAlCY,mBAAA;EAoCD,gEAlCA,OAAA,gBAsCA;EAAA,SApCA,IAAA,mIAoCS;EAAA,SAtBT,MAAA,WA4BoB;EAAA,SA1BpB,QAAA,WA0BoB;EAAA,SAxBpB,KAAA,YA4BA;EAAA,SA1BA,GAAA,WA8BA;EAAA,SA5BA,QAAA,cAgCA;EAAA,SA9BA,MAAA,GAAS,mBAAmB;AAAA;AAgCrB;;;AAAA,KA1BN,oBAAA;8BAED,OAAA;WAEA,OAAA;WAEA,SAAA;AAAA;;;;KAMC,mBAAA;4CAED,MAAA;WAEA,SAAA;WAEA,KAAA;WAEA,MAAA;WAEA,MAAA;WAEA,IAAA;WAEA,OAAA;AAAA"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/postgres/types.ts"],"mappings":";;AAWA;;;;;;;;;KAAY,wBAAA;EAcD,oDAZA,IAAA,WAgBM;EAAA,SAdN,IAAA,WAgBM;EAAA,SAdN,QAAA,UAiBO;EAAA,SAfP,IAAA,WAqBC;EAAA,SAnBD,QAAA;WAEA,gBAAA,WAiBsB;EAAA,SAftB,GAAA;IAAA,SAGM,kBAAA;IAAA,SACA,EAAA;IAAA,SACA,IAAA;IAAA,SACA,GAAA;EAAA,GAoCN;EAAA,SAjCA,OAAA;AAAA;AAuCX;;;AAAA,KAjCY,kBAAA,GAAqB,wBAAwB;EAmCxC,mEAjCN,GAAA,WAqCa;EAAA,SAnCb,GAAA,WA6BqB;EAAA,SA3BrB,iBAAA,WA6BA;EAAA,SA3BA,uBAAA,WA6BA;EAAA,SA3BA,OAAA,WA6BQ;EAAA,SA3BR,gBAAA;EA6BE;;;AAGK;AAMlB;;;;AAAkC;AASlC;;;;;EAlBa,SAdF,kBAAA;AAAA;;;AAsCU;KAhCT,mBAAA,KAAwB,MAAA;EAsCP,mDApClB,IAAA,EAAM,CAAA,IAmDM;EAAA,SAjDZ,QAAA,iBA+CA;EAAA,SA7CA,MAAA,EAAQ,KAAA;IAAA,SACN,IAAA;IAAA,SACA,UAAA;EAAA,IAmDD;EAAA,SAhDD,OAAA;AAAA;;;;KAMC,sBAAA;;;;KASA,0BAAA;EA6DD,mDA3DA,cAAA,GAAiB,sBAAsB,EA2DX;EAAA,SAzD5B,QAAA,YA+DC;EAAA,SA7DD,UAAA;AAAA;;;;KAMC,iBAAA;EA6DQ,2DA3DT,KAAA,gGAiEC;EAAA,SAtDD,IAAA;WAEA,IAAA,EAAM,MAAM;AAAA;;;;KAMX,mBAAA;EA0DD,gEAxDA,OAAA,gBA0DO;EAAA,SAxDP,IAAA;WAcA,MAAA;WAEA,QAAA;WAEA,KAAA;WAEA,GAAA;WAEA,QAAA;WAEA,MAAA,GAAS,mBAAmB;AAAA;;;;KAM3B,oBAAA;8BAED,OAAA;WAEA,OAAA;WAEA,SAAA;AAAA;;;;KAMC,mBAAA;4CAED,MAAA;WAEA,SAAA;WAEA,KAAA;WAEA,MAAA;WAEA,MAAA;WAEA,IAAA;WAEA,OAAA;AAAA"}
@@ -1,3 +1,4 @@
1
+ import { ColumnExpression } from "../../expressions/column-expressions.mjs";
1
2
  import { AggregateExpression } from "../../expressions/aggregate-expressions.mjs";
2
3
 
3
4
  //#region ../@warlock.js/cascade/src/drivers/sql/sql-dialect.contract.d.ts
@@ -215,6 +216,25 @@ interface SqlDialectContract {
215
216
  * ```
216
217
  */
217
218
  aggregateToSql(expression: AggregateExpression): string;
219
+ /**
220
+ * Compile a typed {@link ColumnExpression} tree into a SQL fragment.
221
+ *
222
+ * Used by expression-aware aggregates (e.g. `$agg.sum($expr.mul("price","qty"))`).
223
+ * Column references MUST be quoted/escaped via the dialect's identifier path
224
+ * — never raw-interpolated. Only the `raw` node emits a verbatim string.
225
+ *
226
+ * @param expression - The expression tree to compile
227
+ * @returns A SQL fragment (e.g. `("price" * "quantity")`)
228
+ */
229
+ columnExpressionToSql(expression: ColumnExpression): string;
230
+ /**
231
+ * Build a date-bucket truncation expression for a portable `groupByDate`.
232
+ *
233
+ * @param column - The date/timestamp column to truncate
234
+ * @param unit - The bucket granularity
235
+ * @returns The SQL fragment (e.g. `date_trunc('month', "created_at")`)
236
+ */
237
+ dateTruncSql(column: string, unit: "day" | "week" | "month" | "year"): string;
218
238
  }
219
239
  //#endregion
220
240
  export { SqlDialectContract };
@@ -1 +1 @@
1
- {"version":3,"file":"sql-dialect.contract.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/sql/sql-dialect.contract.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;UAmCiB,kBAAA;EA4Kf;;;;;EAAA,SAtKS,IAAA;EAwKP;;;;;AAuB4C;;;;;;;;;;;EA7K9C,WAAA,CAAY,KAAA;;;;;;;;;;;;;;;;;;;EAoBZ,eAAA,CAAgB,UAAA;;;;;;;;;;;;;;;;;EAkBhB,cAAA,CAAe,KAAA;;;;;;;WAQN,iBAAA;;;;;;;WAQA,aAAA;;;;;;;;;;;;;;;;;EAkBT,WAAA,CAAY,KAAA,WAAgB,MAAA;;;;;;;;;;;;;;;;;;;EAoB5B,WAAA,CAAY,MAAA,UAAgB,IAAA;;;;;;;;;;;;EAa5B,YAAA,CAAa,MAAA,UAAgB,KAAA,WAAgB,IAAA;;;;;;;;;;;;;;;EAgB7C,WAAA,CAAY,OAAA,UAAiB,eAAA;IAA8B,QAAA;IAAkB,OAAA;EAAA;;;;;;;;;;;EAY7E,aAAA,CAAc,MAAA,UAAgB,UAAA;;;;;;;;;;;;;;EAe9B,UAAA,CACE,IAAA,UACA,OAAA;IAAY,MAAA;IAAiB,SAAA;IAAoB,KAAA;IAAgB,UAAA;EAAA;;;;;;;;;;;;;;;;;;;;;EAuBnE,cAAA,CAAe,UAAA,EAAY,mBAAmB;AAAA"}
1
+ {"version":3,"file":"sql-dialect.contract.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/sql/sql-dialect.contract.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;UAmCiB,kBAAA;EA6Je;;;;;EAAA,SAvJrB,IAAA;EAwK0D;;;;;;;;;;;;AA4CC;;;;EAlMpE,WAAA,CAAY,KAAA;;;;;;;;;;;;;;;;;;;EAoBZ,eAAA,CAAgB,UAAA;;;;;;;;;;;;;;;;;EAkBhB,cAAA,CAAe,KAAA;;;;;;;WAQN,iBAAA;;;;;;;WAQA,aAAA;;;;;;;;;;;;;;;;;EAkBT,WAAA,CAAY,KAAA,WAAgB,MAAA;;;;;;;;;;;;;;;;;;;EAoB5B,WAAA,CAAY,MAAA,UAAgB,IAAA;;;;;;;;;;;;EAa5B,YAAA,CAAa,MAAA,UAAgB,KAAA,WAAgB,IAAA;;;;;;;;;;;;;;;EAgB7C,WAAA,CAAY,OAAA,UAAiB,eAAA;IAA8B,QAAA;IAAkB,OAAA;EAAA;;;;;;;;;;;EAY7E,aAAA,CAAc,MAAA,UAAgB,UAAA;;;;;;;;;;;;;;EAe9B,UAAA,CACE,IAAA,UACA,OAAA;IAAY,MAAA;IAAiB,SAAA;IAAoB,KAAA;IAAgB,UAAA;EAAA;;;;;;;;;;;;;;;;;;;;;EAuBnE,cAAA,CAAe,UAAA,EAAY,mBAAA;;;;;;;;;;;EAY3B,qBAAA,CAAsB,UAAA,EAAY,gBAAgB;;;;;;;;EASlD,YAAA,CAAa,MAAA,UAAgB,IAAA;AAAA"}
@@ -1,30 +1,6 @@
1
+ import { ColumnExpression, ColumnExpressionInput } from "./column-expressions.mjs";
2
+
1
3
  //#region ../@warlock.js/cascade/src/expressions/aggregate-expressions.d.ts
2
- /**
3
- * Database-agnostic aggregation expressions.
4
- *
5
- * `count` / `sum` / `avg` / `min` / `max` are **cross-driver** — the same
6
- * call compiles to the native shape on every supported database (MongoDB
7
- * `{ $sum: "$field" }`, SQL `SUM(field)`, ...).
8
- *
9
- * `distinct` / `floor` / `first` / `last` are **MongoDB-only**. They have no
10
- * single-scalar `GROUP BY` equivalent in SQL, so on Postgres they throw a
11
- * fail-fast error at the `.groupBy()` call (naming the `selectRaw` /
12
- * `havingRaw` escape hatch) rather than emit a silently-different query.
13
- *
14
- * @example
15
- * ```typescript
16
- * import { $agg } from '@warlock.js/cascade';
17
- *
18
- * // Works for both MongoDB and SQL
19
- * Lesson.query()
20
- * .groupBy("type", {
21
- * count: $agg.count(),
22
- * total: $agg.sum("duration"),
23
- * avg: $agg.avg("rating")
24
- * })
25
- * .get();
26
- * ```
27
- */
28
4
  /**
29
5
  * Abstract aggregate expression format.
30
6
  *
@@ -32,13 +8,20 @@
32
8
  * to their native syntax.
33
9
  */
34
10
  type AggregateExpression = {
35
- /** The aggregate function type */__agg: AggregateFunction; /** The field to aggregate (null for count) */
11
+ /** The aggregate function type */__agg: AggregateFunction; /** The field to aggregate (null for count, or when `__expr` carries a composed expression) */
36
12
  __field: string | null;
13
+ /**
14
+ * A typed column expression to aggregate over, when the aggregate operates
15
+ * on more than a bare column (e.g. `price * quantity`). When present, drivers
16
+ * compile this instead of `__field`. Absent for the simple bare-column form
17
+ * so existing `$agg.sum("col")` payloads stay byte-for-byte identical.
18
+ */
19
+ __expr?: ColumnExpression;
37
20
  };
38
21
  /**
39
22
  * Supported aggregate functions.
40
23
  */
41
- type AggregateFunction = "count" | "sum" | "avg" | "min" | "max" | "first" | "last" | "distinct" | "floor";
24
+ type AggregateFunction = "count" | "countDistinct" | "sum" | "avg" | "min" | "max" | "first" | "last" | "distinct" | "floor";
42
25
  /**
43
26
  * Checks if a value is an abstract aggregate expression.
44
27
  */
@@ -68,23 +51,77 @@ declare const $agg: {
68
51
  */
69
52
  count(): AggregateExpression;
70
53
  /**
71
- * Sum a numeric field across documents in each group.
54
+ * Count the number of distinct values of a field in each group.
55
+ *
56
+ * @param field - The field name to count distinct values of
57
+ * @returns Abstract count-distinct expression
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * query.groupBy("country", {
62
+ * uniqueCities: $agg.countDistinct("city")
63
+ * });
64
+ * ```
65
+ *
66
+ * **Cross-driver.** Translates to:
67
+ * - SQL: `COUNT(DISTINCT city)`
68
+ * - MongoDB: `{ $addToSet: "$city" }` in the `$group` stage, finalized with
69
+ * `{ $size: "$uniqueCities" }` in the renaming `$project` (the standard
70
+ * distinct-count-per-group pattern, since `$size` is not a `$group`
71
+ * accumulator).
72
+ */
73
+ countDistinct(field: string): AggregateExpression;
74
+ /**
75
+ * Sum a numeric field — or a composed arithmetic expression — across
76
+ * documents in each group.
72
77
  *
73
- * @param field - The field name to sum
78
+ * Pass a bare column name to sum a single field (unchanged from v1), or a
79
+ * typed `$expr` node (`$expr.mul` / `$expr.add` / `$expr.sub` / `$expr.div` /
80
+ * `$expr.col` / `$expr.lit`) to sum a computed value such as `price * quantity`.
81
+ *
82
+ * @param input - A field name or a typed `ColumnExpression`
74
83
  * @returns Abstract sum expression
75
84
  *
76
85
  * @example
77
86
  * ```typescript
87
+ * import { $agg, $expr } from "@warlock.js/cascade";
88
+ *
89
+ * // Bare column
90
+ * query.groupBy("type", { totalDuration: $agg.sum("duration") });
91
+ *
92
+ * // Composed expression: SUM(price * quantity)
93
+ * query.groupBy("type", { revenue: $agg.sum($expr.mul("price", "quantity")) });
94
+ * ```
95
+ *
96
+ * Translates to:
97
+ * - SQL: `SUM("duration")` / `SUM(("price" * "quantity"))`
98
+ * - MongoDB: `{ $sum: "$duration" }` / `{ $sum: { $multiply: ["$price", "$quantity"] } }`
99
+ */
100
+ sum(input: ColumnExpressionInput): AggregateExpression;
101
+ /**
102
+ * Sum a raw, driver-native expression escape hatch.
103
+ *
104
+ * Equivalent to `$agg.sum($expr.raw(expression))`. The raw string is emitted
105
+ * verbatim into the generated query — never build it from untrusted input.
106
+ * Reach for the typed {@link sum} form first; this exists only for fragments
107
+ * the typed combinators can't express.
108
+ *
109
+ * @param expression - A raw expression fragment (e.g. `"price * quantity"`)
110
+ * @returns Abstract sum expression wrapping the raw fragment
111
+ *
112
+ * @example
113
+ * ```typescript
78
114
  * query.groupBy("type", {
79
- * totalDuration: $agg.sum("duration")
115
+ * net: $agg.sumRaw("price * quantity * (1 - discount)"),
80
116
  * });
81
117
  * ```
82
118
  *
83
119
  * Translates to:
84
- * - MongoDB: `{ $sum: "$duration" }`
85
- * - SQL: `SUM(duration)`
120
+ * - SQL: `SUM(price * quantity * (1 - discount))`
121
+ * - MongoDB: throws — raw SQL fragments are not portable to a pipeline; use
122
+ * the typed {@link sum} form (or `groupByRaw`) on MongoDB instead.
86
123
  */
87
- sum(field: string): AggregateExpression;
124
+ sumRaw(expression: string): AggregateExpression;
88
125
  /**
89
126
  * Calculate the average value of a field across documents in each group.
90
127
  *
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate-expressions.d.mts","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/expressions/aggregate-expressions.ts"],"mappings":";;AAiCA;;;;;;;;AAIS;AAMT;;;;AAA6B;AAc7B;;;;;;;;AAAmF;AAenF;;;;;;;;KAvCY,mBAAA;EAyLY,kCAvLtB,KAAA,EAAO,iBAAiB,EAqOH;EAnOrB,OAAA;AAAA;;;;KAMU,iBAAA;;;;iBAcI,qBAAA,CAAsB,KAAA,YAAiB,KAAA,IAAS,mBAAmB;;;;;;;cAetE,IAAA;;;;;;;;;;;;;AAgM6B;;;;WA/K/B,mBAAA;;;;;;;;;;;;;;;;;;sBAqBW,mBAAA;;;;;;;;;;;;;;;;;;sBAqBA,mBAAA;;;;;;;;;;;;;;;;;;sBAqBA,mBAAA;;;;;;;;;;;;;;;;;;sBAqBA,mBAAA;;;;;;;;;;;;;;;;;;;;2BAuBK,mBAAA;;;;;;;;;;;;;;;;;;;wBAsBH,mBAAA;;;;;;;;;;;;;;;;;;;;wBAuBA,mBAAA;;;;;;;;;;;;;;;;;;;;uBAuBD,mBAAA;AAAA"}
1
+ {"version":3,"file":"aggregate-expressions.d.mts","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/expressions/aggregate-expressions.ts"],"mappings":";;;AAmFA;;;;;;AAAA,KA/CY,mBAAA;EAqJkB,kCAnJ5B,KAAA,EAAO,iBAAA,EAiMa;EA/LpB,OAAA;EA2OyB;;;;;;EApOzB,MAAA,GAAS,gBAAgB;AAAA;;;;KAMf,iBAAA;;;;iBAeI,qBAAA,CAAsB,KAAA,YAAiB,KAAA,IAAS,mBAAmB;;;;;;;cAetE,IAAA;EAoJS;;;;;;;;;;;;;;;;WAnIX,mBAAA;EAmP+B;;;;;;;;;;;;;;;;;;;;gCA3NV,mBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;aA8BnB,qBAAA,GAAwB,mBAAA;;;;;;;;;;;;;;;;;;;;;;;;8BA+BP,mBAAA;;;;;;;;;;;;;;;;;;sBAyBR,mBAAA;;;;;;;;;;;;;;;;;;sBAqBA,mBAAA;;;;;;;;;;;;;;;;;;sBAqBA,mBAAA;;;;;;;;;;;;;;;;;;;;2BAuBK,mBAAA;;;;;;;;;;;;;;;;;;;wBAsBH,mBAAA;;;;;;;;;;;;;;;;;;;;wBAuBA,mBAAA;;;;;;;;;;;;;;;;;;;;uBAuBD,mBAAA;AAAA"}
@@ -35,26 +35,99 @@ const $agg = {
35
35
  };
36
36
  },
37
37
  /**
38
- * Sum a numeric field across documents in each group.
38
+ * Count the number of distinct values of a field in each group.
39
39
  *
40
- * @param field - The field name to sum
40
+ * @param field - The field name to count distinct values of
41
+ * @returns Abstract count-distinct expression
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * query.groupBy("country", {
46
+ * uniqueCities: $agg.countDistinct("city")
47
+ * });
48
+ * ```
49
+ *
50
+ * **Cross-driver.** Translates to:
51
+ * - SQL: `COUNT(DISTINCT city)`
52
+ * - MongoDB: `{ $addToSet: "$city" }` in the `$group` stage, finalized with
53
+ * `{ $size: "$uniqueCities" }` in the renaming `$project` (the standard
54
+ * distinct-count-per-group pattern, since `$size` is not a `$group`
55
+ * accumulator).
56
+ */
57
+ countDistinct(field) {
58
+ return {
59
+ __agg: "countDistinct",
60
+ __field: field
61
+ };
62
+ },
63
+ /**
64
+ * Sum a numeric field — or a composed arithmetic expression — across
65
+ * documents in each group.
66
+ *
67
+ * Pass a bare column name to sum a single field (unchanged from v1), or a
68
+ * typed `$expr` node (`$expr.mul` / `$expr.add` / `$expr.sub` / `$expr.div` /
69
+ * `$expr.col` / `$expr.lit`) to sum a computed value such as `price * quantity`.
70
+ *
71
+ * @param input - A field name or a typed `ColumnExpression`
41
72
  * @returns Abstract sum expression
42
73
  *
43
74
  * @example
44
75
  * ```typescript
76
+ * import { $agg, $expr } from "@warlock.js/cascade";
77
+ *
78
+ * // Bare column
79
+ * query.groupBy("type", { totalDuration: $agg.sum("duration") });
80
+ *
81
+ * // Composed expression: SUM(price * quantity)
82
+ * query.groupBy("type", { revenue: $agg.sum($expr.mul("price", "quantity")) });
83
+ * ```
84
+ *
85
+ * Translates to:
86
+ * - SQL: `SUM("duration")` / `SUM(("price" * "quantity"))`
87
+ * - MongoDB: `{ $sum: "$duration" }` / `{ $sum: { $multiply: ["$price", "$quantity"] } }`
88
+ */
89
+ sum(input) {
90
+ if (typeof input === "string") return {
91
+ __agg: "sum",
92
+ __field: input
93
+ };
94
+ return {
95
+ __agg: "sum",
96
+ __field: null,
97
+ __expr: input
98
+ };
99
+ },
100
+ /**
101
+ * Sum a raw, driver-native expression escape hatch.
102
+ *
103
+ * Equivalent to `$agg.sum($expr.raw(expression))`. The raw string is emitted
104
+ * verbatim into the generated query — never build it from untrusted input.
105
+ * Reach for the typed {@link sum} form first; this exists only for fragments
106
+ * the typed combinators can't express.
107
+ *
108
+ * @param expression - A raw expression fragment (e.g. `"price * quantity"`)
109
+ * @returns Abstract sum expression wrapping the raw fragment
110
+ *
111
+ * @example
112
+ * ```typescript
45
113
  * query.groupBy("type", {
46
- * totalDuration: $agg.sum("duration")
114
+ * net: $agg.sumRaw("price * quantity * (1 - discount)"),
47
115
  * });
48
116
  * ```
49
117
  *
50
118
  * Translates to:
51
- * - MongoDB: `{ $sum: "$duration" }`
52
- * - SQL: `SUM(duration)`
119
+ * - SQL: `SUM(price * quantity * (1 - discount))`
120
+ * - MongoDB: throws — raw SQL fragments are not portable to a pipeline; use
121
+ * the typed {@link sum} form (or `groupByRaw`) on MongoDB instead.
53
122
  */
54
- sum(field) {
123
+ sumRaw(expression) {
55
124
  return {
56
125
  __agg: "sum",
57
- __field: field
126
+ __field: null,
127
+ __expr: {
128
+ __expr: "raw",
129
+ expression
130
+ }
58
131
  };
59
132
  },
60
133
  /**