Skip to content

Commit

Permalink
feat: Add simple queries feature flag (#660)
Browse files Browse the repository at this point in the history
  • Loading branch information
BobdenOs committed May 29, 2024
1 parent 5b07296 commit 3335202
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 13 deletions.
43 changes: 33 additions & 10 deletions db-service/lib/cqn2sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,18 +257,41 @@ class CQN2SQLRenderer {
const SELECT = q.SELECT
if (!SELECT.columns) return sql

let cols = SELECT.columns.map(x => {
const name = this.column_name(x)
let col = `'$."${name}"',${this.output_converter4(x.element, this.quote(name))}`
if (x.SELECT?.count) {
// Return both the sub select and the count for @odata.count
const qc = cds.ql.clone(x, { columns: [{ func: 'count' }], one: 1, limit: 0, orderBy: 0 })
return [col, `'${name}@odata.count',${this.expr(qc)}`]
const isRoot = SELECT.expand === 'root'
const isSimple = cds.env.features.sql_simple_queries &&
isRoot && // Simple queries are only allowed to have a root
!ObjectKeys(q.elements).some(e =>
q.elements[e].type === 'cds.Boolean' || // REVISIT: Booleans require json for sqlite
q.elements[e].isAssociation || // Indicates columns contains an expand
q.elements[e].$assocExpand || // REVISIT: sometimes associations are structs
q.elements[e].items // Array types require to be inlined with a json result
)

let cols = SELECT.columns.map(isSimple
? x => {
const name = this.column_name(x)
const escaped = `${name.replace(/"/g, '""')}`
let col = `${this.output_converter4(x.element, this.quote(name))} AS "${escaped}"`
if (x.SELECT?.count) {
// Return both the sub select and the count for @odata.count
const qc = cds.ql.clone(x, { columns: [{ func: 'count' }], one: 1, limit: 0, orderBy: 0 })
return [col, `${this.expr(qc)} AS "${escaped}@odata.count"`]
}
return col
}
return col
}).flat()
: x => {
const name = this.column_name(x)
const escaped = `${name.replace(/"/g, '""')}`
let col = `'$."${escaped}"',${this.output_converter4(x.element, this.quote(name))}`
if (x.SELECT?.count) {
// Return both the sub select and the count for @odata.count
const qc = cds.ql.clone(x, { columns: [{ func: 'count' }], one: 1, limit: 0, orderBy: 0 })
return [col, `'$."${escaped}@odata.count"',${this.expr(qc)}`]
}
return col
}).flat()

const isRoot = SELECT.expand === 'root'
if (isSimple) return `SELECT ${cols} FROM (${sql})`

// Prevent SQLite from hitting function argument limit of 100
let obj = "'{}'"
Expand Down
19 changes: 16 additions & 3 deletions postgres/lib/PostgresService.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,8 @@ GROUP BY k
return super.column_alias4(x, q)
}

SELECT_expand({ SELECT }, sql) {
SELECT_expand(q, sql) {
const { SELECT } = q
if (!SELECT.columns) return sql
const queryAlias = this.quote(SELECT.from?.as || (SELECT.expand === 'root' && 'root'))
const cols = SELECT.columns.map(x => {
Expand All @@ -385,10 +386,22 @@ GROUP BY k
}
return col
})
const isRoot = SELECT.expand === 'root'
const isSimple = cds.env.features.sql_simple_queries &&
isRoot && // Simple queries are only allowed to have a root
!Object.keys(q.elements).some(e =>
q.elements[e].isAssociation || // Indicates columns contains an expand
q.elements[e].$assocExpand || // REVISIT: sometimes associations are structs
q.elements[e].items // Array types require to be inlined with a json result
)

const subQuery = `SELECT ${cols} FROM (${sql}) as ${queryAlias}`
if (isSimple) return subQuery

// REVISIT: Remove SELECT ${cols} by adjusting SELECT_columns
let obj = `to_jsonb(${queryAlias}.*)`
return `SELECT ${SELECT.one || SELECT.expand === 'root' ? obj : `coalesce(jsonb_agg (${obj}),'[]'::jsonb)`
} as _json_ FROM (SELECT ${cols} FROM (${sql}) as ${queryAlias}) as ${queryAlias}`
return `SELECT ${SELECT.one || isRoot ? obj : `coalesce(jsonb_agg (${obj}),'[]'::jsonb)`
} as _json_ FROM (${subQuery}) as ${queryAlias}`
}

doubleQuote(name) {
Expand Down

0 comments on commit 3335202

Please sign in to comment.