relq 1.0.69 → 1.0.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cli/adapters/registry.cjs +6 -1
- package/dist/cjs/cli/utils/pool-manager.cjs +5 -1
- package/dist/cjs/core/helpers/ConnectedSelectBuilder.cjs +7 -0
- package/dist/cjs/select/select-builder.cjs +11 -1
- package/dist/esm/cli/adapters/registry.js +6 -1
- package/dist/esm/cli/utils/pool-manager.js +5 -1
- package/dist/esm/core/helpers/ConnectedSelectBuilder.js +7 -0
- package/dist/esm/select/select-builder.js +11 -1
- package/dist/index.d.ts +257 -46
- package/package.json +3 -1
|
@@ -149,7 +149,12 @@ function getLoadedDialects() {
|
|
|
149
149
|
.map(([dialect]) => dialect);
|
|
150
150
|
}
|
|
151
151
|
async function preloadAdapters(dialects) {
|
|
152
|
-
await Promise.
|
|
152
|
+
const results = await Promise.allSettled(dialects.map(dialect => getAdapter(dialect)));
|
|
153
|
+
const failures = results.filter((r) => r.status === 'rejected');
|
|
154
|
+
if (failures.length > 0) {
|
|
155
|
+
const messages = failures.map(f => f.reason?.message || String(f.reason)).join('; ');
|
|
156
|
+
throw new Error(`Failed to load ${failures.length} adapter(s): ${messages}`);
|
|
157
|
+
}
|
|
153
158
|
}
|
|
154
159
|
async function preloadFamily(family) {
|
|
155
160
|
registerDefaultAdapters();
|
|
@@ -146,7 +146,11 @@ async function closeAllPools() {
|
|
|
146
146
|
closePromises.push(entry.pool.end());
|
|
147
147
|
pools.delete(key);
|
|
148
148
|
}
|
|
149
|
-
await Promise.
|
|
149
|
+
const results = await Promise.allSettled(closePromises);
|
|
150
|
+
const failures = results.filter((r) => r.status === 'rejected');
|
|
151
|
+
if (failures.length > 0) {
|
|
152
|
+
console.warn(`Warning: ${failures.length} pool(s) failed to close gracefully`);
|
|
153
|
+
}
|
|
150
154
|
}
|
|
151
155
|
function getActivePoolCount() {
|
|
152
156
|
return pools.size;
|
|
@@ -52,6 +52,13 @@ class ConnectedSelectBuilder {
|
|
|
52
52
|
this.builder.orderBy(dbColumn, direction);
|
|
53
53
|
return this;
|
|
54
54
|
}
|
|
55
|
+
orderByNulls(column, direction, nulls) {
|
|
56
|
+
const dbColumn = this.relq[methods_1.INTERNAL].hasColumnMapping()
|
|
57
|
+
? Object.keys(this.relq[methods_1.INTERNAL].transformToDbColumns(this.tableName, { [column]: true }))[0]
|
|
58
|
+
: column;
|
|
59
|
+
this.builder.orderByNulls(dbColumn, direction, nulls);
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
55
62
|
limit(count) {
|
|
56
63
|
this.builder.limit(count);
|
|
57
64
|
return this;
|
|
@@ -114,6 +114,10 @@ class SelectBuilder {
|
|
|
114
114
|
this.orderByColumns.push({ column, direction });
|
|
115
115
|
return this;
|
|
116
116
|
}
|
|
117
|
+
orderByNulls(column, direction, nulls) {
|
|
118
|
+
this.orderByColumns.push({ column, direction, nulls });
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
117
121
|
orderAsc(column) {
|
|
118
122
|
return this.orderBy(column, 'ASC');
|
|
119
123
|
}
|
|
@@ -325,7 +329,13 @@ class SelectBuilder {
|
|
|
325
329
|
}
|
|
326
330
|
if (this.orderByColumns.length > 0) {
|
|
327
331
|
query += ' ORDER BY ' + this.orderByColumns
|
|
328
|
-
.map(order =>
|
|
332
|
+
.map(order => {
|
|
333
|
+
let sql = (0, pg_format_1.default)('%I %s', order.column, order.direction);
|
|
334
|
+
if (order.nulls) {
|
|
335
|
+
sql += ` NULLS ${order.nulls}`;
|
|
336
|
+
}
|
|
337
|
+
return sql;
|
|
338
|
+
})
|
|
329
339
|
.join(', ');
|
|
330
340
|
}
|
|
331
341
|
if (this.limitValue !== undefined) {
|
|
@@ -101,7 +101,12 @@ export function getLoadedDialects() {
|
|
|
101
101
|
.map(([dialect]) => dialect);
|
|
102
102
|
}
|
|
103
103
|
export async function preloadAdapters(dialects) {
|
|
104
|
-
await Promise.
|
|
104
|
+
const results = await Promise.allSettled(dialects.map(dialect => getAdapter(dialect)));
|
|
105
|
+
const failures = results.filter((r) => r.status === 'rejected');
|
|
106
|
+
if (failures.length > 0) {
|
|
107
|
+
const messages = failures.map(f => f.reason?.message || String(f.reason)).join('; ');
|
|
108
|
+
throw new Error(`Failed to load ${failures.length} adapter(s): ${messages}`);
|
|
109
|
+
}
|
|
105
110
|
}
|
|
106
111
|
export async function preloadFamily(family) {
|
|
107
112
|
registerDefaultAdapters();
|
|
@@ -104,7 +104,11 @@ export async function closeAllPools() {
|
|
|
104
104
|
closePromises.push(entry.pool.end());
|
|
105
105
|
pools.delete(key);
|
|
106
106
|
}
|
|
107
|
-
await Promise.
|
|
107
|
+
const results = await Promise.allSettled(closePromises);
|
|
108
|
+
const failures = results.filter((r) => r.status === 'rejected');
|
|
109
|
+
if (failures.length > 0) {
|
|
110
|
+
console.warn(`Warning: ${failures.length} pool(s) failed to close gracefully`);
|
|
111
|
+
}
|
|
108
112
|
}
|
|
109
113
|
export function getActivePoolCount() {
|
|
110
114
|
return pools.size;
|
|
@@ -49,6 +49,13 @@ export class ConnectedSelectBuilder {
|
|
|
49
49
|
this.builder.orderBy(dbColumn, direction);
|
|
50
50
|
return this;
|
|
51
51
|
}
|
|
52
|
+
orderByNulls(column, direction, nulls) {
|
|
53
|
+
const dbColumn = this.relq[INTERNAL].hasColumnMapping()
|
|
54
|
+
? Object.keys(this.relq[INTERNAL].transformToDbColumns(this.tableName, { [column]: true }))[0]
|
|
55
|
+
: column;
|
|
56
|
+
this.builder.orderByNulls(dbColumn, direction, nulls);
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
52
59
|
limit(count) {
|
|
53
60
|
this.builder.limit(count);
|
|
54
61
|
return this;
|
|
@@ -108,6 +108,10 @@ export class SelectBuilder {
|
|
|
108
108
|
this.orderByColumns.push({ column, direction });
|
|
109
109
|
return this;
|
|
110
110
|
}
|
|
111
|
+
orderByNulls(column, direction, nulls) {
|
|
112
|
+
this.orderByColumns.push({ column, direction, nulls });
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
111
115
|
orderAsc(column) {
|
|
112
116
|
return this.orderBy(column, 'ASC');
|
|
113
117
|
}
|
|
@@ -319,7 +323,13 @@ export class SelectBuilder {
|
|
|
319
323
|
}
|
|
320
324
|
if (this.orderByColumns.length > 0) {
|
|
321
325
|
query += ' ORDER BY ' + this.orderByColumns
|
|
322
|
-
.map(order =>
|
|
326
|
+
.map(order => {
|
|
327
|
+
let sql = format('%I %s', order.column, order.direction);
|
|
328
|
+
if (order.nulls) {
|
|
329
|
+
sql += ` NULLS ${order.nulls}`;
|
|
330
|
+
}
|
|
331
|
+
return sql;
|
|
332
|
+
})
|
|
323
333
|
.join(', ');
|
|
324
334
|
}
|
|
325
335
|
if (this.limitValue !== undefined) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1676,6 +1676,26 @@ export declare class SelectBuilder {
|
|
|
1676
1676
|
groupBy(...columns: string[]): SelectBuilder;
|
|
1677
1677
|
having(callback: (builder: TypedConditionBuilder<any>) => TypedConditionBuilder<any>): SelectBuilder;
|
|
1678
1678
|
orderBy(column: string, direction?: "ASC" | "DESC"): SelectBuilder;
|
|
1679
|
+
/**
|
|
1680
|
+
* Order by with explicit NULLS FIRST or NULLS LAST placement.
|
|
1681
|
+
* PostgreSQL-specific ordering control for NULL values.
|
|
1682
|
+
*
|
|
1683
|
+
* @param column - Column to order by
|
|
1684
|
+
* @param direction - 'ASC' or 'DESC'
|
|
1685
|
+
* @param nulls - 'FIRST' puts NULLs at the beginning, 'LAST' puts them at the end
|
|
1686
|
+
*
|
|
1687
|
+
* @example
|
|
1688
|
+
* ```typescript
|
|
1689
|
+
* // NULLs at end for ascending order (default PostgreSQL behavior for DESC)
|
|
1690
|
+
* builder.orderByNulls('priority', 'ASC', 'LAST')
|
|
1691
|
+
* // SQL: ORDER BY "priority" ASC NULLS LAST
|
|
1692
|
+
*
|
|
1693
|
+
* // NULLs at start for descending order
|
|
1694
|
+
* builder.orderByNulls('created_at', 'DESC', 'FIRST')
|
|
1695
|
+
* // SQL: ORDER BY "created_at" DESC NULLS FIRST
|
|
1696
|
+
* ```
|
|
1697
|
+
*/
|
|
1698
|
+
orderByNulls(column: string, direction: "ASC" | "DESC", nulls: "FIRST" | "LAST"): SelectBuilder;
|
|
1679
1699
|
orderAsc(column: string): SelectBuilder;
|
|
1680
1700
|
orderDesc(column: string): SelectBuilder;
|
|
1681
1701
|
limit(count: number): SelectBuilder;
|
|
@@ -7706,9 +7726,31 @@ declare class ConnectedDeleteBuilder<TTable = any> {
|
|
|
7706
7726
|
run(): Promise<number>;
|
|
7707
7727
|
run(withMetadata: true): Promise<RelqRun>;
|
|
7708
7728
|
/**
|
|
7709
|
-
* Set RETURNING clause - returns executor with typed
|
|
7710
|
-
* Use '*' to return all columns, or specify column names
|
|
7711
|
-
* Pass null to skip RETURNING clause (useful for conditional returning)
|
|
7729
|
+
* Set RETURNING clause - returns executor with typed results.
|
|
7730
|
+
* Use '*' to return all columns, or specify column names.
|
|
7731
|
+
* Pass null to skip RETURNING clause (useful for conditional returning).
|
|
7732
|
+
*
|
|
7733
|
+
* **Dialect Support:**
|
|
7734
|
+
* | Dialect | Supported | Notes |
|
|
7735
|
+
* |-------------|-----------|-------|
|
|
7736
|
+
* | PostgreSQL | ✅ | Native RETURNING support |
|
|
7737
|
+
* | CockroachDB | ✅ | Native RETURNING support |
|
|
7738
|
+
* | Nile | ✅ | Native RETURNING support |
|
|
7739
|
+
* | AWS DSQL | ✅ | Native RETURNING support |
|
|
7740
|
+
* | MySQL | ❌ | No RETURNING support |
|
|
7741
|
+
* | SQLite | ✅ | Native RETURNING support (3.35+) |
|
|
7742
|
+
*
|
|
7743
|
+
* @param columns - '*' for all, column name(s), or null to skip
|
|
7744
|
+
* @throws {RelqConfigError} If dialect doesn't support RETURNING
|
|
7745
|
+
*
|
|
7746
|
+
* @example
|
|
7747
|
+
* ```typescript
|
|
7748
|
+
* // Get deleted row data
|
|
7749
|
+
* const deleted = await db.table.users.delete()
|
|
7750
|
+
* .where(q => q.equal('id', userId))
|
|
7751
|
+
* .returning('*')
|
|
7752
|
+
* .run()
|
|
7753
|
+
* ```
|
|
7712
7754
|
*/
|
|
7713
7755
|
returning(columns: null): this;
|
|
7714
7756
|
returning(columns: "*"): ReturningExecutor<TTable extends {
|
|
@@ -7745,31 +7787,60 @@ declare class ConnectedInsertBuilder<TTable = any> {
|
|
|
7745
7787
|
clear(): this;
|
|
7746
7788
|
get total(): number;
|
|
7747
7789
|
/**
|
|
7748
|
-
* ON CONFLICT DO NOTHING - skip insert if conflict occurs
|
|
7790
|
+
* ON CONFLICT DO NOTHING - skip insert if conflict occurs.
|
|
7791
|
+
* The insert is silently skipped when a unique constraint violation occurs.
|
|
7792
|
+
*
|
|
7793
|
+
* **Dialect Support:**
|
|
7794
|
+
* | Dialect | Supported | Notes |
|
|
7795
|
+
* |-------------|-----------|-------|
|
|
7796
|
+
* | PostgreSQL | ✅ | Native ON CONFLICT support |
|
|
7797
|
+
* | CockroachDB | ✅ | Native ON CONFLICT support |
|
|
7798
|
+
* | Nile | ✅ | Native ON CONFLICT support |
|
|
7799
|
+
* | AWS DSQL | ✅ | Native ON CONFLICT support |
|
|
7800
|
+
* | MySQL | ⚠️ | Uses INSERT IGNORE (different semantics) |
|
|
7801
|
+
* | SQLite | ✅ | Native ON CONFLICT support |
|
|
7802
|
+
*
|
|
7749
7803
|
* @param columns - Column(s) for conflict detection (array or multiple args)
|
|
7804
|
+
*
|
|
7750
7805
|
* @example
|
|
7751
7806
|
* ```typescript
|
|
7752
|
-
*
|
|
7753
|
-
* .
|
|
7754
|
-
*
|
|
7807
|
+
* // Single column conflict
|
|
7808
|
+
* await db.table.users.insert({ email: 'test@example.com' })
|
|
7809
|
+
* .doNothing('email')
|
|
7810
|
+
* .execute()
|
|
7811
|
+
*
|
|
7812
|
+
* // Multiple columns (composite unique constraint)
|
|
7813
|
+
* await db.table.userRoles.insert({ userId, roleId })
|
|
7814
|
+
* .doNothing('userId', 'roleId')
|
|
7815
|
+
* .execute()
|
|
7755
7816
|
* ```
|
|
7756
7817
|
*/
|
|
7757
7818
|
doNothing(columns: ColumnName<TTable>[]): this;
|
|
7758
7819
|
doNothing(...columns: ColumnName<TTable>[]): this;
|
|
7759
7820
|
/**
|
|
7760
|
-
* ON CONFLICT DO UPDATE - update row if conflict occurs
|
|
7761
|
-
* 100% type-safe, no raw SQL needed
|
|
7821
|
+
* ON CONFLICT DO UPDATE - update row if conflict occurs (UPSERT).
|
|
7822
|
+
* 100% type-safe, no raw SQL needed.
|
|
7823
|
+
*
|
|
7824
|
+
* **Dialect Support:**
|
|
7825
|
+
* | Dialect | Supported | Notes |
|
|
7826
|
+
* |-------------|-----------|-------|
|
|
7827
|
+
* | PostgreSQL | ✅ | Full EXCLUDED.* support |
|
|
7828
|
+
* | CockroachDB | ✅ | Full EXCLUDED.* support |
|
|
7829
|
+
* | Nile | ✅ | Full EXCLUDED.* support |
|
|
7830
|
+
* | AWS DSQL | ✅ | Full EXCLUDED.* support |
|
|
7831
|
+
* | MySQL | ⚠️ | Uses ON DUPLICATE KEY UPDATE (different syntax) |
|
|
7832
|
+
* | SQLite | ✅ | Uses ON CONFLICT DO UPDATE |
|
|
7762
7833
|
*
|
|
7763
7834
|
* @param columns - Column(s) for conflict detection
|
|
7764
7835
|
* @param values - Values to update (static or expression functions)
|
|
7765
|
-
* @param where - Optional type-safe where clause
|
|
7836
|
+
* @param where - Optional type-safe where clause for conditional updates
|
|
7766
7837
|
*
|
|
7767
7838
|
* @example
|
|
7768
7839
|
* ```typescript
|
|
7769
7840
|
* // Static values
|
|
7770
7841
|
* .doUpdate('email', { status: 'updated' })
|
|
7771
7842
|
*
|
|
7772
|
-
* // Expression functions
|
|
7843
|
+
* // Expression functions - access EXCLUDED and current row values
|
|
7773
7844
|
* .doUpdate('email', {
|
|
7774
7845
|
* balance: (excluded, sql) => sql.add(excluded.balance, 100),
|
|
7775
7846
|
* count: (excluded, sql, row) => sql.increment(1),
|
|
@@ -7780,23 +7851,10 @@ declare class ConnectedInsertBuilder<TTable = any> {
|
|
|
7780
7851
|
* // Multiple conflict columns
|
|
7781
7852
|
* .doUpdate(['email', 'tenantId'], { status: 'updated' })
|
|
7782
7853
|
*
|
|
7783
|
-
* // With type-safe where clause
|
|
7854
|
+
* // With type-safe where clause
|
|
7784
7855
|
* .doUpdate('email', { balance: 100 }, q =>
|
|
7785
7856
|
* q.notEqual('role', 'admin')
|
|
7786
7857
|
* .in('status', ['active', 'pending'])
|
|
7787
|
-
* .or(q => q.isNull('deletedAt').greaterThan('score', 100))
|
|
7788
|
-
* )
|
|
7789
|
-
*
|
|
7790
|
-
* // JSONB conditions in conflict where
|
|
7791
|
-
* .doUpdate('email', { updatedAt: new Date() }, q =>
|
|
7792
|
-
* q.jsonb.hasKey('metadata', 'verified')
|
|
7793
|
-
* .array.contains('tags', ['premium'])
|
|
7794
|
-
* )
|
|
7795
|
-
*
|
|
7796
|
-
* // Regex, fulltext, and more
|
|
7797
|
-
* .doUpdate('email', { status: 'matched' }, q =>
|
|
7798
|
-
* q.regex('code', '^[A-Z]{3}[0-9]+$')
|
|
7799
|
-
* .fulltext.search('description', 'important')
|
|
7800
7858
|
* )
|
|
7801
7859
|
* ```
|
|
7802
7860
|
*/
|
|
@@ -7812,9 +7870,36 @@ declare class ConnectedInsertBuilder<TTable = any> {
|
|
|
7812
7870
|
run(): Promise<number>;
|
|
7813
7871
|
run(withMetadata: true): Promise<RelqRun>;
|
|
7814
7872
|
/**
|
|
7815
|
-
* Set RETURNING clause - returns executor with typed
|
|
7816
|
-
* Use '*' to return all columns, or specify column names
|
|
7817
|
-
* Pass null to skip RETURNING clause (useful for conditional returning)
|
|
7873
|
+
* Set RETURNING clause - returns executor with typed results.
|
|
7874
|
+
* Use '*' to return all columns, or specify column names.
|
|
7875
|
+
* Pass null to skip RETURNING clause (useful for conditional returning).
|
|
7876
|
+
*
|
|
7877
|
+
* **Dialect Support:**
|
|
7878
|
+
* | Dialect | Supported | Notes |
|
|
7879
|
+
* |-------------|-----------|-------|
|
|
7880
|
+
* | PostgreSQL | ✅ | Native RETURNING support |
|
|
7881
|
+
* | CockroachDB | ✅ | Native RETURNING support |
|
|
7882
|
+
* | Nile | ✅ | Native RETURNING support |
|
|
7883
|
+
* | AWS DSQL | ✅ | Native RETURNING support |
|
|
7884
|
+
* | MySQL | ❌ | Use LAST_INSERT_ID() instead |
|
|
7885
|
+
* | SQLite | ✅ | Native RETURNING support (3.35+) |
|
|
7886
|
+
*
|
|
7887
|
+
* @param columns - '*' for all, column name(s), or null to skip
|
|
7888
|
+
* @throws {RelqConfigError} If dialect doesn't support RETURNING
|
|
7889
|
+
*
|
|
7890
|
+
* @example
|
|
7891
|
+
* ```typescript
|
|
7892
|
+
* // Return all columns
|
|
7893
|
+
* const user = await db.table.users.insert({ name: 'John' })
|
|
7894
|
+
* .returning('*')
|
|
7895
|
+
* .run()
|
|
7896
|
+
* // user has all columns from users table
|
|
7897
|
+
*
|
|
7898
|
+
* // Return specific columns
|
|
7899
|
+
* const { id, createdAt } = await db.table.users.insert({ name: 'John' })
|
|
7900
|
+
* .returning(['id', 'createdAt'])
|
|
7901
|
+
* .run()
|
|
7902
|
+
* ```
|
|
7818
7903
|
*/
|
|
7819
7904
|
returning(columns: null): this;
|
|
7820
7905
|
returning(columns: "*"): ReturningExecutor<TTable extends {
|
|
@@ -7855,9 +7940,31 @@ declare class ConnectedUpdateBuilder<TTable = any> {
|
|
|
7855
7940
|
run(): Promise<number>;
|
|
7856
7941
|
run(withMetadata: true): Promise<RelqRun>;
|
|
7857
7942
|
/**
|
|
7858
|
-
* Set RETURNING clause - returns executor with typed
|
|
7859
|
-
* Use '*' to return all columns, or specify column names
|
|
7860
|
-
* Pass null to skip RETURNING clause (useful for conditional returning)
|
|
7943
|
+
* Set RETURNING clause - returns executor with typed results.
|
|
7944
|
+
* Use '*' to return all columns, or specify column names.
|
|
7945
|
+
* Pass null to skip RETURNING clause (useful for conditional returning).
|
|
7946
|
+
*
|
|
7947
|
+
* **Dialect Support:**
|
|
7948
|
+
* | Dialect | Supported | Notes |
|
|
7949
|
+
* |-------------|-----------|-------|
|
|
7950
|
+
* | PostgreSQL | ✅ | Native RETURNING support |
|
|
7951
|
+
* | CockroachDB | ✅ | Native RETURNING support |
|
|
7952
|
+
* | Nile | ✅ | Native RETURNING support |
|
|
7953
|
+
* | AWS DSQL | ✅ | Native RETURNING support |
|
|
7954
|
+
* | MySQL | ❌ | No RETURNING support |
|
|
7955
|
+
* | SQLite | ✅ | Native RETURNING support (3.35+) |
|
|
7956
|
+
*
|
|
7957
|
+
* @param columns - '*' for all, column name(s), or null to skip
|
|
7958
|
+
* @throws {RelqConfigError} If dialect doesn't support RETURNING
|
|
7959
|
+
*
|
|
7960
|
+
* @example
|
|
7961
|
+
* ```typescript
|
|
7962
|
+
* // Return updated row
|
|
7963
|
+
* const user = await db.table.users.update({ score: 100 })
|
|
7964
|
+
* .where(q => q.equal('id', userId))
|
|
7965
|
+
* .returning('*')
|
|
7966
|
+
* .run()
|
|
7967
|
+
* ```
|
|
7861
7968
|
*/
|
|
7862
7969
|
returning(columns: null): this;
|
|
7863
7970
|
returning(columns: "*"): ReturningExecutor<TTable extends {
|
|
@@ -8287,6 +8394,30 @@ declare class ConnectedSelectBuilder<TSchema = any, TTable = any, TColumns exten
|
|
|
8287
8394
|
private setupColumnResolver;
|
|
8288
8395
|
where(callback: (q: TypedConditionBuilder<TTable>) => TypedConditionBuilder<TTable>): this;
|
|
8289
8396
|
orderBy(column: ColumnName<TTable>, direction?: "ASC" | "DESC"): this;
|
|
8397
|
+
/**
|
|
8398
|
+
* Order by with explicit NULLS FIRST or NULLS LAST placement.
|
|
8399
|
+
* PostgreSQL-specific ordering control for NULL values.
|
|
8400
|
+
*
|
|
8401
|
+
* @param column - Column to order by (type-safe)
|
|
8402
|
+
* @param direction - 'ASC' or 'DESC'
|
|
8403
|
+
* @param nulls - 'FIRST' puts NULLs at the beginning, 'LAST' puts them at the end
|
|
8404
|
+
*
|
|
8405
|
+
* @example
|
|
8406
|
+
* ```typescript
|
|
8407
|
+
* // NULLs at end for ascending order
|
|
8408
|
+
* db.table.users.select()
|
|
8409
|
+
* .orderByNulls('deletedAt', 'ASC', 'LAST')
|
|
8410
|
+
* .all()
|
|
8411
|
+
* // SQL: ORDER BY "deleted_at" ASC NULLS LAST
|
|
8412
|
+
*
|
|
8413
|
+
* // NULLs at start for descending order
|
|
8414
|
+
* db.table.posts.select()
|
|
8415
|
+
* .orderByNulls('publishedAt', 'DESC', 'FIRST')
|
|
8416
|
+
* .all()
|
|
8417
|
+
* // SQL: ORDER BY "published_at" DESC NULLS FIRST
|
|
8418
|
+
* ```
|
|
8419
|
+
*/
|
|
8420
|
+
orderByNulls(column: ColumnName<TTable>, direction: "ASC" | "DESC", nulls: "FIRST" | "LAST"): this;
|
|
8290
8421
|
limit(count: number): this;
|
|
8291
8422
|
offset(count: number): this;
|
|
8292
8423
|
groupBy(...columns: ColumnName<TTable>[]): this;
|
|
@@ -8444,6 +8575,20 @@ declare class ConnectedSelectBuilder<TSchema = any, TTable = any, TColumns exten
|
|
|
8444
8575
|
* Uses PostgreSQL LATERAL to efficiently fetch related rows with
|
|
8445
8576
|
* ordering and limiting per parent row.
|
|
8446
8577
|
*
|
|
8578
|
+
* **Dialect Support:**
|
|
8579
|
+
* | Dialect | Supported | Notes |
|
|
8580
|
+
* |-------------|-----------|-------|
|
|
8581
|
+
* | PostgreSQL | ✅ | Native LATERAL support |
|
|
8582
|
+
* | CockroachDB | ✅ | Native LATERAL support |
|
|
8583
|
+
* | Nile | ✅ | Native LATERAL support |
|
|
8584
|
+
* | AWS DSQL | ❌ | Use separate queries |
|
|
8585
|
+
* | MySQL | ❌ | Use separate queries |
|
|
8586
|
+
* | SQLite | ❌ | Use separate queries |
|
|
8587
|
+
*
|
|
8588
|
+
* @param tableOrAlias - Table name or [tableName, alias] tuple
|
|
8589
|
+
* @param callback - Join condition callback: `(on, left, right) => on.equal(...)`
|
|
8590
|
+
* @throws {RelqConfigError} If dialect doesn't support LATERAL joins
|
|
8591
|
+
*
|
|
8447
8592
|
* @example
|
|
8448
8593
|
* ```typescript
|
|
8449
8594
|
* // Get top 5 orders per user
|
|
@@ -8451,7 +8596,7 @@ declare class ConnectedSelectBuilder<TSchema = any, TTable = any, TColumns exten
|
|
|
8451
8596
|
* .joinMany('orders', (on, users, orders) =>
|
|
8452
8597
|
* on.equal(users.id, orders.userId)
|
|
8453
8598
|
* .where(q => q.equal('status', 'completed'))
|
|
8454
|
-
* .orderBy(
|
|
8599
|
+
* .orderBy('createdAt', 'DESC')
|
|
8455
8600
|
* .limit(5)
|
|
8456
8601
|
* )
|
|
8457
8602
|
* .all()
|
|
@@ -8461,7 +8606,7 @@ declare class ConnectedSelectBuilder<TSchema = any, TTable = any, TColumns exten
|
|
|
8461
8606
|
* db.table.users.select()
|
|
8462
8607
|
* .joinMany(['orders', 'recentOrders'], (on, users, orders) =>
|
|
8463
8608
|
* on.equal(users.id, orders.userId)
|
|
8464
|
-
* .orderBy(
|
|
8609
|
+
* .orderBy('createdAt', 'DESC')
|
|
8465
8610
|
* .limit(3)
|
|
8466
8611
|
* )
|
|
8467
8612
|
* // Result: Array<{ ...User, recentOrders: Order[] }>
|
|
@@ -8476,6 +8621,20 @@ declare class ConnectedSelectBuilder<TSchema = any, TTable = any, TColumns exten
|
|
|
8476
8621
|
* Type-safe one-to-many LEFT JOIN using LATERAL subquery.
|
|
8477
8622
|
* Returns an array of related rows (empty array if no matches).
|
|
8478
8623
|
*
|
|
8624
|
+
* **Dialect Support:**
|
|
8625
|
+
* | Dialect | Supported | Notes |
|
|
8626
|
+
* |-------------|-----------|-------|
|
|
8627
|
+
* | PostgreSQL | ✅ | Native LATERAL support |
|
|
8628
|
+
* | CockroachDB | ✅ | Native LATERAL support |
|
|
8629
|
+
* | Nile | ✅ | Native LATERAL support |
|
|
8630
|
+
* | AWS DSQL | ❌ | Use separate queries |
|
|
8631
|
+
* | MySQL | ❌ | Use separate queries |
|
|
8632
|
+
* | SQLite | ❌ | Use separate queries |
|
|
8633
|
+
*
|
|
8634
|
+
* @param tableOrAlias - Table name or [tableName, alias] tuple
|
|
8635
|
+
* @param callback - Join condition callback: `(on, left, right) => on.equal(...)`
|
|
8636
|
+
* @throws {RelqConfigError} If dialect doesn't support LATERAL joins
|
|
8637
|
+
*
|
|
8479
8638
|
* @example
|
|
8480
8639
|
* ```typescript
|
|
8481
8640
|
* // Get all users with their orders (empty array if no orders)
|
|
@@ -8496,16 +8655,31 @@ declare class ConnectedSelectBuilder<TSchema = any, TTable = any, TColumns exten
|
|
|
8496
8655
|
leftJoinMany<TRightKey extends keyof TSchema & string, TResult>(table: TRightKey, callback: JoinManyCallback<TSchema, TTable, TSchema[TRightKey], TRightKey, TResult>): ConnectedSelectBuilder<TSchema, TTable, TColumns, TJoined & NestedJoinMany<TRightKey, TResult>>;
|
|
8497
8656
|
distinct(): this;
|
|
8498
8657
|
/**
|
|
8499
|
-
* DISTINCT ON - select unique rows by specified columns
|
|
8500
|
-
*
|
|
8658
|
+
* DISTINCT ON - select unique rows by specified columns.
|
|
8659
|
+
* Returns only the first row for each distinct combination of columns.
|
|
8660
|
+
*
|
|
8661
|
+
* **Dialect Support:**
|
|
8662
|
+
* | Dialect | Supported | Notes |
|
|
8663
|
+
* |-------------|-----------|-------|
|
|
8664
|
+
* | PostgreSQL | ✅ | Native support |
|
|
8665
|
+
* | CockroachDB | ✅ | Native support |
|
|
8666
|
+
* | Nile | ✅ | Native support |
|
|
8667
|
+
* | AWS DSQL | ❌ | Use GROUP BY instead |
|
|
8668
|
+
* | MySQL | ❌ | Use GROUP BY instead |
|
|
8669
|
+
* | SQLite | ❌ | Use GROUP BY instead |
|
|
8670
|
+
*
|
|
8671
|
+
* @param columns - Columns to select distinct on (must be first in ORDER BY)
|
|
8672
|
+
* @throws {RelqConfigError} If dialect doesn't support DISTINCT ON
|
|
8501
8673
|
*
|
|
8502
8674
|
* @example
|
|
8503
8675
|
* ```typescript
|
|
8676
|
+
* // Get latest log per user
|
|
8504
8677
|
* db.table.logs.select()
|
|
8505
8678
|
* .distinctOn('userId')
|
|
8506
8679
|
* .orderBy('userId')
|
|
8507
8680
|
* .orderBy('createdAt', 'DESC')
|
|
8508
8681
|
* .all()
|
|
8682
|
+
* // SQL: SELECT DISTINCT ON ("user_id") * FROM "logs" ORDER BY "user_id", "created_at" DESC
|
|
8509
8683
|
* ```
|
|
8510
8684
|
*/
|
|
8511
8685
|
distinctOn(...columns: string[]): this;
|
|
@@ -8538,7 +8712,31 @@ declare class ConnectedSelectBuilder<TSchema = any, TTable = any, TColumns exten
|
|
|
8538
8712
|
*/
|
|
8539
8713
|
forUpdate(): this;
|
|
8540
8714
|
/**
|
|
8541
|
-
* FOR UPDATE SKIP LOCKED - skip locked rows (job queue pattern)
|
|
8715
|
+
* FOR UPDATE SKIP LOCKED - skip locked rows (job queue pattern).
|
|
8716
|
+
* Essential for implementing efficient work queues.
|
|
8717
|
+
*
|
|
8718
|
+
* **Dialect Support:**
|
|
8719
|
+
* | Dialect | Supported | Notes |
|
|
8720
|
+
* |-------------|-----------|-------|
|
|
8721
|
+
* | PostgreSQL | ✅ | Native support |
|
|
8722
|
+
* | CockroachDB | ✅ | Native support |
|
|
8723
|
+
* | Nile | ✅ | Native support |
|
|
8724
|
+
* | AWS DSQL | ❌ | Use FOR UPDATE only |
|
|
8725
|
+
* | MySQL | ✅ | Native support (8.0+) |
|
|
8726
|
+
* | SQLite | ❌ | No row locking |
|
|
8727
|
+
*
|
|
8728
|
+
* @throws {RelqConfigError} If dialect doesn't support SKIP LOCKED
|
|
8729
|
+
*
|
|
8730
|
+
* @example
|
|
8731
|
+
* ```typescript
|
|
8732
|
+
* // Job queue pattern - claim next available job
|
|
8733
|
+
* const job = await db.table.jobs.select()
|
|
8734
|
+
* .where(q => q.equal('status', 'pending'))
|
|
8735
|
+
* .orderBy('createdAt')
|
|
8736
|
+
* .limit(1)
|
|
8737
|
+
* .forUpdateSkipLocked()
|
|
8738
|
+
* .get()
|
|
8739
|
+
* ```
|
|
8542
8740
|
*/
|
|
8543
8741
|
forUpdateSkipLocked(): this;
|
|
8544
8742
|
/**
|
|
@@ -8583,26 +8781,39 @@ declare class ConnectedSelectBuilder<TSchema = any, TTable = any, TColumns exten
|
|
|
8583
8781
|
*/
|
|
8584
8782
|
value<T = any>(column: string): Promise<T | null>;
|
|
8585
8783
|
/**
|
|
8586
|
-
* Iterate through query results row-by-row using PostgreSQL cursors
|
|
8587
|
-
* Efficient for large datasets as it doesn't load all data into memory
|
|
8784
|
+
* Iterate through query results row-by-row using PostgreSQL cursors.
|
|
8785
|
+
* Efficient for large datasets as it doesn't load all data into memory.
|
|
8786
|
+
*
|
|
8787
|
+
* **Dialect Support:**
|
|
8788
|
+
* | Dialect | Supported | Notes |
|
|
8789
|
+
* |-------------|-----------|-------|
|
|
8790
|
+
* | PostgreSQL | ✅ | Native cursor support |
|
|
8791
|
+
* | CockroachDB | ✅ | Native cursor support |
|
|
8792
|
+
* | Nile | ✅ | Native cursor support |
|
|
8793
|
+
* | AWS DSQL | ❌ | Use pagination() instead |
|
|
8794
|
+
* | MySQL | ❌ | Use pagination() instead |
|
|
8795
|
+
* | SQLite | ❌ | Use pagination() instead |
|
|
8588
8796
|
*
|
|
8589
|
-
* @param callback - Async function called for each row
|
|
8797
|
+
* @param callback - Async function called for each row. Return `false` to stop iteration.
|
|
8590
8798
|
* @param options - Optional configuration
|
|
8591
8799
|
* @param options.batchSize - Number of rows to fetch per batch (default: 100)
|
|
8800
|
+
* @throws {RelqConfigError} If dialect doesn't support cursors
|
|
8592
8801
|
*
|
|
8593
8802
|
* @example
|
|
8594
8803
|
* ```typescript
|
|
8595
|
-
*
|
|
8596
|
-
*
|
|
8804
|
+
* // Process all unverified users
|
|
8805
|
+
* await db.table.users.select('email', 'userId')
|
|
8597
8806
|
* .where(q => q.equal('verified', false))
|
|
8598
8807
|
* .each(async (row) => {
|
|
8599
|
-
*
|
|
8808
|
+
* await sendVerificationEmail(row.email);
|
|
8600
8809
|
* });
|
|
8601
8810
|
*
|
|
8602
|
-
* //
|
|
8603
|
-
* await db.table(
|
|
8604
|
-
* .
|
|
8605
|
-
*
|
|
8811
|
+
* // Stop early with false return
|
|
8812
|
+
* await db.table.logs.select()
|
|
8813
|
+
* .each(async (row, index) => {
|
|
8814
|
+
* if (index >= 1000) return false; // Stop after 1000
|
|
8815
|
+
* processLog(row);
|
|
8816
|
+
* }, { batchSize: 50 });
|
|
8606
8817
|
* ```
|
|
8607
8818
|
*/
|
|
8608
8819
|
each<T = InferResultType<TSchema, TTable, TColumns> & TJoined>(callback: (row: T, index: number) => void | false | Promise<void | false>, options?: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "relq",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.71",
|
|
4
4
|
"description": "The Fully-Typed PostgreSQL ORM for TypeScript",
|
|
5
5
|
"author": "Olajide Mathew O. <olajide.mathew@yuniq.solutions>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -67,7 +67,9 @@
|
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@aws-sdk/dsql-signer": "^3.971.0",
|
|
70
|
+
"@clack/prompts": "^1.0.0",
|
|
70
71
|
"@pgsql/types": "^17.6.2",
|
|
72
|
+
"citty": "^0.2.0",
|
|
71
73
|
"cli-spinners": "^3.4.0",
|
|
72
74
|
"jiti": "^2.6.1",
|
|
73
75
|
"ora": "^9.0.0",
|