I'm trying to call a BCP command from c#.I'm building the command within the c# code, and am getting an error. Here is the BCP command as I'm building it:
string.Format("bcp \"EXEC {0} {1}\" queryout \"{2}\" -c -t\"\0\" -S {3} -U {4} -P {5}", spName, filterList, path, server, user, password);
Here is the command after formatting:
"bcp \"EXEC StatisticsByType #Date='1/1/2013'\" queryout \"\\\\Server\\Folder\\MagicWords.txt\" -c -t\"\0\" -S server.com -U userName -P password"
The error I'm getting is:
{"Incorrect syntax near 'queryout'.\r\n'.' is an invalid name because it contains a NULL character or an invalid unicode character."}
I'm assuming it has something to do with how .Net is escaping the ". Would anyone know the proper way to pass those quotation marks around, or if thats even the issue?
EDIT: I ran the BCP command in question from the powershell ISE. It threw an "Unable to open BCP host data-file" error. So I changed the queryout server name to it's mapped network drive name, and It worked. However, doing the same thing in C# yielded no change.
I suspect the problem is the delimiter. The sequence -t\"\0\" translates to a NULL character between quotes. You might try replacing \0 with another delimeter like , or ;. If you do want the NULL character as a delimiter you should insert -t\\0 in your string.
Related
I have a dotnet core container app (linux) using the following code to make an async SELECT call to db2 (z/os):
var result = new DataTable();
var query = "SELECT * FROM DB.TABLE WITH UR;"
using (var connection = new DB2Connection(_connection))
{
await connection.OpenAsync();
using (var command = new DB2Command(query, connection))
{
using (var myReader = await command.ExecuteReaderAsync()) // fails here when query > 1190 chars
{
result.Columns.AddRange(myReader.GetColumnSchema()
.Select(x => new DataColumn(x.ColumnName, x.DataType))
.ToArray());
result.BeginLoadData();
while (await myReader.ReadAsync())
{
var contents = new object[myReader.FieldCount];
myReader.GetValues(contents);
result.LoadDataRow(contents, false);
}
result.EndLoadData();
}
}
}
This code works fine for any query under 1190 characters. When I increase the sql string to 1191 characters or more, the call hangs and times out with the following error:
IBM.Data.DB2.Core.DB2Exception (0x80004005): ERROR [08001] [IBM] SQL30081N A communication error has been detected. Communication protocol being used: "TCP/IP". Communication API being used: "SOCKETS". Location where the error was detected: "170.2.8.84". Communication function detecting the error: "recv". Protocol specific error code(s): "110", "", "". SQLSTATE=08001
The issue does not occur when running with Docker locally. I am unable to run Windows containers on the culprit host, so that comparison is unavailable.
Client:
# db2level
DB21085I This instance or install (instance name, where applicable: "*") uses
"64" bits and DB2 code release "SQL11055" with level identifier "0606010F".
Informational tokens are "DB2 v11.5.5.1", "s2103171200", "DYN2103171200AMD64",
and Fix Pack "1".
Product is installed at "/app/clidriver".
A DBA was unable to see any activity for the timed out queries. What could be impeding these calls based on this specific size threshold?
To test if your db2cli can execute the same SQL statement (i.e. independently of C# and independently of .net core, you can copy and modify the script below, by copying the hostname, username, password, and port number from your credentials json file (or wherever you received your connection string details).
If your connection is NOT encrypted with SSL/TLS, modify the script to remove the Security=SSL parameters from the database and dsn definitions below, and use the correct non-SSL port-number.
If your password contains any characters that are considered special by your linux shell, escape each such special character by a backslash \ when assigning the password variable below in the script. Otherwise you will get SQL30082N reason "24".
After you modify this file, chmod +x the file, and run it.
#!/bin/bash
#
# populate db2dsdriver.cfg for Db2-on-cloud lite and verify db2cli connects and runs SQL statements.
#
# Prereq: (1) an IBM Db2 CLI driver is already installed and on the PATH for current userid.
# Prereq: (2) the version of the CLI driver matches the version of the Db2-lite instance (per the UI dashboard)
# Prereq: (3) after you modify this file, remember to `chmod +x` this file before running it.
#
# This script works with clidriver , with the Db2 runtime client, with the Db2 data server client (fat client).
# Can re-run this script, overwrites current matching entries in db2dsdriver.cfg (if exists) else creates that file.
#
# For IBM Db2-on-cloud (lite plan , kostenlos ).
# Configure the db2dsdriver.cfg file for use with db2cli tool to connect to Db2-on-cloud from command-line bash.
#
# You must modify the variable-values below by copying username, password, port, hostname from your credentials json file.
# And remember to escape ( precede with \) any and all special-character in password, otherwise connect will fail.
#
# Note 1: this expects the Db2-on-cloud hostname to have SSL/TLS encrypted-connections to BLUDB which is the default
# for all IBM Db2-on-cloud hostnames ending with pattern *appdomain.cloud
#
# Note 2: at clidriver version 11.5.6.0 , db2cli tool can return exit-code 0 even on failure, doh!
#
# Note 3: to get your username and password, host and port-number , download the credentials json file and view it to see them.
# Then copy their values into the appropriate variables below before making this script executable and run it.
#
set -u
typeset which=/usr/bin/which
typeset db2cli=$( ${which} db2cli )
[[ -z ${db2cli:=""} ]] && print "\nERROR: please either dot in a db2profile to allow db2cli to be on the PATH\nor edit your PATH environment variable to put the clidriver/bin directory on the PATH" && exit 1
typeset dbname=bludb # default database-name for Db2-on-cloud lite plan shared databases.
typeset hostname="change me"
typeset ssl_port_number=32733 # from credentials json
typeset password="change me" # remember to escape \ any special characters here, copy from credentials json file.
typeset username="change me" # copy from credentials json file.
typeset dbalias=bludb # you can use whatever alias-name you like, 8 bytes long max
typeset input_sql_tmpfile=/tmp/db2cli_inputsql.sql
${db2cli} writecfg add -database ${dbname} -host ${hostname} -port ${ssl_port_number} -parameter Security=SSL
rc=$?
(( rc > 0 )) && print "\nERROR: failed to write the database to the config file\n" && exit 1
${db2cli} writecfg add -dsn ${dbalias} -database ${dbname} -host ${hostname} -port ${ssl_port_number} -parameter Security=SSL
rc=$?
(( rc > 0 )) && print "\nERROR: failed to write the dsn to the config file\n" && exit 1
${db2cli} validate -dsn ${dbalias} -connect -user ${username} -passwd ${password}
rc=$?
(( rc > 0 )) && print "\nERROR: failed to connect to the dsn with supplied credentials\n" && exit 1
# Verify that the db2cli tool can run some SQL statements by putting them into a file and passing the file to db2cli
#create an inputfile containing SQL statements ( make a temp file for this purpose)
# note that the default statement delimiter for db2cli is crlf (or lf on linux) not semicolon as with db2clp.
echo "values current timestamp" > ${input_sql_tmpfile}
echo "values current server" >> ${input_sql_tmpfile}
echo "values current user" >> ${input_sql_tmpfile}
${db2cli} execsql -execute -dsn ${dbalias} -user ${username} -passwd ${password} -inputsql ${input_sql_tmpfile}
Trying to send the code below to the command line, but I get errors. I know there is an issue with backslashes sent to CMD. Any help here on how to send it? Thanks!
string strCmdText="/C C:\\Program Files\\MetaTrader 5\\terminal64.exe /config:C:\\Users\\vguer036\\AppData\\Roaming\\MetaQuotes\\Terminal\\D0E8209F77C8CF37AD8BF550E51FF075\\config\\common.ini";
System.Diagnostics.Process.Start("CMD.exe", strCmdText);
When you use this:
string strCmdText="/C C:\\Program Files\\MetaTrader 5\\terminal64.exe /config:C:\\Users\\vguer036\\AppData\\Roaming\\MetaQuotes\\Terminal\\D0E8209F77C8CF37AD8BF550E51FF075\\config\\common.ini";
System.Diagnostics.Process.Start("CMD.exe", strCmdText);
then you are trying to start the application CMD.exe, which in turn you instruct to execute a particular application. However, because of the spaces, what CMD is trying to execute is the command C:\\Program with parameters Files\\MetaTrader, 5\\terminal64.exe etc. That is where your error message comes from.
One way to solve this is to add extra quotes around the filename (as Dour High Arch commented):
string strCmdText=#"/C ""C:\Program Files\MetaTrader 5\terminal64.exe"" ""/config:C:\Users\vguer036\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\config\common.ini""";
Note the doubled quotes, which are required to use a literal quote inside a verbatim string literal (#"...").
But this way you are still executing one application (CMD.exe) to start another (terminal64.exe). Why not start that terminal64 directly?
System.Diagnostics.Process.Start(
#"C:\Program Files\MetaTrader 5\terminal64.exe",
#"/config:C:\Users\vguer036\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\config\common.ini");
You should experiment to see whether you need extra quotes around that application name, but I don't think so.
So im trying to run multiple commands in a one command line, im using WGET to download a few files to a local path this part works so far.
Here is what Ive got
startInfo.Arguments = #"/K wget -x -np -S -N -nH -r -R ""index.html*""
http://my-server.ip -P """ +
Properties.Settings.Default.userAddonDir+" && exit";
with this I should expect CMD to launch WGET with the command line:
-x -np -S -N -nH -r -R ""index.html*""
from my server:
http://my-server.ip
to my users local path for example:
E:/Folder/Sub Folder/
then once wget is finished it should see
&& Exit
and close cmd...
things to note, the users local path is likely to have white space so I've wrapped it in quotes to prevent the path being cut off. (hence all the quotes)
but what the path resolves to is odd. not to sure
E:/Folder/Sub Folder/ && exit/
If I place any more "" it breaks any ideas or pointers as im at a loss.
Thanks in advance.
Change your command to
string x = #"E:\temp";
string t = #"/K wget -x -np -S -N -nH -r -R ""index.html*"" http://my-server.ip -P """ +
x + #""" && exit";
After the operator + the initial verbatim character is no more effective. You need to reapply it to the last part of your string constant && exit
However, have you tried to launch your command with /C instead of /K?
This will automatically close the command window so you don't need anymore the Exit command
I know most people will give responses like, right click database name, select task, generate scripts, etc. Most people give detail response on the internet include stackoverflow
I already know that long time ago.
My question is any suggestions to write scripts to export/import stored procedures. If I only have to do 5 or 10, it is no big issue. I just have to pick right ones. If there are lots like 50 or more, it is pain and bound to be errors. Sometimes, to make life easier, we want to export/import with suffix such as *test.sql.
Any suggestion using SQL scripts, C# or dos commands to do so?
Thanks
Since you tagged also C#. With .NET you can do it using Microsoft.SqlServer.Smo. There is a StoredProcedure class. This approach gives you really lots of control over the task you want to achieve. Example usage you can find here, also using PowerShell.
Here's a snippet I've put together that might help you(uses Microsoft.SqlServer.Smo, Microsoft.SqlServer.ConnectionInfo, Microsoft.SqlServer.Management.Sdk.Sfc DLLs):
var serverConnection = new ServerConnection(new SqlConnection(ConnectionString));
var server = new Server(serverConnection);
foreach (StoredProcedure sp in server.Databases["YourDB"].StoredProcedures)
{
if (sp.Name.Contains("yourSubstring"))
{
System.IO.File.WriteAllText(sp.TextHeader + Environment.NewLine + sp.TextBody);
}
}
Why have you tagged this question with C# when you ask for answers in SQL or DOS script?
This CMD script drops all stored procedures whose names begin with "usp":
FOR /F "tokens=* USEBACKQ" %%F IN (`sqlcmd -S %SERVER% -d %DATABASE% -Q "select name from sys.objects where type = 'P'" `) DO (echo %%F | findstr "^usp" >nul 2>&1 && sqlcmd -S %SERVER% -d %DATABASE% -Q "drop procedure %%F" )
It's all on one line and %SERVER% and %DATABASE% identify the server and database.
This script loads all stored procedures from the current directory whose names match "usp*.sql". You will need the setting QUOTED_IDENTIFIER to be ON if you invoke XML data type methods.
for %%f in (usp*.sql) do sqlcmd -S %SERVER% -d %DATABASE% -I -i %%f
my application will run a command that is like this:
wco -f "C:\Work\6.70 Ex\Master Build.Txt"
what i do is i usually open up cmd and type the above line manually.
i tried to automate this with a script:
string strCmdText = "wco -f C:\Work\6.70 ex\Master Build.Txt";
System.Diagnostics.Process.Start("CMD.exe", strCmdText);
however because of the white spaces it gets confusing.
please help
If you want to use cmd.exe to run a program, you need to add either the /C or /K switch. The /C switch runs the command and then exits cmd.exe. /K runs the command and then leaves cmd.exe open.
cmd.exe /K echo hello
I assume wco is a program of yours? If so, you can bypass using cmd.exe and just call wco.exe directly.
You need to escape your arguments just like you do on the command line plus you need to escape your backslashes:
string strCmdText = "wco -f \"C:\\Work\\6.70 ex\\Master Build.Txt\"";
System.Diagnostics.Process.Start("CMD.exe", strCmdText);
You will need to use the escape character, which is \ in C#.
string strCmdText = "wco -f \"C:\Work\6.70 ex\Master Build.Txt\"";
System.Diagnostics.Process.Start("CMD.exe", strCmdText);
However, if "wco" is an executable, the actual code you should use is
string strCmdText = "-f \"C:\Work\6.70 ex\Master Build.Txt\"";
System.Diagnostics.Process.Start("wco", strCmdText);
This will probably make it easier to redirect the output.