Skip to content

Commit

Permalink
fix: WithSchemaPlugin is adding schema to table function arguments …
Browse files Browse the repository at this point in the history
…and causes db errors in `json_agg` and `to_json`. (#827)
  • Loading branch information
igalklebanov authored Jan 11, 2025
1 parent 3192122 commit 95f1b02
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 0 deletions.
41 changes: 41 additions & 0 deletions src/plugin/with-schema/with-schema-transformer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { AggregateFunctionNode } from '../../operation-node/aggregate-function-node.js'
import { AliasNode } from '../../operation-node/alias-node.js'
import { FunctionNode } from '../../operation-node/function-node.js'
import { IdentifierNode } from '../../operation-node/identifier-node.js'
import { OperationNodeTransformer } from '../../operation-node/operation-node-transformer.js'
import { OperationNode } from '../../operation-node/operation-node.js'
Expand Down Expand Up @@ -34,6 +36,11 @@ const ROOT_OPERATION_NODES: Record<RootOperationNode['kind'], true> = freeze({
MergeQueryNode: true,
})

const SCHEMALESS_FUNCTIONS: Record<string, true> = {
json_agg: true,
to_json: true,
}

export class WithSchemaTransformer extends OperationNodeTransformer {
readonly #schema: string
readonly #schemableIds = new Set<string>()
Expand Down Expand Up @@ -105,6 +112,40 @@ export class WithSchemaTransformer extends OperationNodeTransformer {
}
}

protected override transformAggregateFunction(
node: AggregateFunctionNode,
): AggregateFunctionNode {
return {
...super.transformAggregateFunction({ ...node, aggregated: [] }),
aggregated: this.#transformTableArgsWithoutSchemas(node, 'aggregated'),
}
}

protected override transformFunction(node: FunctionNode): FunctionNode {
return {
...super.transformFunction({ ...node, arguments: [] }),
arguments: this.#transformTableArgsWithoutSchemas(node, 'arguments'),
}
}

#transformTableArgsWithoutSchemas<
A extends string,
N extends { func: string } & {
[K in A]: readonly OperationNode[]
},
>(node: N, argsKey: A): readonly OperationNode[] {
return SCHEMALESS_FUNCTIONS[node.func]
? node[argsKey].map((arg) =>
!TableNode.is(arg) || arg.table.schema
? this.transformNode(arg)
: {
...arg,
table: this.transformIdentifier(arg.table.identifier),
},
)
: this.transformNodeList(node[argsKey])
}

#isRootOperationNode(node: OperationNode): node is RootOperationNode {
return node.kind in ROOT_OPERATION_NODES
}
Expand Down
47 changes: 47 additions & 0 deletions test/node/src/with-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,53 @@ for (const dialect of DIALECTS.filter(

await query.execute()
})

if (dialect === 'postgres') {
it('should not add schema for json_agg parameters', async () => {
const query = ctx.db
.withSchema('mammals')
.selectFrom('pet')
.select((eb) => [
eb.fn.jsonAgg('pet').as('one'),
eb.fn.jsonAgg(eb.table('pet')).as('two'),
eb.fn.jsonAgg('pet').orderBy('pet.name', 'desc').as('three'),
])

testSql(query, dialect, {
postgres: {
sql: 'select json_agg("pet") as "one", json_agg("pet") as "two", json_agg("pet" order by "mammals"."pet"."name" desc) as "three" from "mammals"."pet"',
parameters: [],
},
mysql: NOT_SUPPORTED,
mssql: NOT_SUPPORTED,
sqlite: NOT_SUPPORTED,
})

await query.execute()
})

it('should not add schema for to_json parameters', async () => {
const query = ctx.db
.withSchema('mammals')
.selectFrom('pet')
.select((eb) => [
eb.fn.toJson('pet').as('one'),
eb.fn.toJson(eb.table('pet')).as('two'),
])

testSql(query, dialect, {
postgres: {
sql: 'select to_json("pet") as "one", to_json("pet") as "two" from "mammals"."pet"',
parameters: [],
},
mysql: NOT_SUPPORTED,
mssql: NOT_SUPPORTED,
sqlite: NOT_SUPPORTED,
})

await query.execute()
})
}
})

describe('insert into', () => {
Expand Down

0 comments on commit 95f1b02

Please sign in to comment.