SQLite database is locked on executing alter table query c# - c#

The following function is used for the newer version of my app which needs to add a column to the existing database.
public void AddColumnIfNotexist()
{
try
{
using (SQLiteCommand cmd = _DBConnection.CreateCommand())
{
cmd.CommandText = string.Format("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'myTable'");
bool hascol = false;
using (SQLiteDataReader reader = cmd.ExecuteReader())
{
if (reader.Read())
{
//does column exists?
hascol = reader.GetString(0).Contains(String.Format("\"{0}\"", "myNewColumn"));
reader.Close();
}
}
if (!hascol)
{
StringBuilder sql = new StringBuilder(SQLMAXLENGTH);
sql.Append("ALTER TABLE Groups ADD COLUMN IsStayingRatio BIT NOT NULL DEFAULT 0");
cmd.CommandText = sql.ToString();
cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + "\r\n\r\n" + ex.StackTrace);
LogApp.Write("Exception ex:" + ex.Message +"stacktrace "+ex.StackTrace, LogApp.Level.Error);
}
}
}
When I execute this gets an exception as
database is locked
Stacktrace
at System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
at System.Data.SQLite.SQLiteDataReader.NextResult()
at System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
at System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.SQLite.SQLiteCommand.ExecuteNonQuery(CommandBehavior behavior)
at System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
at DataPlus.DB.AddColumnIfNotexist() in D:\Projects\DB.cs:line 343
But this is not always happened. this happens if the size of the DB Fil is large. Also, the issue is not there while debugging from the IDE.
Is there any limitations for the Alter Table query?
Can anyone spot out the issue which makes the DB locked?

I would suggest to start a new SQLiteCommand
Note that an SQLITE_LOCKED error is distinct from SQLITE_BUSY (5).
SQLITE_BUSY means that another database connection (probably in
another process) is using the database in a way that prevents you from
using it. SQLITE_LOCKED means the source of contention is internal and
comes from the same database connection that received the
SQLITE_LOCKED error.
Sometimes people think they have finished with a SELECT statement because sqlite3_step() has returned SQLITE_DONE. But the SELECT is not really complete until sqlite3_reset() or sqlite3_finalize() have been called
Error Code SQLITE_LOCKED (6): Database Is Locked

Adding the following lines after reader.Close() worked for me.
GC.Collect();
GC.WaitForPendingFinalizers();
It seems that the DB was not released on executing the ALTER query.
Got the hint from here.

Related

An open datareader associated with this command error in C#

I want to build a simple loop to check incoming data from SQL server, compare it to a textfield, and execute non query if there are no duplicates.
I wrote this code:
try
{
bool exists = false;
conn = new SqlConnection(DBConnectionString);
SqlCommand check_user = new SqlCommand("SELECT usrEmail FROM tblUsers", conn);
SqlCommand add_user = new SqlCommand("INSERT INTO tblUsers (usrEmail, usrPassword, usrRealname, usrIsowner) VALUES (#email, #pass, #name, #owner)", conn);
// (I have removed all the paramaters from this code as they are working and irrelevant)
conn.Open();
SqlDataReader check = check_user.ExecuteReader();
while (check.Read())
{
if (Convert.ToString(check[0]) == UserEmail.Text)
{
MessageBox.Show("The email you entered already exists in the system.");
exists = true;
break;
}
}
if (exists == false)
{
add_user.ExecuteNonQuery();
}
else
{
return;
}
}
catch (Exception ex)
{
MessageBox.Show("There was a problem uploading data to the database. Please review the seller's details and try again. " + ex.Message);
return;
}
finally
{
conn.Close();
}
I used breakpoints and saw that the code runs the while loop fine, but when it reaches the ExecuteNonQuery command, it returns an error message:
there is already an open datareader associated with this command which
must be closed first
I tried to use a check.Close(); command, but when I do, it suddenly gets stuck with the duplicate email error message for reasons passing understanding.
Additionally, there was a fix I tried in which the data actually WAS sent to the database (I saw it in SQL Server Management Studio), but still gave an error message... That was even stranger, since the nonquery command is the LAST in this function. If it worked, why did it go to the catch?
I have searched the site for answers, but the most common answers are MARS (I have no idea what that is) or a dataset, which I do not want to use in this case.
Is there a simple solution here? Did I miss something in the code?
The simples way out would be:
using(SqlDataReader check = check_user.ExecuteReader())
{
while (check.Read())
{
if (Convert.ToString(check[0]) == UserEmail.Text)
{
MessageBox.Show("The email you entered already exists in the system.");
exists = true;
break;
}
}
}
That said, there are some serious problems with this code.
First of all, you don't really want to read all users just to check that an email address is already taken. select count(*) from tblUsers where usrEmail = #email is fine...
...or not, because there's a possibility of a race condition. What you should do is add a unique constraint on a usrEmail column and just insert into tblUsers, catching violations. Or you can use merge if you feel like it.
Next, you don't really want to have your data access code all over the place. Factor it out into separate classes/methods at least.

Having trouble with a SQL Server CE insertion function

I've written the below function, which errors out correctly with non-int input and with int input returns that the audit was started properly. Unfortunately when I check the table I see that the data was never actually inserted.
Any suggestions for what I'm doing wrong?
public string SqlLocation = "Data Source="+ new FileInfo(Path.GetDirectoryName(Application.ExecutablePath) + "\\DRAssistant.sdf");
public string StartAudit(string sqlLocation, string dps)
{
int dpsInteger;
if (!int.TryParse(dps, out dpsInteger))
return "DPS is not a number!";
try
{
var myConnection = new SqlCeConnection(sqlLocation);
myConnection.Open();
var myCommand = myConnection.CreateCommand();
myCommand.CommandText = string.Format("SELECT dps FROM DispatchReviews
WHERE dps = {0}", dpsInteger);
SqlCeDataReader reader = myCommand.ExecuteReader();
if (reader.Read())
{ return "DPS review has already started!"; }
myCommand.CommandText = "INSERT INTO DispatchReviews (dps, starttime,
reviewer) VALUES (#dps, #starttime, #reviewer)";
myCommand.Parameters.AddWithValue("#dps", dpsInteger);
myCommand.Parameters.AddWithValue("#starttime", DateTime.Now);
myCommand.Parameters.AddWithValue("#reviewer", Environment.UserName);
myCommand.Prepare();
myCommand.ExecuteNonQuery();
myCommand.Dispose();
myConnection.Close();
return "Dispatch Review Started!";
}
catch(Exception ex)
{ return "Unable to save DPS!" + ex.Message; }
}
Edit: Turns out this was just an idiot problem--which anybody looking at the SqlLocation could probably figure out--in that every time I built the application a new copy of the .sdf was copied into the application directory, overwriting the previous one. Also, the database I was checking for updates was not the one in the execution directory, but the one that was being copied into it, which is why it was always empty. I noticed this because when I tried to add the same DPS multiple times the first time I would get the DPS review started message, but subsequent attempts would give the error that it had previously been created.
Can you please show us your connection string??
Most likely, if you test this inside Visual Studio, the database file is being copied around (from your initial directory to the output directory where the app runs) and your INSERT will probably work just fine - but you're just looking at the wrong file when you check that fact.

C#, SQL Server 2005 and missing session

I have a problem with a customer. I have this code:
var conn = new SqlConnection(Util.GetConnectionString());
var DataCommand = new SqlCommand();
var sql = "";
// subseccion
try
{
sql = "TRUNCATE TABLE preview_" + tablename;
DataCommand = new SqlCommand(sql, conn);
DataCommand.Connection.Open();
int numcol = DataCommand.ExecuteNonQuery();
sql = "insert into preview_" + tablename+ " select * from " + tablename;
DataCommand = new SqlCommand(sql, conn);
DataCommand.ExecuteNonQuery();
}
catch (Exception ex)
{
var latest_error = ex.Message;
Util.Add_Event_Log(latest_error);
}
finally
{
DataCommand.Dispose();
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
conn.Dispose();
}
This do the next thing, I give a name of a table, it TRUNCATE a table then copy the information from "table" to "preview_table" and it works as expected.
However, we found that if we don't give TRUNCATE permission for the table, it fail. But, my problem is that it does not only fail but also deleting the current session (and may be also restart the server process).
My bet it is a server problem (server 2003) may be it is not patched or anything because I am working inside a try-catch part so it should not fail in this fashion.
My customers says the problem is in the code.
But I am not sure, maybe I should not a sql command in a chain.
Is this happening in the development environment as well as production environment? If so, you need to step through your code with the VS debugger and pin point the line at which the session is being deleted.
You should also check the event logs on the production server to see if they can provide any information.
As stated in the comments by msergey, it may be the Util.Add_Event_Log throwing an exception but you should test this by stepping through the code.
If it is Util.Add_Event_Log causing the issue, move this code out of the catch into its own try/catch statement by declaring an exception variable in the outer scope.
If it does wind up that the use of TRUNCATE is the culprit you might try swapping that out in favor of using a DELETE statement instead. Performance won't be as great, but you wouldn't require elevated user permissions in SQL Server either.

SQL Reading from a DB problem using a DataReader

I have a table of Users (tblUsers) which contains details of University staff. I am trying to populate a text box with the names of lecturers associated with a selected module.
I am getting all UserIDs associated with a particular module, testing if the User is a lecturer, if so then I add the ID to an ArrayList.
I then iterate through this array and call the method below during each iteration passing through the current ID.
However, if you look at the method below I am using a SqlDataReader and am getting an error while reading from it on this line:
txtLecturerName.Text += myReader["First_Name"].ToString();
The error message is:
'myReader["First_Name"]' threw an exception of type 'System.IndexOutOfRangeException'
The table layout I am using is below the method code. Any help with this would be greatly appreciated, I am one cup of coffee away from putting my head through the screen.
public void outputLecturerNames(string lecturerID)
{
// Create a new Connection object using the connection string
SqlConnection myConnection = new SqlConnection(conStr);
// If the connection is already open - close it
if (myConnection.State == ConnectionState.Open)
{
myConnection.Close();
}
// 'using' block allows the database connection to be closed
// first and then the exception handling code is triggered.
// This is a better approach than using a 'finally' block which
// would close the connection after the exception has been handled.
using (myConnection)
{
try
{
// Open connection to DB
myConnection.Open();
SqlCommand selectCommand = new SqlCommand(selectQuery, myConnection);
// Declare a new DataReader
SqlDataReader myReader;
selectQuery = "SELECT * FROM tblUsers WHERE User_ID='";
selectQuery += lecturerID + "'";
myReader = selectCommand.ExecuteReader();
while (myReader.Read())
{
txtLecturerName.Text += myReader["First_Name"].ToString();
txtLecturerName.Text += " ";
txtLecturerName.Text += myReader["Last_Name"].ToString();
txtLecturerName.Text += " , ";
}
myReader.Close();
}
catch (Exception err)
{
Console.WriteLine("Error: " + err);
}
}
}
tblUsers:
[User_ID][First_Name][Last_Name][Email_Address]
In your method, the variable selectQuery is not declared, and it is used as parameter to SqlCommand before it is assigned the query string on tblUsers.
You've probably misspelled a column name.
In general, you should never write SELECT * FROM ....
Instead, you should select only the columns you need.
This will make your program run faster by only querying the information that you need, and can produce better error messages.
This error is created when the column name given is not found. If you are anything like me, you've probably checked it several times, but is the table name correct (correct database, correct schema) and is the column name correct?
http://msdn.microsoft.com/en-us/library/f01t4cfy.aspx
You might try fully qualifying the name of the table (database.dbo.tblUsers). This would ensure that you are hitting the table you think you are. Also, try and put the names of the columns into the SQL statement. If they are not correct, your SQL statement will not execute properly.

SQLITE - INSERT does not return error but no data is inserted

I am attempting to insert data into a local SQLITE database file from a C# application. The transaction does not throw any errors but the data is not inserted. The same insert statement works from within a query analyzer.
Do I need to perform a commit? Is there a Commit method?
Command's transaction property is null..
var command = new SQLiteCommand(insert.BuildInsert(tableName,keyValuePairs),Connection);
command.ExecuteNonQuery();
UPDATE:
I have also tried to assciate my SQLiteCommand with a SQLiteTransaction but have had no luck.
try
{
SQLiteTransaction liteTransaction = Connection.BeginTransaction();
SQLiteCommand command = new SQLiteCommand(insert.BuildInsert(tableName, keyValuePairs), Connection);
command.Transaction = liteTransaction;
command.ExecuteNonQuery();
liteTransaction.Commit();
}
catch (SQLiteException e)
{
//error
connection.Close();
}
The BuildInsert method just constructs a string that is the INSERT. The insert works fine in a query analyzer.
public string BuildInsert(string tableName, IDictionary<string, string> testDataDic)
{
stringBuilder = new StringBuilder(String.Format("INSERT INTO {0} ", tableName));
AddColumns(stringBuilder,testDataDic.Keys);
AddValues(stringBuilder, testDataDic.Values);
return stringBuilder.ToString();
}
This is an example of the INSERT statement. This works fine outside of the code but does not throw any errors:
INSERT INTO TestData (walking,running,image,yoga,exercise,meditation,hobby,somethingelse,howoften,numtechniques,userreturn,chosentechnique,chosentechnique)VALUES ("true","I will try to use deep breathing.","false","true","false","false","true","true","When I'm stressed","1","true","deepbreathing","deepbreathing"); COMMIT;
DOH! So the local database build operation was set to 'Always copu' I have multiple versions of the same file.
Sorry for that. That's lame.

Categories