I am trying to add a section that can add extra columns to a table so I am trying a parametrised ALTER TABLE query. However when I run it I am getting an error :
SQL logic error near "#TableName":syntax error
Code:
public void addNewField(string tableName, string newFieldName, string dataType)
{
SQLiteConnection dbConnection = new SQLiteConnection(connectionStrings.getSQLiteDBConnectString());
string sql = #"ALTER TABLE #TableName ADD #NewFieldName #dataType";
dbConnection.Open();
SQLiteCommand cmd = new SQLiteCommand(sql, dbConnection);
cmd.Parameters.AddWithValue("#TableName", tableName);
cmd.Parameters.AddWithValue("#NewFieldName", newFieldName);
cmd.Parameters.AddWithValue("#dataType", dataType);
try
{
cmd.ExecuteNonQuery();
} catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
dbConnection.Close();
}
Why am I getting the error? I tried different field names and have run the SQL string (with # variables replaced) in a part of my application that allows to run free typed queries and the base query worked.
With SQLite, the only kind of parameterization possible is parameterized values in DQL/DML statements. It is not possible to provide any database object identifier names as query parameters to any kind of statements.
Parameterization is intended to separate user-supplied values from the SQL code. And the user should not be provided with ability to change the schema. Otherwise, RDBMS may not be a good fit for your application.
Related
Hello so i m creating a registration form in C# with MySql so it connects to the database and everything but i get this error Napaka pri registraciji Unknown column " in 'field list' the translation of Napaka pri registraciji means Error at registering i just have it in my language. I get this error when i insert data in textboxes and press Register..
the code:
private void btn_Reg_Click(object sender, EventArgs e)
{
MySqlConnection dataConnection = new MySqlConnection();
dataConnection.ConnectionString = "datasource=localhost;port=3306;username=root;password=";
dataConnection.Open();
MySqlTransaction transakcija = dataConnection.BeginTransaction();
MySqlCommand dataCommand = new MySqlCommand();
dataCommand.Connection = dataConnection;
dataCommand.Transaction = transakcija;
try
{
dataCommand.CommandText = "Insert INTO lr.users (upIme,geslo) VALUES (`"+this.tB_upIme.Text+"`,`"+this.tB_geslo.Text+"`)";
dataCommand.CommandType = CommandType.Text;
dataCommand.ExecuteNonQuery();
transakcija.Commit();
MessageBox.Show("Registracija uspešna!");
}
catch (Exception eks)
{
transakcija.Rollback();
MessageBox.Show("Napaka pri registraciji\n" + eks.Message);
}
finally
{
dataCommand.Connection.Close();
}
}
There are two things I immediately see wrong here...
First, you're using back ticks to wrap your values. In MySQL Back ticks represent database objects, so the query is looking for objects named by those values instead of using the values themselves. So instead of this:
`"+this.tB_upIme.Text+"`
You'd want this:
'"+this.tB_upIme.Text+"'
Second, and vastly more importantly, your code is wide open to SQL injection attacks. You'll want to use query parameters, not direct string concatenation. While it may look like you're just putting values into the query string, you're actually taking user input and treating it as executable code in your query string, which means users can run any arbitrary code they want on your database.
First, add parameters to your query:
"Insert INTO lr.users (upIme,geslo) VALUES (#upIme, #geslo)"
(You'll notice this also makes the query a heck of a lot cleaner and easier to read.) Then add your parameters to the command:
dataCommand.Parameters.AddWithValue("#upIme", this.tB_upIme.Text);
dataCommand.Parameters.AddWithValue("#geslo", this.tB_geslo.Text);
Then when you execute that command it will treat the user-input values as values instead of as executable code.
Change to single quotes ' in the values.
dataCommand.CommandText =
"Insert INTO lr.users (upIme,geslo)
VALUES ('"+this.tB_upIme.Text+"','"+this.tB_geslo.Text+"');";
To get a columns' DataType I'm using following code in C#
System.Data.SQLite.SQLiteConnection con = new System.Data.SQLite.SQLiteConnection("Data Source=C:\\Users\\SaMax\\Documents\\CustomersDB.s3db; Version=3; FailIfMissing=True;");
System.Data.SQLite.SQLiteCommand cmd = new System.Data.SQLite.SQLiteCommand("select typeof(Column_Name) from Table_Name;");
cmd.Connection = con;
try
{
con.Open();
string str = cmd.ExecuteScalar().ToString();
con.Close();
MessageBox.Show(str);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
It works. But I was wondering if it's OK, and OK for SQL, MySql as well.
I was wondering if it's OK
Hard to tell without knowing your intentions, but unless your are coding something highly dynamic the chances are that you already know before-hand what is the type of the column. Why are you running this query?
and OK for SQL
It is not standard SQL, it's a function specific to SQLite. Other SQL flavors might have something similar to obtain the type of a column, but you can't use that same SQL string and expect it to work on any database.
For SQL-Server
Try This:
SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS where
TABLE_NAME='Product' and COLUMN_NAME='Id'
I have a connection to a database with right to another. I want to call a procedure on the other database which has a user table data type parameter. But the user table data type isn't found whatever I try.
I tried using database name in front of [dbo].[myType] but it's not a valid syntax.
I tried creating the same type in the current database
I tried creating the same type in the model database
I tried appending "USE MyOtherDatabase" at the top of my SqlCommand.Text
Everything failed (I'm really abashed the "USE ..." approach failed).
How can I do that?
Sample of code:
// While connected to MyOtherDatabase
CREATE TYPE dbo.typeClubMembersVersion AS TABLE (
ID INT
, UNIQUE(ID)
, [version] INT
)
GO
CREATE PROCEDURE dbo.spCheckCMembersMods (
#pVersions AS dbo.typeClubMembersVersion READONLY
, #pWhoID AS BIGINT
)
AS
BEGIN
[...]
END
SqlCommand com = new SqlConnection(functions.ConnectionString).CreateCommand();
com.CommandText = #"
// While connected to CurrentDatabase
USE MyOtherDatabase
DECLARE #tbl AS dbo.typeClubMembersVersion
BEGIN TRANSACTION
UPDATE dbo.tClubMembers
SET
Title = #Title
OUTPUT inserted.ID, deleted.[version] INTO #tbl (ID, [version])
WHERE IdMember = #IdMember
EXEC dbo.spCheckCMembersMods #tbl, #whoID
COMMIT
";
com.Parameters.Add("#Title", SqlDbType.NVarChar, 20).Value = this.Title;
com.Parameters.Add("#IdMember", SqlDbType.BigInt).Value = this.Id;
com.Parameters.Add("#whoID", SqlDbType.BigInt).Value = (object)whoID ?? DBNull.Value;
com.Connection.Open();
try
{
com.ExecuteNonQuery();
}
catch (Exception exe)
{
throw exe;
}
finally
{
com.Connection.Close();
}
First, what you are calling "Schemas" are actually "Databases" in SQL Server. The "dbo." in your object names is a "Schema" in SQL Server. The "USE.." command only works on Databases.
Secondly, you cannot reference or use Types from another database, it has to be defined in the same database(s) that it is used in. Types can be in other SQL Server schemas, but not in other Databases, which is what you are actually trying to do here.
OK, as you noted, your Type is defined in [myOtherDatbase] so why doesn't it work? Probably because the USE.. and SQL command strings do not work the way you might think. Whenever you pass a string like this to SQL Server and try to execute it:
com.CommandText = #"
// While connected to CurrentDatabase
USE MyOtherDatabase
DECLARE #tbl AS dbo.typeClubMembersVersion
BEGIN TRANSACTION
UPDATE dbo.tClubMembers
SET
Title = #Title
OUTPUT inserted.ID, deleted.[version] INTO #tbl (ID, [version])
WHERE IdMember = #IdMember
EXEC dbo.spCheckCMembersMods #tbl, #whoID
COMMIT
";
SQL Server will first compile the entire string and then try to execute it. This means that all of the commands are compiled first before any of them are executed. And that means that your DECLARE #tbl and UPDATE.. commands are compiled before the USE command is executed. So when they are compiled you are still in the previous database where the Type has not been defined. This is what leads to your syntax errors (which are coming from the compiler, not from their execution).
There are three possible solutions:
Define the Type in currentDatabase also (I am pretty sure that this works, but not 100%).
Reconnect with a connection string that specifies "Initial Catalog=MyOtherDatabase".
Re-execute everything after your USE command with Dynamic SQL.
Of these I would recommend #2.
Silly me, I just realized that there is another option:
First execute just the USE command by itself,
then, execute the rest of the SQL commands on the same connection.
Of course this will leave you in [MyOtherDatabase], so you may want to end this by executing another USE command back to your original database.
It's been such a very long time since I had to use SqlConnection.ChangeDatabase I fergot about it. Until now I've always been able to use "fully named objects" to make my databases interract with each other.
Since I'm currently stuck I'll use it but I hope somebody tells me a way that don't force me to let go the current database connection.
SqlCommand com = new SqlConnection(functions.ConnectionString).CreateCommand();
com.CommandText = #"
DECLARE #tbl AS dbo.typeClubMembersVersion
BEGIN TRANSACTION
UPDATE dbo.tClubMembers
SET
Title = #Title
OUTPUT inserted.ID, deleted.[version] INTO #tbl (ID, [version])
WHERE IdMember = #IdMember
EXEC dbo.spCheckCMembersMods #tbl, #whoID
COMMIT
";
com.Parameters.Add("#Title", SqlDbType.NVarChar, 20).Value = this.Title;
com.Parameters.Add("#IdMember", SqlDbType.BigInt).Value = this.Id;
com.Parameters.Add("#whoID", SqlDbType.BigInt).Value = (object)whoID ?? DBNull.Value;
com.Connection.Open();
try
{
com.Connection.ChangeDatabase("MyOtherDatabase");
com.ExecuteNonQuery();
}
catch (Exception exe)
{
throw exe;
}
finally
{
com.Connection.Close();
}
Why do I get an exception when trying to truncate a MySQL table (using MySQL Connector/Net)? I am trying to give the table name with a parameter.
This is the code I'm executing:
var connectionString = "Server="+_server+";Uid="+_user+";Pwd="+_password+";Database="+_database+";";
try
{
using (var conn = new MySqlConnection(connectionString))
{
conn.Open();
const string sql = "TRUNCATE TABLE #tablename"; // also tried with TRUNCATE #tablename
var cmd = new MySqlCommand(sql, conn);
cmd.Parameters.AddWithValue("#tablename", "test");
cmd.ExecuteNonQuery();
conn.Close();
}
}
catch (MySqlException ex)
{
Console.WriteLine(ex.ToString());
}
And this is the execption:
MySql.Data.MySqlClient.MySqlException (0x80004005): You have an error
in your SQ L syntax; check the manual that corresponds to your MySQL
server version for the right syntax to use near ''test'' at line 1
When I try a select query, for example, then I don't have any problems. This runs fine and returns correct data:
conn.Open();
const string sql = "SELECT body FROM test WHERE id=#pid";
var cmd = new MySqlCommand(sql, conn);
cmd.Parameters.AddWithValue("#pid", 1);
cmd.ExecuteScalar();
conn.Close();
Parameters are used for query values, not object names like tables.
So this will not work for sure.
You need to set the table name in the command string by using string concatenation. You can avoid sql injection attacks by manually checking for weird characters in the table name (spaces, dashes, semicolons, etc..)
I've been playing around with this for a while now, and i can't seem to get it to work either. I can't find any documentation online, so i'm starting to think you may not be able to truncate with a parameter like you've tried.
However, is there really a need to prevent SQL injection on this command? Does the user enter the name of the table they want to truncate, and if so, they're just going to truncate a table which...is essentially what the command does anyway?
I have lots of SQL queries written inline in C# using .net framework.
For Example
string sMysql = #"
SELECT
[Something]
from Person where person_uid=12"
I want it to convert it to stored procedures which will be written in transact SQL.
Something like this:
CREATE PROCEDURE [dbo].[aspnet_AnyDataInTables]
#uid int
AS
BEGIN
SELECT
[Something]
from Person where person_uid=#uid
END
I can do it manually but I have lots of inline queries to convert. Is there a way to do this hectic job programmatically?
For your specific example, throw out the stored procedure idea, and change your code snippet to the following:
// initialize UID value and SQL query with parameter placeholder
int uid = 12;
sql = "SELECT [Something] FROM [Person] WHERE [person_uid] = #UID";
// initialize connection and open
using (SqlConnection connection = new SqlConnection("<connection string>")
{
SqlCommand command = new SqlCommand(sql, connection)
// add UID parameter
command.Parameters.Add(new SqlParameter("UID", uid);
try
{
connection.Open();
// execute and read results
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
// process results
}
}
catch (Exception ex)
{
// handle exceptions
}
}
As stated in question comments, stored procedures aren't necessarily faster than inline parameterized queries. SQL Server will even cache execution plans for queries that aren't parameterized, but I prefer the explicit declaration of parameters.
Take a look at this article on execution plan caching and reuse for SQL Server 2008 if you want more information.