0

I'm using PHP/MySQL to store data collected from a web page. I'm getting

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 'INSERT INTO narrative_photos VALUES (`filename`, `narrative_id`) VALUES ('ash_02

When I take the statement produced by PHP and paste it into the MySQL console, the statement works fine.

Here's the PHP code:

foreach ($files['pictures']['final_name'] as $key => $final_name) {
    $sql .= "INSERT INTO narrative_photos ";
    $sql .= "(`filename`, `narrative_id`) ";
    $sql .= "VALUES (";
    $sql .= "'" . db_escape($db, $final_name) . "', ";
    $sql .= "'LAST_INSERT_ID()'); ";
}

It produces something that looks like this:

INSERT INTO narrative_photos VALUES (`filename`, `narrative_id`) VALUES ('ash_020819-140257.png', 3);

If I paste that into MySQL it works. But if I comment out the PHP code and substitute:

 $sql .= "INSERT INTO narrative_photos VALUES (`filename`, `narrative_id`) VALUES ('ash_020819-140257.png', 3);";

it continues to throw the MySQL error.

I've been playing with this for a couple of hours and I can't figure out where my mistake is. I would appreciate a second set of eyes. Thanks!

EDIT: Here's the entire function for context.

function insert_narrative($narrative, $files) {
    global $db;

    $sql = "INSERT INTO narratives ";
    $sql .= "(date, positive_thing, what_you_did, goals, plan, entered_by, library_id) ";
    $sql .= "VALUES (";
    $sql .= "'" . db_escape($db, $narrative['sqldate']) . "', ";
    $sql .= "'" . db_escape($db, $narrative['positive_thing']) . "', ";
    $sql .= "'" . db_escape($db, $narrative['what_you_did']) . "', ";
    $sql .= "'" . db_escape($db, $narrative['goals']) . "', ";
    $sql .= "'" . db_escape($db, $narrative['plan']) . "', ";
    $sql .= "'" . db_escape($db, $narrative['entered_by']) . "', ";
    $sql .= "'" . db_escape($db, $_SESSION['library_id']) . "'";
    $sql .= "); ";

    if (!empty($files['pictures']['final_name'])) {
        foreach ($files['pictures']['final_name'] as $key => $final_name) {
            $sql .= "INSERT INTO narrative_photos ";
            $sql .= "(`filename`, `narrative_id`) ";
            $sql .= "VALUES (";
            $sql .= "'" . db_escape($db, $final_name) . "', ";
            $sql .= "LAST_INSERT_ID()); ";

        }
    }
    $result = mysqli_query($db, $sql);
    if ($result) {
        return true;
    } else {
        echo mysqli_error($db);
        db_disconnect($db);
        exit;
    }
}

EDIT #2: I just realized that independent of the syntax error my approach isn't going to work because that LAST_INSERT_ID is probably going to pick up the ids for each of those inserts instead of just using the id from the main table. I've modified the function but I'm still getting a syntax error at SET @narrative_id. Here's the code.

$sql = "INSERT INTO narratives ";
$sql .= "(date, positive_thing, what_you_did, goals, plan, entered_by, library_id) ";
$sql .= "VALUES (";
$sql .= "'" . db_escape($db, $narrative['sqldate']) . "', ";
$sql .= "'" . db_escape($db, $narrative['positive_thing']) . "', ";
$sql .= "'" . db_escape($db, $narrative['what_you_did']) . "', ";
$sql .= "'" . db_escape($db, $narrative['goals']) . "', ";
$sql .= "'" . db_escape($db, $narrative['plan']) . "', ";
$sql .= "'" . db_escape($db, $narrative['entered_by']) . "', ";
$sql .= "'" . db_escape($db, $_SESSION['library_id']) . "'";
$sql .= "); ";
$sql .= "SET @narrative_id = LAST_INSERT_ID()";

if (!empty($files['pictures']['final_name'])) {
    foreach ($files['pictures']['final_name'] as $key => $final_name) {
        $sql .= "INSERT INTO narrative_photos ";
        $sql .= "(`filename`, `narrative_id`) ";
        $sql .= "VALUES (";
        $sql .= "'" . db_escape($db, $final_name) . "', ";
        $sql .= "@narrative_id); ";

    }
}
12
  • 7
    I think you don't want these quotes 'LAST_INSERT_ID()' in fact if you can remove that completely. As the key is automatically done on inserts. Basically your passing a string to the Pkey of the table. Which is why you get an error. Commented Feb 8, 2019 at 22:14
  • 1
    Removing the quotes doesn't get rid of the error. The reason for the LAST_INSERT_ID() is that I have a SQL statement preceding this that writes to a related table. Commented Feb 8, 2019 at 22:28
  • 1
    Well LAST_INSERT_ID is a MySql function, it was like putting quotes around DATE(datetime) or WHERE. Commented Feb 8, 2019 at 22:33
  • 2
    How are you trying to run the query - as it's potentially a multi-query? Commented Feb 8, 2019 at 22:36
  • 1
    @rancidamoeba Can you just echo $sql; just before the closing for loop and see what you are getting as sql. Commented Feb 8, 2019 at 22:37

2 Answers 2

2

If you're trying to make one query to insert all the values, you need to not include the INSERT part of the query each time, just adding a new set of values instead. You could use something like this. Note that LAST_INSERT_ID() should not be enclosed in quotes, as that will insert the literal string "LAST_INSERT_ID()" instead of the value.

$sql  = "INSERT INTO narrative_photos (`filename`, `narrative_id`) VALUES ";
$values = array();
foreach ($files['pictures']['final_name'] as $final_name) {
    $values[] = "('" . db_escape($db, $final_name) . "', LAST_INSERT_ID())";
}
$sql .= implode(', ', $values);

Notes

I'm assuming that you actually want all of these filenames to end up with the same value in narrative_id, which is going to link back to another table.

Although from the look of it these values have been filtered already (I presume they are actual system filenames), the code is still potentially vulnerable to SQL injection. This question and this question offer some good advice as to how you can use prepared statements with arrays of parameters.

Sign up to request clarification or add additional context in comments.

5 Comments

The final_name bit seems prone to injection
$key is not used. And this is a good opportunity to promote prepared statements.
Wait a sec. How is this expected to work? (The OP's coding attempt, I mean.) Is this expected to keep catching the new LAST_INSERT_ID from the previous inserted row? I am concerned that this is going to store skewed/unintended rows.
@mickmackusa my assumption was that all the files are supposed to be associated with an id from an insert in another table (given the name of the field being narrative_id). That's why I went for this approach as they will all get the same value.
I'm not blaming you on this. I just don't know if this is going to work as planned/intended. Think about what LAST_INSERT_ID() does. Am I crazy here? I haven't run any local tests to confirm my suspicions. I know it is a common habit to declare $values = array(); but if there are no rows, there shouldn't be a query executed.
0

I don't understand why you concatenating each line to a variable instead of just doing an entire statement. Also, in your examples you have used VALUES twice which is incorrect. I can't understand why you are creating the SQL statement inside a loop but you never execute it, which you need to. I don't know what API you are using but here's an example.

if (isset($files['pictures']['final_name'])) {
  foreach ($files['pictures']['final_name'] as $key => $final_name) {
    $sql = "INSERT INTO narrative_photos (`filename`, `narrative_id`) 
            VALUES ('".db_escape($db, $final_name)."', LAST_INSERT_ID())";
    if (!$mysqli->query($sql)) {
      echo "SQL failed: (".$mysqli->errno.") ".$mysqli->error;
    }
  }
} else {
  echo "final name does not exist";
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.