Skip to content

Commit d0e7a11

Browse files
authored
Handle empty stdin in YDB CLI running in a job (#18395)
1 parent 4138c79 commit d0e7a11

File tree

7 files changed

+81
-18
lines changed

7 files changed

+81
-18
lines changed

ydb/apps/ydb/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
* Fixed a bug where ydb cli was trying to read parameters from stdin even if it had no data.
12
* Add `--replace` option to `ydb tools restore` command. If enabled, scheme objects present in the backup would be dropped before restoring.
23
* Added date range parameters (--date-to, --date-from to support uniform PK distribution) for ydb workload log run operations including bulk_upsert, insert, and upsert
34
* Do not save to local backups destination tables of `ASYNC REPLICATION` and its changefeeds. It prevents duplication of changefeeds and reduces the amount of space the backup takes on disk.

ydb/public/lib/ydb_cli/commands/ydb_service_scripting.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ int TCommandExecuteYqlScript::Run(TConfig& config) {
116116

117117
if (!Parameters.empty() || InputParamStream) {
118118
THolder<TParamsBuilder> paramBuilder;
119-
while (GetNextParams(driver, Script, paramBuilder)) {
119+
while (GetNextParams(driver, Script, paramBuilder, config.IsVerbose())) {
120120
auto asyncResult = client.ExecuteYqlScript(
121121
Script,
122122
paramBuilder->Build(),

ydb/public/lib/ydb_cli/commands/ydb_service_table.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ int TCommandExecuteQuery::ExecuteDataQuery(TConfig& config) {
457457

458458
if (!Parameters.empty() || InputParamStream) {
459459
THolder<TParamsBuilder> paramBuilder;
460-
while (GetNextParams(driver, Query, paramBuilder)) {
460+
while (GetNextParams(driver, Query, paramBuilder, config.IsVerbose())) {
461461
TParams params = paramBuilder->Build();
462462
auto operation = [this, &txSettings, &params, &settings, &asyncResult](NTable::TSession session) {
463463
auto promise = NThreading::NewPromise<NTable::TDataQueryResult>();
@@ -723,7 +723,7 @@ int TCommandExecuteQuery::ExecuteQueryImpl(TConfig& config) {
723723
SetInterruptHandlers();
724724
if (!Parameters.empty() || InputParamStream) {
725725
THolder<TParamsBuilder> paramBuilder;
726-
while (GetNextParams(driver, Query, paramBuilder)) {
726+
while (GetNextParams(driver, Query, paramBuilder, config.IsVerbose())) {
727727
auto operation = [this, &paramBuilder, &settings, &asyncResult](TClient client) {
728728
auto promise = NThreading::NewPromise<TPartIterator<TClient>>();
729729
asyncResult = promise.GetFuture();

ydb/public/lib/ydb_cli/commands/ydb_sql.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ int TCommandSql::RunCommand(TConfig& config) {
180180
if (!Parameters.empty() || InputParamStream) {
181181
// Execute query with parameters
182182
THolder<TParamsBuilder> paramBuilder;
183-
while (!IsInterrupted() && GetNextParams(driver, Query, paramBuilder)) {
183+
while (!IsInterrupted() && GetNextParams(driver, Query, paramBuilder, config.IsVerbose())) {
184184
auto asyncResult = client.StreamExecuteQuery(
185185
Query,
186186
NQuery::TTxControl::NoTx(),

ydb/public/lib/ydb_cli/commands/ydb_yql.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ int TCommandYql::RunCommand(TConfig& config, const TString& script) {
101101

102102
if (!Parameters.empty() || InputParamStream) {
103103
THolder<TParamsBuilder> paramBuilder;
104-
while (!IsInterrupted() && GetNextParams(driver, Script, paramBuilder)) {
104+
while (!IsInterrupted() && GetNextParams(driver, Script, paramBuilder, config.IsVerbose())) {
105105
auto asyncResult = client.StreamExecuteYqlScript(
106106
script,
107107
paramBuilder->Build(),

ydb/public/lib/ydb_cli/common/parameters.cpp

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,53 @@ void TCommandWithParameters::AddParams(TParamsBuilder& paramBuilder) {
192192
}
193193
}
194194

195+
namespace {
196+
bool StdinHasData(bool verbose) {
197+
#if defined(_win32_)
198+
// Too complex case for Windows
199+
return false;
200+
#else
201+
// fd_set to store a set of descriptor set.
202+
fd_set read_fds;
203+
FD_ZERO(&read_fds);
204+
FD_SET(STDIN_FILENO, &read_fds);
205+
206+
struct timeval timeout;
207+
timeout.tv_sec = 0;
208+
timeout.tv_usec = 0; // No timeout, instant check
209+
210+
// Check if stdin is available for reading
211+
int selectResult = select(STDIN_FILENO + 1, &read_fds, NULL, NULL, &timeout);
212+
if (selectResult == 0) {
213+
if (verbose) {
214+
Cerr << "stdin is not available" << Endl;
215+
}
216+
return false;
217+
}
218+
219+
// Trying to read 1 symbol from stdin
220+
char buffer[1];
221+
ssize_t result = read(fileno(stdin), buffer, sizeof(buffer));
222+
if (result == -1) {
223+
if (verbose) {
224+
Cerr << "Error reading from stdin. Error: " << strerror(errno) << Endl;
225+
}
226+
} else if (result == 0) {
227+
if (verbose) {
228+
Cerr << "No data from stdin" << Endl;
229+
}
230+
} else {
231+
if (verbose) {
232+
Cerr << "stdin has data, returning first symbol '" << buffer[0] << "' back..." << Endl;
233+
}
234+
ungetc(buffer[0], stdin);
235+
return true;
236+
}
237+
return false;
238+
#endif
239+
}
240+
}
241+
195242
void TCommandWithParameters::ParseParameters(TClientCommand::TConfig& config) {
196243
// Deprecated options with defaults:
197244
if (!DeprecatedSkipRows.empty()) {
@@ -253,10 +300,12 @@ void TCommandWithParameters::ParseParameters(TClientCommand::TConfig& config) {
253300
}
254301
}
255302

303+
bool verbose = config.IsVerbose();
304+
256305
if (InputFiles.empty()) {
257-
if (!IsStdinInteractive() && !ReadingSomethingFromStdin) {
306+
if (!IsStdinInteractive() && !ReadingSomethingFromStdin && StdinHasData(verbose)) {
258307
// By default reading params from stdin
259-
SetParamsInputFromStdin();
308+
SetParamsInputFromStdin(verbose);
260309
}
261310
} else {
262311
auto& file = InputFiles[0];
@@ -265,10 +314,10 @@ void TCommandWithParameters::ParseParameters(TClientCommand::TConfig& config) {
265314
throw TMisuseException() << "Path to input file is \"-\", meaning that parameter value[s] should be read "
266315
"from stdin. This is only available in non-interactive mode";
267316
}
268-
SetParamsInputFromStdin();
317+
SetParamsInputFromStdin(verbose);
269318
} else {
270319
if (IsStdinInteractive() || ReadingSomethingFromStdin) {
271-
SetParamsInputFromFile(file);
320+
SetParamsInputFromFile(file, verbose);
272321
} else {
273322
throw TMisuseException() << "Path to input file is \"" << file << "\", meaning that parameter value[s]"
274323
" should be read from file. This is only available in interactive mode. Can't read parameters both"
@@ -320,21 +369,27 @@ void TCommandWithParameters::SetParamsInput(IInputStream* input) {
320369
}
321370
}
322371

323-
void TCommandWithParameters::SetParamsInputFromStdin() {
372+
void TCommandWithParameters::SetParamsInputFromStdin(bool verbose) {
324373
if (ReadingSomethingFromStdin) {
325374
throw TMisuseException() << "Can't read both parameters and query text from stdinput";
326375
}
327376
ReadingSomethingFromStdin = true;
328377
SetParamsInput(&Cin);
378+
if (verbose) {
379+
Cerr << "Reading parameters from stdin" << Endl;
380+
}
329381
}
330382

331-
void TCommandWithParameters::SetParamsInputFromFile(TString& file) {
383+
void TCommandWithParameters::SetParamsInputFromFile(TString& file, bool verbose) {
332384
TFsPath fsPath = GetExistingFsPath(file, "input file");
333385
InputFileHolder = MakeHolder<TFileInput>(fsPath);
334386
SetParamsInput(InputFileHolder.Get());
387+
if (verbose) {
388+
Cerr << "Reading parameters from file \"" << file << '\"' << Endl;
389+
}
335390
}
336391

337-
void TCommandWithParameters::InitParamTypes(const TDriver& driver, const TString& queryText) {
392+
void TCommandWithParameters::InitParamTypes(const TDriver& driver, const TString& queryText, bool verbose) {
338393
if (SyntaxType == NQuery::ESyntax::Pg) {
339394
ParamTypes.clear();
340395
return;
@@ -343,9 +398,15 @@ void TCommandWithParameters::InitParamTypes(const TDriver& driver, const TString
343398
auto types = TYqlParamParser::GetParamTypes(queryText);
344399
if (types.has_value()) {
345400
ParamTypes = *types;
401+
if (verbose) {
402+
Cerr << "Successfully retrieved parameter types from query text locally" << Endl;
403+
}
346404
return;
347405
}
348406

407+
if (verbose) {
408+
Cerr << "Failed to retrieve parameter types from query text locally. Executing ExplainYqlScript..." << Endl;
409+
}
349410
// Fallback to ExplainYql
350411
NScripting::TScriptingClient client(driver);
351412
auto explainSettings = NScripting::TExplainYqlRequestSettings()
@@ -360,11 +421,12 @@ void TCommandWithParameters::InitParamTypes(const TDriver& driver, const TString
360421
ParamTypes = result.GetParameterTypes();
361422
}
362423

363-
bool TCommandWithParameters::GetNextParams(const TDriver& driver, const TString& queryText, THolder<TParamsBuilder>& paramBuilder) {
424+
bool TCommandWithParameters::GetNextParams(const TDriver& driver, const TString& queryText,
425+
THolder<TParamsBuilder>& paramBuilder, bool verbose) {
364426
paramBuilder = MakeHolder<TParamsBuilder>();
365427
if (IsFirstEncounter) {
366428
IsFirstEncounter = false;
367-
InitParamTypes(driver, queryText);
429+
InitParamTypes(driver, queryText, verbose);
368430

369431
if (!InputParamStream) {
370432
AddParams(*paramBuilder);

ydb/public/lib/ydb_cli/common/parameters.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class TCommandWithParameters : public TCommandWithExamples, public TCommandWithI
3232
void AddLegacyBatchParametersOptions(TClientCommand::TConfig& config);
3333
void AddDefaultParamFormats(TClientCommand::TConfig& config);
3434
void AddLegacyStdinFormats(TClientCommand::TConfig& config);
35-
bool GetNextParams(const TDriver& driver, const TString& queryText, THolder<TParamsBuilder>& paramBuilder);
35+
bool GetNextParams(const TDriver& driver, const TString& queryText, THolder<TParamsBuilder>& paramBuilder, bool verbose);
3636

3737
THashMap<EDataFormat, TString>& GetInputFormatDescriptions() override;
3838

@@ -41,9 +41,9 @@ class TCommandWithParameters : public TCommandWithExamples, public TCommandWithI
4141
static void ParseJson(TString&& str, std::map<TString, TString>& result);
4242
void ApplyJsonParams(const std::map<TString, TString>& params, TParamsBuilder& paramBuilder);
4343
void SetParamsInput(IInputStream* input);
44-
void SetParamsInputFromFile(TString& file);
45-
void SetParamsInputFromStdin();
46-
void InitParamTypes(const TDriver& driver, const TString& queryText);
44+
void SetParamsInputFromFile(TString& file, bool verbose);
45+
void SetParamsInputFromStdin(bool verbose);
46+
void InitParamTypes(const TDriver& driver, const TString& queryText, bool verbose);
4747

4848
TMaybe<TString> ReadData();
4949

0 commit comments

Comments
 (0)