Problems executing SQL-script using Firebird.NET 2.5 (Error Code = -104) - sql

Sorry for my English first of all. I have a problem and need help.
I have a simple tool made by myself on c#. This tool makes connect to local or remote firebird server (v.2.5). And my tool can create specified .fdb file (database) somewhere on the server.
Also I have a file with SQL statements (create table, triggers and so on). I want to execute this file after database was created. Executing this file will fill structure of user database - not data, only structure.
But then I try to execute my SQL script - firebird server returns a
SQL error code = -104 Token unknown line xxx column xxx.
That's the line on this CREATE TABLE SQL statement, for example:
CREATE TABLE tb1
(
col1 INTEGER NOT NULL,
col2 VARCHAR(36)
);
/* This next create statement causes an error */
CREATE TABLE tb2
(
col1 INTEGER NOT NULL,
col2 VARCHAR(36)
);
If I will leave only one create statement in my file - all will be good... I don't know how I explained (it's clear or not)) - another words - why can't I execute full query with many create statements in one transaction? There is my main method which executes query:
public static string Do(string conString, string query)
{
using (FbConnection conn = new FbConnection())
{
try
{
conn.ConnectionString = conString;
conn.Open();
FbTransaction trans = conn.BeginTransaction();
FbCommand cmd = new FbCommand(query, conn, trans);
cmd.ExecuteNonQuery();
trans.Commit();
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex.ToString());
return "Transaction Fail";
}
}
return "Transaction Commited";
}
There is a query is my SQL file.

As Victor already stated in his final comment, you can use the FBScript class for batch execution.
I was just confronted with the same task. This question pointed me in the right direction but i had to do some further digging.
I this example, the source of the statements is a external script file:
private void ExecuteScript(FbConnection myConnection, string scriptPath) {
if (!File.Exists(scriptPath))
throw new FileNotFoundException("Script not found", scriptPath);
FileInfo file = new FileInfo(scriptPath);
string script = file.OpenText().ReadToEnd();
// use FbScript to parse all statements
FbScript fbs = new FbScript(script);
fbs.Parse();
// execute all statements
FbBatchExecution fbe = new FbBatchExecution(myConnection, fbs);
fbe.Execute(true);
}
This will work fine, but you may wonder why this whole thing isn't surrounded by a transaction. Actually there is no support to "bind" FbBatchExecution to a transaction directly.
The first thing i tried was this (will not work)
private void ExecuteScript(FbConnection myConnection, string scriptPath) {
using (FbTransaction myTransaction = myConnection.BeginTransaction()) {
if (!File.Exists(scriptPath))
throw new FileNotFoundException("Script not found", scriptPath);
FileInfo file = new FileInfo(scriptPath);
string script = file.OpenText().ReadToEnd();
// use FbScript to parse all statements
FbScript fbs = new FbScript(script);
fbs.Parse();
// execute all statements
FbBatchExecution fbe = new FbBatchExecution(myConnection, fbs);
fbe.Execute(true);
myTransaction.Commit();
}
}
This will result in an exception stating: "Execute requires the Command object to have a Transaction object when the Connection object assigned to the command is in a pending local transaction. The Transaction property of the Command has not been initialized."
This means nothing more than that the commands that are executed by FbBatchExecution are not assigned to our local transaction that is surrounding the code block. What helps here is that that FbBatchExecution provides
the event CommandExecuting where we can intercept every command and assign our local transaction like this:
private void ExecuteScript(FbConnection myConnection, string scriptPath) {
using (FbTransaction myTransaction = myConnection.BeginTransaction()) {
if (!File.Exists(scriptPath))
throw new FileNotFoundException("Script not found", scriptPath);
FileInfo file = new FileInfo(scriptPath);
string script = file.OpenText().ReadToEnd();
// use FbScript to parse all statements
FbScript fbs = new FbScript(script);
fbs.Parse();
// execute all statements
FbBatchExecution fbe = new FbBatchExecution(myConnection, fbs);
fbe.CommandExecuting += delegate(object sender, CommandExecutingEventArgs args) {
args.SqlCommand.Transaction = myTransaction;
};
fbe.Execute(true);
// myTransaction.Commit();
}
}
Note that i have uncommented the myTransaction.Commit() line. I was a little bit surprised by this behavior, but if you keep that line the transaction will throw an exception stating that it has already been committed. The bool parameter fbe.Execute(true) is named "autoCommit", but changing this to false seems to have no effect.
I would like some feedback if you see any potential issues with assigning the local transaction this way, or if it has any benefits at all or could as well be omitted.

Probably error in launching two create statements in one batch. Would it work if you break it to separate queries? Does it work in your SQL tool?

Related

Groovy SQL Execute Parameters not getting replaced

This problem is frustrating me so much, that I've finally created a Stackoverflow Account - this is my first question here, go easy on me.
I've got an SQL Script like this:
select * from d_table
where id = :line;
I'd like to execute that query from a groovy script. So far I've written this:
static void main(String[] args) {
String dbURL = 'some.db:Port:serviceName'
String dbUser = 'dbUser'
String dbPw = 'aReallyStrongPassword'
// connect to SQL Instance and DB
def sql = Sql.newInstance(dbURL, dbUser, dbPw, 'oracle.jdbc.OracleDriver')
// read Inputfile from local dir
new File("inputfile.csv").eachLine { inputLine ->
// loop over Inputfile
try {
// read SQL Script from local dir
String sqlString = new File("script.sql").getText("UTF-8")
// execute SQL Script, replace :line with input-ID
resp = sql.execute sqlString, line: inputLine
} catch(Exception e) {
println(e)
}
}
// close SQL Session
sql.close()
}
But I'll always get an error like this:
WARNING: Failed to execute: select * from d_table
where id = :line; because: ORA-00933: SQL command not properly ended
I've got the feeling the query is not built properly, but I don't know whats the problem?
Using groovy 3.0.4.

JPA Entity Manager - How to run SQL script file?

I have an SQL script file which drops and recreates various tables as well as inserts various records into these tables. The script runs fine when executing in the SQL query console however I need it to be executed by the Entity Manager.
Any idea's on how I would be able to do this?
Thanks,
H
Late to the party but here's how I do it. Couple of things to note here:
My SQL file ("sql-queries.sql") is on the classpath - you could do this any other way that will get you an input stream...
My SQL file has 1 statement per line
I'm manually beginning/committing transactions, one for each line/statement in the file
Here's the method to execute the file:
void executeStatements(BufferedReader br, EntityManager entityManager) throws IOException {
String line;
while( (line = br.readLine()) != null )
{
entityManager.getTransaction().begin();
entityManager.createNativeQuery(line).executeUpdate();
entityManager.getTransaction().commit();
}
}
Here's how I call it:
InputStream sqlFileInputStream = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("geo-data.sql");
// Convert input stream to something that can be read line-by-line
BufferedReader sqlFileBufferedReader = new BufferedReader( new InputStreamReader(sqlFileInputStream));
executeStatements(sqlFileBufferedReader, dao.getEntityManager());
I tested nominally with 1 transaction instead of 1 per statement (note that this means that 1 bad query will break everything) and the time to execute is the same:
void executeStatements(BufferedReader br, EntityManager entityManager) throws IOException {
String line;
entityManager.getTransaction().begin();
while( (line = br.readLine()) != null )
{
entityManager.createNativeQuery(line).executeUpdate();
}
entityManager.getTransaction().commit();
}

SqlCommand.ExecuteReader Fails to Report Errors

I'm executing a SQL command to create a new record in a database table and get the ID of the created record. However, there's a constraint error generated by the SQL command (uninitialized non-null field) which is not being picked up by the VB code. The code is roughly:-
connection = New SqlConnection(connection_string)
connection.Open()
sql_command = New SqlCommand(command) 'command = the SQL command to execute
sql_command.Connection = connection
sql_command.Parameters.AddRange(sql_parameters.ToArray()) ' sql_parameters is a parameter to the function
reader = sql_command.ExecuteReader()
If reader IsNot Nothing Then
If reader.HasRows Then
While reader.Read
response_handler(reader, data) 'response handler is a callback which populates the data object
End While
End If
reader.Close()
End If
The reader object is non-null but contains no data and no exception is generated. The SQL command is:-
insert into [table] ([column1], [column2], [column3], [column4])
output Inserted.[pk]
values (#1, #2, #3, #4)
Executing the SQL statement using SQL Server Management Studio, I get the error:-
Msg 515, Level 16, State 2, Line 2
Cannot insert the value NULL into column 'somecolumn', table 'tablename'; column does not allow nulls. INSERT fails.
I have also added a handler for the InfoMessage event on the SqlConnection object but that doesn't get called, even when I set FireInfoMessageEventOnUserErrors to true.
Why am I not getting an error and what is the correct way to ensure the error is reported to VB?
I'm using Visual Studio 2008.
The HasRows call returns false if there is an error. That way you will never see the error. Remove both If statements. The null check is redundant, the other one suppresses errors.
The severity level is 16 which does not interrupt the current session. If you want to throw a hard error you could add something like this directly after your insert...
If ##Error <> 0
Begin
Raiserror('Constraint Error Encountered',20,1) With Log;
End
The security context will have to have sysadmin rights in order to perform the RAISERROR WITH LOG, but if you can't do this I'm sure there are other ways to throw a hard error. In any event a warning isn't going to throw an error to your VB code.
I don't know exactly how it works internally, but the property HasRows is set by a private method within the SqlDataReader class called something like TryGetNextResult, which uses a lot of try/catch blocks to set a boolean output parameter, so this method never throws any exception, or even provides any feedback about any errors. What I can't work out is exactly how it realises there will be errors without even attempting the insert, but it does.
With this sample table:
CREATE TABLE T (ID INT IDENTITY, A INT NOT NULL);
I ran:
string sql = #"INSERT T (A) OUTPUT inserted.ID, inserted.A VALUES (1);";
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand(sql, connection))
{
connection.Open();
using (var reader = command.ExecuteReader())
{
Console.WriteLine(reader.HasRows); // True
while (reader.Read())
{
Console.WriteLine(reader.GetInt32(1)); // 1
}
}
}
When I changed this to try and insert null, reader.HasRows was false, but by putting a breakpoint in I was able to test before reader.Read() was called, and this shows that the identity value of the table T was unchanged, so the reader can't be validating inserts by simply rolling back transactions. It is a mystery to me why a SqlDataReader is able to identify that this insert will fail before execution, but yet if the SQL is executed it will still attempt the insert before raising an error.
To further prove the behaviour I ran a similar test with a simple select command, and saw the same behaviour:
static void Main(string[] args)
{
string sql = #" SELECT Date = CAST(D AS DATE)
FROM (VALUES
(1, '20130129'),
(2, '20130130'),
(3, '20130131'),
(4, '20130132')
) t (A, D)
ORDER BY A;";
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand(sql, connection))
{
connection.Open();
using (var reader = command.ExecuteReader())
{
Console.WriteLine(reader.HasRows); // false
while (reader.Read()) // Exception
{
Console.WriteLine(reader.GetString(0));
}
}
}
}
Again this shows, that somehow the SqlDataReader has picked up that there will be an error casting 20130132 to a date. If I remove this row from the SQL the HasRows property becomes true.
I realise this doesn't actually answer your question but it was too long for a comment, hopefully it will help a little, and/or maybe prompt somebody who knows a lot more about c# than me to add their own answer.

SQL ERROR: The connection was not closed. The connection's current state is open

EDIT
After staring at this for 2 days, I do see one issue. I was still opening the original connection. So I changed the inner open statements to conn2.Open. Then, I changed the second inner query to where all the variables were number 3 instead of 2 so that they were completely different than the previous query. At that point, I got the error:
There is already an open DataReader associated with this Command which must be closed first.
I took out the inner connections, thinking I could use the outer connection and took out the inner .Close lines, but that also returned an error saying the connection was not closed.
END EDIT
I am writing a script that updates user information with data pulled from other tables where that user may be in it multiple times for purchases made.
So first, the "outside" sql query pulls some data from the items table which contains purchaser information as well as category information. For each item, it is going to check it's purchaser's information.
Second, the first "inner" sql query pulls category information from the user table. Some code is then run to see if they're already marked as purchasing from the category of the "outside" query. If they are not, it adds the category to a string variable.
Lastly, the second "inner" sql query updates the user table for the current user with the new category list.
I've asked about how to perform queries like this before, but was always given a solution of combining the queries into one. That worked for the other queries, but I cannot do that here. I must iterate through each record of the outer query to perform the necessary functions inside of it. But my issue here is that I get an SQL error saying that the connection was not closed, and it points to the catch of the outer query (for 'conn').
I had tried to set my 2 inner queries so that they used different connection variables (conn2 and conn3), and also different strSQL variables, but that didn't help. And I'm still a newb when it comes to SQL, having programmed using MySQL until this probject. Any help would be greately appreciated.
using (SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["connectionName"].ToString()))
using (SqlCommand strSQL = conn.CreateCommand())
{
strSQL.CommandText = "SELECT field FROM itemsTable";
try
{
conn.Open();
using (SqlDataReader itemReader = strSQL.ExecuteReader())
{
while (itemReader.Read())
{
{Do some stuff here}
using (SqlConnection conn2 = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["connectionName"].ToString()))
using (SqlCommand strSQL2 = conn2.CreateCommand())
{
strSQL2.CommandText = "SELECT fields FROM userTable";
try
{
conn2.Open();
using (SqlDataReader itemReader2 = strSQL2.ExecuteReader())
{
while (itemReader2.Read())
{
{Do stuff here}
}
itemReader2.Close();
}
}
catch (Exception e3)
{
throw new Exception(e3.Message);
}
finally
{
conn2.Close();
}
}
{Do some more stuff here}
using (SqlConnection conn2 = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["connectionName"].ToString()))
using (SqlCommand strSQL2 = conn2.CreateCommand())
{
strSQL2.CommandText = "UPDATE userTable set field='value'";
try
{
conn2.Open();
strSQL2.ExecuteNonQuery();
}
catch (Exception e2)
{
throw new Exception(e2.Message);
}
finally
{
conn2.Close();
}
}
{Do even more stuff here.}
}
itemReader.Close();
}
}
catch (Exception e1)
{
throw new Exception(e1.Message);
}
finally
{
conn.Close();
}
}
There's some unusual logic going on with conn.Open(). I see it used several times, but I think you mean to use conn2.Open() in the inner using statements after the first call.

Partial replace on SQL image data column

This question is related to another one I posted earlier.
To recap, I need to fix an issue with an ancient legacy app where people messed up data storage by re-installing the software the wrong way.
The application stores data by saving a record in an SQL DB. Each record holds a reference to a file on disk of which the filename auto-increments.
By re-installing the app the filename auto-increment was re-set so the DB now holds multiple unrelated records which reference the same filename and I have to directories with files which I obviously cannot merge because of these identical filenames. The files hold no reference to the DB data so the only course of action that remains is to filter the DB records on date created and try to rename "EXED" to "IXED" or something like that.
The DB is relatively simple with one table containing a column that holds data of type "Image".
An example content of this image data is as follows:
0x3200001000000000000000200B0000000EFF00000300000031340000000070EC0100002C50000004000000C90000005D010000040000007955B63F4D01000004000000F879883E4F01000004000000BC95563E98010000040000009A99993F4A01000004000000000000004B01000004000000000000009101000004000000000000004E01000004000000721C83425101000004000000D841493F5E01000004000000898828414101000004000000F2D2BD3F4201000004000000FCA9B13F40010000040000007574204244010000040000000000204345010000040000007DD950414601000004000000000000004701000004000000000000009201000004000000000000008701000004000000D2DF13426A0100000400000000005C42740100000400000046B68F40500100000400000018E97A3F7901000004000000FB50CF3C7A01000004000000E645703F99010000040000000000E0404C010000040000008716593F8601000004000000000006439A0100000400000000008040700100000400000063D887449E01000004000000493CBA3E9C0100000400000069699D429B01000004000000DD60CA3F9D0100000400000035DE3C44B4010000040000008B5C744433000000040000003D0ABB4134000000040000000AFF7C44350000000400000093CB3942750400000400000054A69F41BA010000040000002635C64173040000040000008367C24100000080690100002B5000003101000032000010000000000000002009000000000000000100000000000000F00000000000000080080100000100000010000000540100000100000021F0AA42270000000200000010000000540100000200000021F0AA42280000000300000010000000540100000300000059C9E6432900000004000000100000005401000004000000637888442A00000005000000100000005401000005000000DFEF87442B00000006000000100000005401000006000000000000002C00000007000000100000005401000007000000000000002D00000008000000100000005401000008000000000000002D000000090000001000000054010000090000002F353D442D0000000A00000010000000540100000A00000035DE3C44340000000B00000010000000540100000B0000008B5C7444240000009D50000010000000CDCCCC3E2C513B41F65D5F3F2C51BB419E50000010000000CCBA2C3FE17C8C411553B13F83F32142000000403700000000FE0000090000004558454434386262002D50000008000000447973706E6F65008E5000000E00000056454C442052414D502033363000000000F000000000
The data is apparently Hex which mostly encodes meaningless crap but also holds the name of physical files (towards the end of the data field) in the filesystem that is linked to the SQL records:
??#7???????????EXED48bb?-P??????Dyspnoe??P??????VELD RAMP 360
I'm interested in the EXED part.
There is no clear regularity in the offset at which the filename appears and the filename is of variable length (so I do not know beforehand how long the substring will be).
I can call up all records with SQL like this:
SELECT COUNT(*) as "Number of EXED Files after critical date"
FROM [ZAN].[dbo].[zanu]
WHERE udata is not null
and SUBSTRING(udata, 1 , 2147483647) like '%EXED%'
and [udatum] > 0
and CONVERT(date,[udatum]) > CONVERT(date,'20100629')
What I would like to do now is know how to replace this EXED substring by something else (e.g. IXID).
I'm unfamiliar with SQL and Googling so far has yielded very little information on my options here.
I also have no other info on the original code that generated this data/the data format/encoding/whatever...
It's a mess really.
Any help is welcome!
An update on this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Threading;
namespace ZANLinq
{
class Program
{
static void Main(string[] args)
{
try
{
DataContext zanDB = new DataContext(#"Data Source=.\,1433;database=ZAN;Integrated Security=true");
string strSQL = #"SELECT
Idnr,
Udatum,
Uzeit,
Unr,
Uart,
Ubediener,
Uzugriff,
Ugr,
Uflags,
Usize,
Udata
FROM Zanu
WHERE (Udata IS NOT null and SubString(Udata, 1 , 2147483647) LIKE '%EXED%')
AND (Idnr = ' 2')";
var zanQuery = zanDB.ExecuteQuery<Zanu>(strSQL);
List<Zanu> list = zanQuery.ToList<Zanu>();
foreach (Zanu zanTofix in list)
{
string strOriginal = ASCIIEncoding.ASCII.GetString(zanTofix.Udata);
string strFixed = strOriginal.Replace("EXED", "IXED");
zanTofix.Udata = ASCIIEncoding.ASCII.GetBytes(strFixed);
}
zanDB.SubmitChanges();
//Console.WriteLine(zanResults.Count<Zanu>().ToString());
}
catch (SqlException e)
{
Console.WriteLine(e.Message);
}
}
}
}
It finds the records I'm interested in, I can easily manipulate the data but the commit doesnt work. I'm stumped, there are no exceptions, no indication the code is wrong.
Anybody have ideas?
UPDATE:
I think the above does not work because my table appears to have a composite PK (I cannot change this):
Since I could not debug this (no info anywhere, no exceptions, just a silent fail of the submitchanges()) I decided to use another approach and abandon Linq2SQL altogether:
try
{
SqlConnection thisConnection = new SqlConnection(#"Network Library=DBMSSOCN;Data Source=.\,1433;database=ZAN;Integrated Security=SSPI");
DataSet zanDataSet = new DataSet();
SqlDataAdapter zanDa;
SqlCommandBuilder zanCmdBuilder;
thisConnection.Open();
//Initialize the SqlDataAdapter object by specifying a Select command
//that retrieves data from the sample table.
zanDa = new SqlDataAdapter(#"SELECT
Idnr,
Udatum,
Uzeit,
Unr,
Uart,
Ubediener,
Uzugriff,
Ugr,
Uflags,
Usize,
Udata
FROM Zanu
WHERE (Udata IS NOT null and SubString(Udata, 1 , 2147483647) LIKE '%IXED%')
AND (Idnr = ' 2')
AND (Uzeit = '13:21')", thisConnection);
//Initialize the SqlCommandBuilder object to automatically generate and initialize
//the UpdateCommand, InsertCommand, and DeleteCommand properties of the SqlDataAdapter.
zanCmdBuilder = new SqlCommandBuilder(zanDa);
//Populate the DataSet by running the Fill method of the SqlDataAdapter.
zanDa.Fill(zanDataSet, "Zanu");
Console.WriteLine("Records that will be affected: " + zanDataSet.Tables["Zanu"].Rows.Count.ToString());
foreach (DataRow record in zanDataSet.Tables["Zanu"].Rows)
{
string strOriginal = ASCIIEncoding.ASCII.GetString((byte[])record["Udata"]);
string strFixed = strOriginal.Replace("IXED", "EXED");
record["Udata"] = ASCIIEncoding.ASCII.GetBytes(strFixed);
//string strPostMod = ASCIIEncoding.ASCII.GetString((byte[])record["Udata"]);
}
zanDa.Update(zanDataSet, "Zanu");
thisConnection.Close();
Console.ReadLine();
}
catch (SqlException e)
{
Console.WriteLine(e.Message);
}
This seems to work but any input on why the Linq does not work and whether or not my second solution is efficient/optimal or not is still very much appreciated.