Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 2 additions & 22 deletions command-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,10 @@
"alias": ["force:data:soql:query"],
"command": "data:query",
"flagAliases": ["apiversion", "resultformat", "soqlqueryfile", "targetusername", "u", "usetoolingapi"],
"flagChars": ["b", "f", "o", "q", "r", "t", "w"],
"flagChars": ["f", "o", "q", "r", "t"],
"flags": [
"all-rows",
"api-version",
"async",
"bulk",
"file",
"flags-dir",
"json",
Expand All @@ -191,25 +189,7 @@
"query",
"result-format",
"target-org",
"use-tooling-api",
"wait"
],
"plugin": "@salesforce/plugin-data"
},
{
"alias": ["force:data:soql:bulk:report"],
"command": "data:query:resume",
"flagAliases": ["apiversion", "bulkqueryid", "resultformat", "targetusername", "u"],
"flagChars": ["i", "o", "r"],
"flags": [
"api-version",
"bulk-query-id",
"flags-dir",
"json",
"loglevel",
"result-format",
"target-org",
"use-most-recent"
"use-tooling-api"
],
"plugin": "@salesforce/plugin-data"
},
Expand Down
21 changes: 0 additions & 21 deletions messages/bulk.report.md

This file was deleted.

27 changes: 1 addition & 26 deletions messages/soql.query.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ Execute a SOQL query.

Specify the SOQL query at the command line with the --query flag or read the query from a file with the --file flag.

If your query returns more than 10,000 records, specify the --bulk flag. The command then runs the query using Bulk API 2.0, which has higher limits than the default API used by the command.

When using --bulk, the command waits 3 minutes by default for the query to complete. Use the --wait parameter to specify a different number of minutes to wait, or set --wait to 0 to immediately return control to the terminal. If you set --wait to 0, or you use the --async flag, or the command simply times out, the command displays an ID. Pass this ID to the the "data query resume" command using the --bulk-query-id flag to get the results; pass the ID to the "data resume" command to get the job status.
If your query returns more than 10,000 records, prefer to use the `sf data export bulk` command instead. It runs the query using Bulk API 2.0, which has higher limits than the default API used by the command.

# examples

Expand All @@ -24,10 +22,6 @@ When using --bulk, the command waits 3 minutes by default for the query to compl

<%= config.bin %> <%= command.id %> --query "SELECT Name FROM ApexTrigger" --use-tooling-api

- Use Bulk API 2.0 to run a query that returns many rows, and return control to the terminal immediately:

<%= config.bin %> <%= command.id %> --query "SELECT Id FROM Contact" --bulk --wait 0

# flags.query.summary

SOQL query to execute.
Expand All @@ -40,22 +34,10 @@ Use Tooling API so you can run queries on Tooling API objects.

File that contains the SOQL query.

# flags.bulk.summary

Use Bulk API 2.0 to run the query.

# flags.async.summary

Use Bulk API 2.0, but don't wait for the job to complete.

# flags.all-rows.summary

Include deleted records. By default, deleted records are not returned.

# flags.wait.summary

Time to wait for the command to finish, in minutes.

# flags.output-file.summary

File where records are written; only CSV and JSON output formats are supported.
Expand All @@ -67,10 +49,3 @@ Total number of records retrieved: %s.
# queryRunningMessage

Querying Data

# bulkQueryTimeout

Query ID: %s
Query is in progress.

Run "sf data query resume -i %s -o %s" to get the latest status and results.
21 changes: 0 additions & 21 deletions src/bulkDataRequestCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,27 +102,6 @@ export abstract class BulkDataRequestCache extends TTLConfig<TTLConfig.Options,
}
}

export class BulkQueryRequestCache extends BulkDataRequestCache {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have 1 class per data-resumable command (so that each has it's own json file). This one was used only by data query/data query resume, data export resume has a separate one.

public static getDefaultOptions(): TTLConfig.Options {
return {
isGlobal: true,
isState: true,
filename: BulkQueryRequestCache.getFileName(),
stateFolder: Global.SF_STATE_FOLDER,
ttl: Duration.days(3),
};
}

public static getFileName(): string {
return 'bulk-data-query-cache.json';
}

public static async unset(key: string): Promise<void> {
const cache = await BulkQueryRequestCache.create();
cache.unset(key);
await cache.write();
}
}
export class BulkDeleteRequestCache extends BulkDataRequestCache {
public static getDefaultOptions(): TTLConfig.Options {
return {
Expand Down
113 changes: 9 additions & 104 deletions src/commands/data/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import fs from 'node:fs';
import { Connection, Logger, Messages, SfError } from '@salesforce/core';
import { Connection, Logger, Messages } from '@salesforce/core';
import type { Record as jsforceRecord } from '@jsforce/jsforce-node';
import {
AnyJson,
Expand All @@ -18,13 +18,10 @@ import {
JsonArray,
toJsonMap,
} from '@salesforce/ts-types';
import { Duration } from '@salesforce/kit';
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { BulkV2, QueryJobV2 } from '@jsforce/jsforce-node/lib/api/bulk2.js';
import { orgFlags, perflogFlag, resultFormatFlag } from '../../flags.js';
import { Field, FieldType, SoqlQueryResult } from '../../types.js';
import { displayResults, transformBulkResults } from '../../queryUtils.js';
import { BulkQueryRequestCache } from '../../bulkDataRequestCache.js';
import { displayResults } from '../../queryUtils.js';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-data', 'soql.query');
Expand Down Expand Up @@ -63,26 +60,6 @@ export class DataSoqlQueryCommand extends SfCommand<DataQueryResult> {
summary: messages.getMessage('flags.use-tooling-api.summary'),
aliases: ['usetoolingapi'],
deprecateAliases: true,
exclusive: ['bulk'],
}),
bulk: Flags.boolean({
char: 'b',
default: false,
summary: messages.getMessage('flags.bulk.summary'),
exclusive: ['use-tooling-api'],
}),
wait: Flags.duration({
unit: 'minutes',
char: 'w',
summary: messages.getMessage('flags.wait.summary'),
dependsOn: ['bulk'],
exclusive: ['async'],
}),
async: Flags.boolean({
summary: messages.getMessage('flags.async.summary'),
dependsOn: ['bulk'],
exclusive: ['wait'],
deprecated: true,
}),
'all-rows': Flags.boolean({
summary: messages.getMessage('flags.all-rows.summary'),
Expand Down Expand Up @@ -128,32 +105,18 @@ export class DataSoqlQueryCommand extends SfCommand<DataQueryResult> {
this.logger = await Logger.child('data:soql:query');
const flags = (await this.parse(DataSoqlQueryCommand)).flags;

if (flags.bulk || flags.wait || flags.async) {
this
.warn(`Bulk mode for "data query" is deprecated, the following flags will be removed after April 2025: --bulk | --wait | --async.
Use "data export bulk" for bulk queries instead.
`);
}

try {
// --file will be present if flags.query isn't. Oclif exactlyOne isn't quite that clever
const queryString = flags.query ?? fs.readFileSync(flags.file as string, 'utf8');
const conn = flags['target-org'].getConnection(flags['api-version']);
if (flags['result-format'] !== 'json') this.spinner.start(messages.getMessage('queryRunningMessage'));
const queryResult = flags.bulk
? await this.runBulkSoqlQuery(
conn,
queryString,
flags.async ? Duration.minutes(0) : flags.wait ?? Duration.minutes(0),
flags['all-rows'] === true
)
: await this.runSoqlQuery(
flags['use-tooling-api'] ? conn.tooling : conn,
queryString,
this.logger,
this.configAggregator.getInfo('org-max-query-limit').value as number,
flags['all-rows']
);
const queryResult = await this.runSoqlQuery(
flags['use-tooling-api'] ? conn.tooling : conn,
queryString,
this.logger,
this.configAggregator.getInfo('org-max-query-limit').value as number,
flags['all-rows']
);

if (flags['output-file'] ?? !this.jsonEnabled()) {
displayResults({ ...queryResult }, flags['result-format'], flags['output-file']);
Expand All @@ -169,48 +132,6 @@ Use "data export bulk" for bulk queries instead.
if (flags['result-format'] !== 'json') this.spinner.stop();
}
}
/**
* Executes a SOQL query using the bulk 2.0 API
*
* @param connection
* @param query
* @param timeout
*/
private async runBulkSoqlQuery(
connection: Connection,
query: string,
timeout: Duration,
allRows: boolean | undefined = false
): Promise<SoqlQueryResult> {
if (timeout.milliseconds === 0) {
const job = new QueryJobV2(connection, {
bodyParams: {
query,
operation: allRows ? 'queryAll' : 'query',
},
pollingOptions: { pollTimeout: timeout.milliseconds, pollInterval: 5000 },
});
const info = await job.open();
return prepareAsyncQueryResponse(connection)(this)({ query, jobId: info.id });
}
try {
const bulk2 = new BulkV2(connection);
const res =
(await bulk2.query(query, {
pollTimeout: timeout.milliseconds ?? Duration.minutes(5).milliseconds,
pollInterval: 5000,
...(allRows ? { scanAll: true } : {}),
})) ?? [];
return transformBulkResults((await res.toArray()) as jsforceRecord[], query);
} catch (e) {
if (e instanceof Error && e.name === 'JobPollingTimeout' && 'jobId' in e && typeof e.jobId === 'string') {
process.exitCode = 69;
return prepareAsyncQueryResponse(connection)(this)({ query, jobId: e.jobId });
} else {
throw e instanceof Error || typeof e === 'string' ? SfError.wrap(e) : e;
}
}
}

private async runSoqlQuery(
connection: Connection | Connection['tooling'],
Expand Down Expand Up @@ -248,22 +169,6 @@ Use "data export bulk" for bulk queries instead.
}
}

const prepareAsyncQueryResponse =
(connection: Connection) =>
(cmd: SfCommand<unknown>) =>
async ({ query, jobId }: { query: string; jobId: string }): Promise<SoqlQueryResult> => {
const cache = await BulkQueryRequestCache.create();
await cache.createCacheEntryForRequest(jobId, connection.getUsername(), connection.getApiVersion());
cmd.log(messages.getMessage('bulkQueryTimeout', [jobId, jobId, connection.getUsername()]));
return buildEmptyQueryResult({ query, jobId });
};

const buildEmptyQueryResult = ({ query, jobId }: { query: string; jobId: string }): SoqlQueryResult => ({
columns: [],
result: { done: false, records: [], totalSize: 0, id: jobId },
query,
});

const searchSubColumnsRecursively = (parent: AnyJson): string[] => {
const column = ensureJsonMap(parent);
const name = ensureString(column.columnName);
Expand Down
80 changes: 0 additions & 80 deletions src/commands/data/query/resume.ts

This file was deleted.

Loading
Loading