Skip to content

Commit bc94bdd

Browse files
bpamiriclaude
andcommitted
Remove maxrows from candidate query to fix BoxLang+Postgres
BoxLang throws when setMaxRows() is called on a PostgreSQL JDBC PreparedStatement with certain parameter combinations. The for-loop already exits on the first successful claim, so maxrows is unnecessary. Also cleans up diagnostic code from the test. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d6659c4 commit bc94bdd

File tree

2 files changed

+6
-55
lines changed

2 files changed

+6
-55
lines changed

vendor/wheels/JobWorker.cfc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,12 @@ component {
5555

5656
local.sql &= " ORDER BY priority DESC, runAt ASC";
5757

58-
// Fetch a small batch of candidates for optimistic locking
58+
// Fetch candidates for optimistic locking.
59+
// NOTE: Avoid maxrows option — BoxLang + PostgreSQL throws when setMaxRows()
60+
// is called on the JDBC PreparedStatement with certain parameter combinations.
61+
// The for-loop below processes only the first successful claim anyway.
5962
try {
60-
local.candidates = queryExecute(local.sql, local.params, {datasource = variables.$datasource, maxrows = 5});
63+
local.candidates = queryExecute(local.sql, local.params, {datasource = variables.$datasource});
6164
} catch (any e) {
6265
$ensureJobTable();
6366
local.result.skipped = true;

vendor/wheels/tests/specs/jobs/JobWorkerSpec.cfc

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -73,64 +73,12 @@ component extends="wheels.WheelsTest" {
7373
expect(local.enqueued).toHaveKey("persisted");
7474
expect(local.enqueued.persisted).toBeTrue();
7575

76-
// Diagnostic: test candidate query pieces to isolate BoxLang+Postgres issue
77-
local.now = CreateDateTime(Year(Now()), Month(Now()), Day(Now()), Hour(Now()), Minute(Now()), Second(Now()));
78-
local.diag = "";
79-
80-
// Test A: full candidate query WITH maxrows (same as processNext)
81-
try {
82-
local.qA = queryExecute(
83-
"SELECT id FROM wheels_jobs WHERE status = 'pending' AND runAt <= :runAt AND queue IN (:queue1) ORDER BY priority DESC, runAt ASC",
84-
{runAt = {value = local.now, cfsqltype = "cf_sql_timestamp"}, queue1 = {value = "test_claim", cfsqltype = "cf_sql_varchar"}},
85-
{datasource = application.wheels.dataSourceName, maxrows = 5}
86-
);
87-
local.diag &= "A=#local.qA.recordCount#";
88-
} catch (any eA) {
89-
local.diag &= "A=ERR:#Left(eA.message, 100)#";
90-
}
91-
92-
// Test B: same query WITHOUT maxrows
93-
try {
94-
local.qB = queryExecute(
95-
"SELECT id FROM wheels_jobs WHERE status = 'pending' AND runAt <= :runAt AND queue IN (:queue1) ORDER BY priority DESC, runAt ASC",
96-
{runAt = {value = local.now, cfsqltype = "cf_sql_timestamp"}, queue1 = {value = "test_claim", cfsqltype = "cf_sql_varchar"}},
97-
{datasource = application.wheels.dataSourceName}
98-
);
99-
local.diag &= ", B=#local.qB.recordCount#";
100-
} catch (any eB) {
101-
local.diag &= ", B=ERR:#Left(eB.message, 100)#";
102-
}
103-
104-
// Test C: without IN clause, use direct = comparison
105-
try {
106-
local.qC = queryExecute(
107-
"SELECT id FROM wheels_jobs WHERE status = 'pending' AND runAt <= :runAt AND queue = :queue1",
108-
{runAt = {value = local.now, cfsqltype = "cf_sql_timestamp"}, queue1 = {value = "test_claim", cfsqltype = "cf_sql_varchar"}},
109-
{datasource = application.wheels.dataSourceName}
110-
);
111-
local.diag &= ", C=#local.qC.recordCount#";
112-
} catch (any eC) {
113-
local.diag &= ", C=ERR:#Left(eC.message, 100)#";
114-
}
115-
116-
// Test D: without timestamp param comparison
117-
try {
118-
local.qD = queryExecute(
119-
"SELECT id FROM wheels_jobs WHERE status = 'pending' AND queue = :queue1",
120-
{queue1 = {value = "test_claim", cfsqltype = "cf_sql_varchar"}},
121-
{datasource = application.wheels.dataSourceName}
122-
);
123-
local.diag &= ", D=#local.qD.recordCount#";
124-
} catch (any eD) {
125-
local.diag &= ", D=ERR:#Left(eD.message, 100)#";
126-
}
127-
12876
// Process it
12977
local.worker = new wheels.JobWorker();
13078
local.result = local.worker.processNext(queues = "test_claim");
13179

13280
// Job was processed (may succeed or fail depending on job class)
133-
expect(local.result.skipped).toBeFalse(local.diag);
81+
expect(local.result.skipped).toBeFalse();
13482
expect(Len(local.result.jobId)).toBeGT(0);
13583
});
13684

0 commit comments

Comments
 (0)