Create stored procedure from x++ - microsoft-dynamics

Got myself into trouble today trying to create a stored procedure from ax.
Here is a simple example:
static void testProcedureCreation(Args _args)
{
MyParamsTable myParams;
SqlStatementExecutePermission perm;
str sqlStatement;
LogInProperty Lp = new LogInProperty();
OdbcConnection myConnection;
Statement myStatement;
ResultSet myResult;
str temp;
;
select myParams;
LP.setServer(myParams.Server);
LP.setDatabase(myParams.Database);
//Lp.setUsername("sa");
//Lp.setPassword("sa");
sqlStatement = #"create procedure testproc
as begin
print 'a'
end";
//sqlStatement = strFmt(sqlStatement, myStr);
info(sqlStatement);
perm = new SqlStatementExecutePermission(sqlStatement);
perm.assert();
try
{
myConnection = new OdbcConnection(LP);
}
catch
{
info("Check username/password.");
return;
}
myStatement = myConnection.createStatement();
myResult = myStatement.executeQuery(sqlStatement);
while (myResult.next())
{
temp = myResult.getString(1);
info(temp);
if (strScan(temp, 'Error', 1, strLen(temp)) > 0)
throw error(temp);
}
myStatement.close();
CodeAccessPermission::revertAssert();
}
To be honest, in my real example I am using BCP and some string concat with a lot of | ' and "".
Anyway, here is what I got:
For a couple of hours I kept changing and retrying a lot of things and, a good thought came into my mind.
"Let's try with a much easier example and check the results!"
OK, no luck, the results were the same, as you can see in the pic above.
But for no reason, I tried to :
exec testproc
in my ssms instance and to my surprise, it worked. My small procedure was there.
It would be so nice if someone could explain this behavior and maybe what should be the correct approach.

This Q/A should provide an answer.
How to get the results of a direct SQL call to a stored procedure?
executeQuery vs executeUpdate

Related

How do I add precautions to get around Microsoft.ACE.OLEDB.12.0 not registered?

When I use the following code on my home computer it works fine for me. However, I have to use it on multiple different machines.
"Provider = Microsoft.ACE.OLEDB.12.0;"
I was thinking that there could be a get around to this because I need it in a more universal format so that it works on multiple machines.
I have hoped that there could be a work around, maybe by including an alternative provider in the code (if that is possible) or an catch or if statement that will rerun the code but with an alternate provider if it cannot find the first one.
It would be changed to Microsoft.Jet.OLEDB.4.0
If anyone could scratch up a little bit of code to work around this so that my program could take into account both providers that would be great as it would make my projects more universal.
Background code (ignore unsafe SQL unless you want to fix it):
OleDbDataReader^ openData(String^ fieldEntity, String^ field, String^ tableName)
{
String^ sqlstr = "SELECT * FROM ";
sqlstr += tableName + " WHERE " + field + " = " + fieldEntity;
OleDbConnection^ conn = nullptr;
conn = gcnew OleDbConnection("Provider = Microsoft.ACE.OLEDB.12.0;" +
"Data Source =" + "myDatabaseV3.accdb");
OleDbCommand^ cmd = nullptr;
//fix this so that it will consider both providers.
conn->Open();
cmd = gcnew OleDbCommand(sqlstr, conn);
OleDbDataReader^ reader = cmd->ExecuteReader(System::Data::CommandBehavior::CloseConnection);
return reader;
}
I have solved this problem myself. I realised that I could you another try catch statement in my class to change the connection string if there is an exception
conn = gcnew OleDbConnection("Provider = Microsoft.Jet.OLEDB.4.0;" +
"Data Source =" + "myDatabaseV3.mdb");
pause();
OleDbCommand^ cmd = nullptr;
//fix this so that it will come out of the current directiory
try {
conn->Open();
}
catch (Exception^ ex)
{
conn->ConnectionString = "Provider = Microsoft.ACE.OLEDB.12.0;" +
"Data Source =" + "myDatabaseV3.accdb";
conn->Open();
}
You could switch these around if you wanted the order of these doesn't really matter. Furthermore, if this solution is invalid you could have a nested try catch statement. However, my code in particular has this In a class and the way this is ran anyway is in a try catch in another part of the code so it is unnecessary for me.

Qt PL/SQL - Assignment Operator - Character String Buffer too small

I have been picking my brain for a while trying to figure this one out.
The problem I am having is that the function I am using in Oracle returns a BLOB. It's a list of items that are concatenated together using ||.
From the research I have done,
In the QSQLQuery docs it says "Stored procedures that uses the return statement to return values, or return multiple result sets, are not fully supported. For specific details see SQL Database Drivers." - which leads me to believe I may need to switch to a different codebase if Qt cannot handle it yet.
The documentation for the QOCI driver mentions this "Binary Large Objects (BLOBs) can be read and written, but be aware that this process may require a lot of memory. You should use a forward only query to select LOB fields (see QSqlQuery::setForwardOnly())."
I did set
query.setForwardOnly(true);
Before I prepared or executed the statement.
However, I still get this error
QSqlError("6502", "Unable to execute statement", "ORA-06502: PL/SQL: numeric or value error: character string buffer too small\nORA-06512: at line 55\n")
I had to scrub the code a bit, I hope this is still helpful to give context to what i'm trying to accomplish
temp_clob clob;
name varchar2(183) := ?;
start varchar2(16) := ?;
end varchar2(16) := ?;
count integer := ?;
return_val named_redacted_table_object; -- Sorry had to remove this, it's a table with more Date, Varchar, etc
begin
dbms_lob.createtemporary(temp_clob, true);
return_val := package_name.function_name (
set_name => name,
start_time => to_date(start, 'yyyy-mm-dd hh24:mi'),
end_time => to_date(end, 'yyyy-mm-dd hh24:mi'),
max_count => count);
-- In here was a loop that would break apart the removed table object and make it into strings along the following lines
-- '' || return_val(i).name || return_val(i).value || etc
-- and would store these into the CLOB / temp_clob
? := temp_clob;
end;
I could not get something as simple as this to work
begin
? := 'test123';
end;
With the assumption I could at least read this string in Qt.
Here is my code for Qt
QString name = "test";
QSqlQuery query(db);
query.setForwardOnly(true);
query.prepare(sql);
QString test_sql_text = ui->comboBox_test_text->currentText();
qDebug() << name;
query.bindValue(0, name);
query.bindValue(1, "2003-03-14 00:00");
query.bindValue(2, "2005-03-14 23:00");
query.bindValue(3, 2);
query.bindValue(4, QString(""), QSql::Out);
bool query_executed_ok = query.exec();
qDebug() << "did it execute?" << query_executed_ok;
// qDebug() << query.executedQuery();
qDebug() << query.boundValues();
qDebug() << query.lastError();
QSqlRecord rec = query.record();
qDebug() << rec;
int record_count = rec.count();
qDebug() << "Records: " << record_count;
while (query.next()) {
for(int i=0;i<record_count;i++) {
qDebug() << query.isValid() << " - " << rec.fieldName(i) << " " << query.value(i).toString();
}
}
The error posted appears to be from within the Oracle code; ORA.... You have stripped so much it's hard to see what is actually happening, especially the are where the error apparently occurred. But perhaps using Oracle supplied code that is specifically designed to handle CLOBs. Instead of
'' || return_val(i).name ...
Try
dbms_lob.append(temp_clob, to_clob(return_val(i).name))
begin
? := 'test123';
end;
Bind variables are used to assign values to variables. You define your variable in your pl/sql code and assign a value to it at runtime by using a bind variable. In that case pl/sql code will compile correctly.
In your code the bind variable is used to replace the pl/sql variable, not the value, which will fail. Your pl/sql block cannot be compiled because it cannot resolve the "?".
A valid use of bind variables would be
BEGIN
l_xyz := ?;
END;
where you assign the value test123 at runtime.
It took some fiddling, and I realize I gave fairly cryptic code. So thank you to belayer and Koen for taking a shot at my mess.
What I was able to determine and get working for anyone else running into this:
Let me start off by saying I am not sure if this is a bug, or if i'm doing something in a way that was not intended by the designers of QSqlQuery (The class for handling SQL calls).
The call would work in SQL developer and I would see the intended CLOB with all characters. I was unable to get DBMS_Output to work, however, I saw this post saying to reserve space on the string before binding it to the query.
It solves my issue and shows the result in the debug window. However, it presents a new problem. What if the string becomes larger than my hard coded reserve value?
Here's the updated code for that
query.prepare(sql);
QString name= ui->comboBox_name->currentText();
qDebug() << project;
query.bindValue(":name", project);
query.bindValue(":start_date", "2005-03-14 00:00");
query.bindValue(":end_date", "2006-03-14 23:00");
query.bindValue(":max_count", 3);
QString testStr ="*****";
//testStr.truncate(0); //Maybe this works too?
testStr.reserve( 1000000 ); // This did it for sure
qDebug() << testStr.capacity();
query.bindValue(":result_clob", testStr, QSql::Out);
bool query_executed_ok = query.exec();
qDebug() << "did it execute?" << query_executed_ok;
if (query_executed_ok) {
testStr = query.boundValue(":result_clob").toString();
qDebug() << testStr;
} else {
qDebug() << "string is empty";
}
I got the idea to do this, from THIS post.

Prettier putting if statement on one line

Prettier formats if statement without curley braces into one line.
This means that this :
function getErrorMessage(response) {
let errorMessage = null;
if (!response.originalError.response)
errorMessage = 'network error';
else
errorMessage = response.originalError.response.data.errorMessage;
return errorMessage;
}
becomes this :
function getErrorMessage(response) {
let errorMessage = null;
if (!response.originalError.response) errorMessage = 'network error';
else errorMessage = response.originalError.response.data.errorMessage;
return errorMessage;
}
which is FAR more unreadable.
Is there a way of disabling this?
As asked in a similar question, it turns out that the answer is that you can not and will not be able to.
As for the WFT that an average senses, well... Apparently, opinionated doesn't mean respected and well-considered in opinion of many. It means that it's implementing the author's opinion.
So, surprisingly, the unexpected thing isn't the lack of configurability but rather that there are any options to be set at all! Go figure... Someone should create a new package called EvenPrettier or FexiblyPrettier and fork in more options. If I only knew how, I'd do it.
I finally ended up using Beautify - HookyQR extension for vscode
https://marketplace.visualstudio.com/items?itemName=HookyQR.beautify
Example Settings
File: .jsbeautifyrc
{
"brace_style": "collapse,preserve-inline",
"max_preserve_newlines": 2,
"end_with_newline": false
}
Example Code
File: a.js
function dfs(start) {
if (start > n)
return;
ans.push(start);
for (let i = 0; i <= 9; i++)
dfs(start * 10 + i);
}

SqlDataAdapter not loading datatable - C++

I have been trying to load an SQL database into a datatable in C++, however; it doesn't seem to want to work. The connection is working though, as DataReader works. Here is my code
void importDatabase() {
SqlConnection con;
SqlDataAdapter^ da;
SqlCommand cmd;
DataTable^ dt;
int count = 1;
try {
con.ConnectionString = "Data Source=MYNAME\\SQLEXPRESS;Initial Catalog=VinylRecords;Integrated Security=True";
cmd.CommandText = "SELECT * FROM Records";
cmd.Connection = %con;
con.Open();
da = gcnew SqlDataAdapter(%cmd);
dt = gcnew DataTable("Records");
Console::Write(da->ToString());
da->Fill(dt);
for (int i = 0; i < dt->Rows->Count - 1; i++) {
String^ value_string;
value_string = dt->Rows[i]->ToString();
Console::WriteLine(dt->Rows[i]->ToString());
count++;
}
cout << "There are " << count << " many records";
}
catch (Exception^ ex) {
Console::WriteLine(ex->ToString());
}
}
Please note, that I slightly altered the source name to post here, but only the first part.
What is wrong with my code?
So, the problem is here:
dt->Rows[i]->ToString()
Rows[i] is a Row object. And the Row class's ToString() method always prints out the fully qualified typename, which is what you are seeing. So this is technically working just fine. What you will need to do to get something useful is: you will need to access a specific column in that row and get it's value, then output that.
Something along the lines of:
foreach (DataRow dr in dt.Rows)
{
Console.Write(dr.Field<int>("ColumnOne"));
Console.Write(" | ");
Console.WriteLine(dr.Field<string>("ColumnTwo"));
}
I am not entirely sure on the syntax for accessing a specific cell inside of a DataTable when using C++\CLI. So I have provided the C# equivalent to explain why it is you were getting output of managed type names (e.g. "System.Data.DataRow") instead of the info inside of the Row's columns.
Also, I noticed you tagged this question with "mysql", but you are using the ADO.NET System.Data.SqlClient namespace. The SqlDataReader and SqlDataAdapter classes only work with TSQL (Microsoft's SQL Server databases) If you are actually connecting to a mysql database you will want to use the System.Data.OdbcDataAdapter class. You can read a little more here: https://msdn.microsoft.com/en-us/library/ms254931.aspx

Execute PL/SQL script in C++ using OCCI oracle

I want to run SQL script from a C++ program. my code goes like this:
int main()
{
//.....
sql_stmt = "Insert into t1 values ('qwerty');\nInsert into t1 values ('dothar');"
"//and many more INSERT statements";
sql_stmt = "DECLARE\nrollback_check_counter number;\n"
"BEGIN\n"
"rollback_check_counter :=1;\n"
"SAVEPOINT sp_1;\nIF rollback_check_counter = 1 THEN\n"
"BEGIN\n"+sql_stmt+"EXCEPTION\n"
"WHEN PROGRAM_ERROR THEN\n"
"rollback_check_counter :=0;\n"
"ROLLBACK TO sp_1;\n"
"WHEN OTHERS THEN\n"
"rollback_check_counter :=0;\n"
"ROLLBACK TO sp_1;\n"
"END;\n"
"END IF;\n"
"commit;\n"
"END;";
try
{
Connection *conn = env->createConnection(user,passwd); //error prone
Statement *stmt = conn->createStatement();
stmt->setSQL(sql_stmt);
row_count = stmt->execute(); //stmt->execute(sql_stmt);
Connection::conn->terminateStatement(Statement *stmt);
//con->terminateStatement(stmt);
env->terminateConnection(conn);
Environment::terminateEnvironment(env);
}
catch(SQLException& ex)
{}
//.....
return 0;
}
Although when i run these insert statement only they fairly run well but when i forms a SQL Script structure they seems to fail. I want to do so because i want to implement rollback. What am i missing? Could anyone suggest any alternative to implement it.
There are ; missing after both ROLLBACK TO sp_1