Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
}${
nullsFirst === true
? sql.fragment` NULLS FIRST`
: nullsFirst === false
? sql.fragment` NULLS LAST`
: sql.blank
}`
),
","
)}`
: sql.blank
}
${(isSafeInteger(limit) && sql.fragment`limit ${sql.literal(limit)}`) ||
sql.blank}
${(offset && sql.fragment`offset ${sql.literal(offset)}`) || sql.blank}`;
if (flip) {
const flipAlias = Symbol();
fragment = sql.fragment`\
with ${sql.identifier(flipAlias)} as (
${fragment}
)
select *
from ${sql.identifier(flipAlias)}
order by (row_number() over (partition by 1)) desc`; /* We don't need to factor useAsterisk into this row_number() usage */
}
if (useAsterisk) {
/*
* NOTE[useAsterisk/row_number]: since LIMIT/OFFSET is inside this
* subquery, row_number() outside of this subquery WON'T include the
* offset. We must add it back wherever row_number() is used.
*/
const sqlCommon = sql.fragment`\
${sqlCommonUnbounded}
where ${queryBuilder.buildWhereClause(!invert, invert, options)}`;
/*
* Since the offset makes the diagram asymmetric, if offset === 0
* then the diagram is symmetric and things are simplified a little.
*/
const isForwardOrSymmetric = !invert || offset === 0;
if (!isForwardOrSymmetric) {
assert(invert);
assert(offset > 0);
// We're looking for a previous page, and there's an offset, so lets just
// assume there's a previous page where offset is smaller.
return sql.literal(true);
} else if (canHaveCursorInWhere) {
assert(isForwardOrSymmetric);
if (!queryHasBefore && !queryHasFirst) {
assert(isForwardOrSymmetric);
// There can be no next page since there's no upper bound
return sql.literal(false);
} else if (queryHasBefore && !queryHasFirst) {
/*
* We invert the upper buildWhereBoundClause to only represent the data
* after `before`, then check if there's at least one record in that set.
*
* This only works if the `before` cursor can be represented in the
* SQL WHERE clause, otherwise we're doing limit/offset pagination
* which requires different logic. It also only works if there's no
* `first` clause, otherwise there could be a next page before the
* `before` clause.
* then the diagram is symmetric and things are simplified a little.
*/
const isForwardOrSymmetric = !invert || offset === 0;
if (!isForwardOrSymmetric) {
assert(invert);
assert(offset > 0);
// We're looking for a previous page, and there's an offset, so lets just
// assume there's a previous page where offset is smaller.
return sql.literal(true);
} else if (canHaveCursorInWhere) {
assert(isForwardOrSymmetric);
if (!queryHasBefore && !queryHasFirst) {
assert(isForwardOrSymmetric);
// There can be no next page since there's no upper bound
return sql.literal(false);
} else if (queryHasBefore && !queryHasFirst) {
/*
* We invert the upper buildWhereBoundClause to only represent the data
* after `before`, then check if there's at least one record in that set.
*
* This only works if the `before` cursor can be represented in the
* SQL WHERE clause, otherwise we're doing limit/offset pagination
* which requires different logic. It also only works if there's no
* `first` clause, otherwise there could be a next page before the
* `before` clause.
*/
return sql.fragment`\
exists(
${sqlCommonUnbounded}
where ${queryBuilder.buildWhereClause(false, false, options)}
and not (${queryBuilder.buildWhereBoundClause(invert)})
} else if (invert) {
assert(offset === 0);
// Paginating backwards and there's no offset (which factors in before/after), so there's no previous page.
return sql.fragment`false`;
} else {
assert(!invert);
/*
* We're paginating forwards; either there's a before, there's a first,
* or both.
*
* We want to see if there's more than limit+offset records in sqlCommon.
*/
return sql.fragment`\
exists(
${sqlCommon}
offset ${sql.literal(limit + offset)}
)`;
}
}
}
const getPgCursorPrefix = (): SQL[] =>
buildWhereBoundClause(isLower: boolean): SQL {
this.lock("whereBound");
const clauses = this.compiledData.whereBound[isLower ? "lower" : "upper"];
if (clauses.length) {
return sql.fragment`(${sql.join(clauses, ") and (")})`;
} else {
return sql.literal(true);
}
}
buildWhereClause(
: queryBuilder.data.cursorPrefix.map(val => sql.literal(val));
if (
", "
)})`;
} else {
return sql.fragment`json_build_array(${sql.join(
getPgCursorPrefix(),
", "
)}, ${
/*
* NOTE[useAsterisk/row_number]: If we have useAsterisk then the
* query with limit offset is in a subquery, so our row_number()
* call doesn't know about it. Here we add the offset back in
* again. See matching NOTE in QueryBuilder.js.
*/
options.useAsterisk
? sql.fragment`${sql.literal(
queryBuilder.getFinalOffset() || 0
)} + `
: sql.fragment``
}(row_number() over (partition by 1)))`;
}
}
);
([expr, alias]) =>
sql.fragment`${sql.literal(alias)}::text, ${expr}`
),