0

I'm working on a project, where I use metadata of mp3 songs in a string class. These are the data (i.e. artist, title, etc.) I want to copy into a mySQL database (from a list I made from the data) for which I have the next query:

SELECT * FROM database.tablename WHERE artist='"+song[i].artist+ "' AND title='" + song[i].title + "' AND genre='"+song[i].genre+"';

Code works perfectly (knowing that 12 lines copied successfully) until the for loop reaches a song what contains an apostrophe ('), it exits with syntax error at the given symbol. For example: song[i].title is I'm not her. I tried everything i could imagine:

  1. I tried replace ' to string.empty
  2. Tried to replace to ""
  3. Even tried to replace to "''" (2 apostrophes) so the query can recognize an apostrophe but nothing seems to help (always got the same syntax error, like when replace even wasn't there)

Here is a line, so you can see how I tried: song[i].artist = song[i].artist.Replace("'","");

So my question is: Is there any solution to get rid of the apostrophe, or any way to make the query work?

EDIT: Here is my original code (only the for loop) for better understanding.

for (int i = 0; i < Array.Length; i++)
            {
                Command = @"SELECT * FROM database.tablename WHERE artist='"+song[i].artist+ "' AND title='" + song[i].title + "' AND genre='"+song[i].genre+"';";
                MySqlCommand myCommand = new MySqlCommand(Command);
                using (MySqlConnection mConnection = new MySqlConnection(ConnectionString.ToString()))
                {
                    adapter = new MySqlDataAdapter(Command,mConnection);
                    mConnection.Open();
                    adapter.Fill(dataset);
                    int adat = dataset.Tables[0].Rows.Count;
                    if (adat <= 0)       //if current data does not exist
                    {
                        song[i].artist = song[i].artist.Replace("'","\'");
                        song[i].title = song[i].title.Replace("'", "\'");
                        song[i].genre = song[i].genre.Replace("'", "\'");
                        myCommand = new MySqlCommand("INSERT INTO database.tablename (artist,title,length,genre) VALUES ('"+song[i].artist+"','"+song[i].title+"','"+song[i].length+"','"+song[i].genre+"');",mConnection);
                        myCommand.ExecuteNonQuery();
                        dataset.Clear();
                    }
                    mConnection.Close();
                }
            }

And here is what I'm "using" (sorry, don't know how to make it look proper):

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.IO; using TagLib; using TagLib.Id3v2; using System.Configuration; using System.Data.SqlClient; using MySql; using MySql.Data; using MySql.Data.MySqlClient; using Programname.Properties;

profgyuri
  • 103
  • 10
  • 5
    Try looking up "parameterized queries" using whatever tech you are using (you don't show the actual code, so don't know what libraries you are using, or I'd link you to one). Among several other benefits, they'll let you put strings in your DB no matter the format. –  Aug 27 '18 at 22:44
  • Maybe this [link](https://stackoverflow.com/questions/9596652/how-to-escape-apostrophe-in-mysql) will help you, A “'” inside a string quoted with “'” may be written as “''”. – G. LC Aug 27 '18 at 22:46
  • A double apostrophe should work. Have you copied the sql query with values being generated and run it mysql to see if its working there? – Mehdi Ibrahim Aug 27 '18 at 22:56
  • Did you try this: Field.Replace("'", "''") – Frank Ball Aug 27 '18 at 22:58
  • For clearance: 1. I tested the query in MySQL workbench, it works with other databases without a problem. 2. Since i write 90% of the metadata of the mp3s I'm know it well its an apostrophe – profgyuri Aug 27 '18 at 23:09
  • Chad, My code is pretty comlicated here, that's why i didn't want to copy more of it, but first I'll look after what you said. – profgyuri Aug 27 '18 at 23:15
  • Seeing your full code, your first query will likely be better with a `Count(*)` since you're just checking to see if the data exists. – Parrish Husband Aug 27 '18 at 23:54
  • @GyörgyTurbék see my latest update addressing this. – Parrish Husband Aug 28 '18 at 00:12

2 Answers2

1

Just to add in a data provider agnostic solution into the mix, this is an approach you could take which decouples the code from MySql.

The ConnectionString can be set in the app/web.config:

<connectionStrings>
  <add name="Example" providerName="MySql.Data.MySqlClient" connectionString="Data Source=1.1.1.1"/>
</connectionStrings>

And the code would use the System.Data.Common classes:

var connectionSettings = ConfigurationManager.ConnectionStrings["Example"];
var dbFactory = DbProviderFactories.GetFactory(connectionSettings.ProviderName);

using (DbConnection connection = dbFactory.CreateConnection(connectionSettings.ConnectionString))
using (DbCommand countCommand = connection.CreateCommand())
{
    string sql = @"
SELECT COUNT(*) 
FROM database.tablename 
WHERE artist=@artist AND title=@title 
AND genre=@genre";

    countCommand.CommandText = sql;
    countCommand.Parameters.Add(dbFactory.GetParameter("@artist", null));
    countCommand.Parameters.Add(dbFactory.GetParameter("@title", null));
    countCommand.Parameters.Add(dbFactory.GetParameter("@genre", null));

    for (int i = 0; i < songs.Length; i++)
    {
        var song = songs[i];

        countCommand.Parameters["@artist"].Value = song.artist;
        countCommand.Parameters["@title"].Value = song.title;
        countCommand.Parameters["@genre"].Value = song.genre;

        int matches = (int)countCommand.ExecuteScalar();
        if (matches == 0)
            continue;

        using (DbCommand insertCommand = connection.CreateCommand())
        {
            string insertSql = @"
INSERT INTO database.tablename(artist, title, length, genre) 
VALUES(@artist, @title, @length, @genre";

            insertCommand.CommandText = insertSql;
            insertCommand.Parameters.Add(dbFactory.GetParameter("@artist", song.artist));
            insertCommand.Parameters.Add(dbFactory.GetParameter("@title", song.title));
            insertCommand.Parameters.Add(dbFactory.GetParameter("@length", song.length));
            insertCommand.Parameters.Add(dbFactory.GetParameter("@genre", song.genre));

            int result = insertCommand.ExecuteNonQuery();
        }
    }
}

And you can create custom extensions that offer slightly better options than what comes out of the DbProviderFactory:

public static class FactoryExtensions
{
    public static DbParameter GetParameter(this DbProviderFactory factory, string name, object value)
    {
        var param = factory.CreateParameter();
        param.Value = value ?? DBNull.Value;
        param.Name = name;
        return param;
    }

    public static DbConnection CreateConnection(this DbProviderFactory factory, string connectionString, bool open = true)
    {
        DbConnection connection = factory.CreateConnection();
        connection.ConnectionString = connectionString;

        if (open)
            connection.Open();

        return connection;
    }
}

Obviously your code is more complex than the example you provided, but this is a starting point.

Parrish Husband
  • 3,148
  • 18
  • 40
  • After a little trial and error the closest i got to the solution according to your code is where I get a message at the first line in the for loop: Additional information: Parameter '@artist' not found in the collection. – profgyuri Aug 28 '18 at 01:45
  • @GyörgyTurbék It's because I forgot to set the parameter name in my example. You need to add `param.Name = name`, see my updated answer. – Parrish Husband Aug 28 '18 at 01:58
  • Thanks for the quick reply! My only problem now that my code stuck somewhere on both of the answers (memory usage grown then nothing happened), but once i figured it out tomorrow (I need to sleep) I'll keep you updated. – profgyuri Aug 28 '18 at 02:24
  • Okay, I have absolutly no idea how and why, but with some alteration of your provided code I was finally able to make everything work. Huge Thanks, you might just saved my thesis. – profgyuri Aug 28 '18 at 12:44
  • Using parameters in your SQL statements is why this worked for you. The apostrophe escaping doesn't need to happen if you use these. Additionally you eliminate SQL injection attacks, so you should always use parameters. My answer was mostly showing the `System.Data.Common` tricks. – Parrish Husband Aug 28 '18 at 13:03
0

As @Chad mentioned, parameters are the way to go.

With the standard System.Data classes, you'd probably be looking at something like this:

// set up command using @parameters
using (SqlCommand cmd = new SqlCommand("SELECT * FROM database.tablename WHERE artist=@artist AND title=@title AND genre=@genre", connection))
{
    // add parameter values to command
    cmd.Parameters.Add(new SqlParameter("@artist", song[i].artist));
    cmd.Parameters.Add(new SqlParameter("@title", song[i].title));
    cmd.Parameters.Add(new SqlParameter("@genre", song[i].genre));

    SqlDataReader reader = cmd.ExecuteReader();

    // do useful stuff here
}

I found (and then butchered) a MySQL Connector/NET tutorial from here that might be a bit more MySQL-specific:

string connStr = "*** your connection string here ***";
MySqlConnection conn = new MySqlConnection(connStr);
conn.Open();

string sql = "SELECT * FROM database.tablename WHERE artist=@artist AND title=@title AND genre=@genre";
MySqlCommand cmd = new MySqlCommand(sql, conn);

cmd.Parameters.AddWithValue("@artist", song[i].artist);
cmd.Parameters.AddWithValue("@title", song[i].title);
cmd.Parameters.AddWithValue("@genre", song[i].genre);

MySqlDataReader rdr = cmd.ExecuteReader();

// do useful stuff here

rdr.Close();
conn.Close();
Nigel Whatling
  • 2,371
  • 1
  • 16
  • 22
  • Also don't hard-code data provider specific classes if you can avoid it. – Parrish Husband Aug 27 '18 at 23:20
  • Believe it or not, but still the same error: Additional information: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'm Not Her (Hook N Sling Remix)','00:03:16.0670000','House')' at line 1 - Right at the apostrophe again. – profgyuri Aug 27 '18 at 23:42
  • 1
    That's odd. Just to confirm (sorry), you used parameters for the INSERT query as well as the SELECT? – Nigel Whatling Aug 28 '18 at 00:09
  • @NigelWhatling yeah that's what I suspect also – Parrish Husband Aug 28 '18 at 00:21
  • On 1 hand you were right, I forgot to change the INSERT query (sorry from late night me), on the other hand my code stuck somewhere and call stack only gives me the " Application.Run(new Form1());" row, so more digging needed tomorrow – profgyuri Aug 28 '18 at 01:54