SQL error because of a single quote (')

Hi all,

I’m posting this because I encountered this error which I fixed but there might be other issues people have encountered with this. I recently updated my site to Omeka 3.1.2 and I started getting reports of Omeka errors. From the log:
2025-02-07T05:59:40+00:00 ERR (3): Zend_Db_Statement_Mysqli_Exception: Mysqli prepare error: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ‘Stairways to the Sky: Rice and Life in the Philippines’

It turns out this was the presence of a single quote ('). It turns out, this is only an issue if the field is HTML and also, when I replaced the ’ with ', when I saved it, it replaced ' with a single quote! Wha? Anyway, replacing it with a smart apostrophe (‘) works but you lose a bit of punctuation symmetry unless you want to go through by hand. Ugh.

Anyway, just wondering if anyone else has seen this problem with other characters busting the SQL code. Any advice would be great. Thanks!

Certainly that’s something that shouldn’t happen.

Can you post the whole error log entry for that error? It should have a bunch more lines showing a traceback, and that info will help determine where the problem is.

Sure John! There’s the full error:

2025-02-07T05:59:40+00:00 ERR (3): Zend_Db_Statement_Mysqli_Exception: Mysqli prepare error: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ‘Stairways to the Sky: Rice and Life in the Philippines’
Department: Asi…’ at line 2 in /mapping/application/libraries/Zend/Db/Statement/Mysqli.php:77
Stack trace:
#0 /mapping/application/libraries/Zend/Db/Statement.php(115): Zend_Db_Statement_Mysqli->_prepare(‘SELECT items…’)
#1 /mapping/application/libraries/Zend/Db/Adapter/Mysqli.php(388): Zend_Db_Statement->__construct(Object(Zend_Db_Adapter_Mysqli), ‘SELECT items…’)
#2 /mapping/application/libraries/Zend/Db/Adapter/Abstract.php(479): Zend_Db_Adapter_Mysqli->prepare(‘SELECT items…’)
#3 /mapping/application/libraries/Zend/Db/Adapter/Abstract.php(794): Zend_Db_Adapter_Abstract->query(‘SELECT items…’, Array)
#4 /mapping/application/libraries/Omeka/Db.php(80): Zend_Db_Adapter_Abstract->fetchCol(Object(Zend_Db_Select))
#5 /mapping/plugins/RelatedContent/RelatedContentPlugin.php(315): Omeka_Db->__call(‘fetchCol’, Array)
#6 /mapping/plugins/RelatedContent/RelatedContentPlugin.php(175): RelatedContentPlugin->getResultsByElement(41, Array, ‘2’)
#7 /mapping/application/libraries/Omeka/Plugin/Broker.php(154): RelatedContentPlugin->hookPublicItemsShow(Array)
#8 /mapping/application/libraries/globals.php(188): Omeka_Plugin_Broker->callHook(‘public_items_sh…’, Array)
#9 /mapping/themes/curatescape/items/show-template-default.php(91): fire_plugin_hook(‘public_items_sh…’, Array)
#10 /mapping/themes/curatescape/items/show.php(14): include(‘/home/philippi/…’)
#11 /mapping/application/libraries/Omeka/View.php(114): include(‘/home/philippi/…’)
#12 /mapping/application/libraries/Zend/View/Abstract.php(889): Omeka_View->_run(‘/home/philippi/…’)
#13 /mapping/application/libraries/Zend/Controller/Action/Helper/ViewRenderer.php(912): Zend_View_Abstract->render(NULL)
#14 /mapping/application/libraries/Zend/Controller/Action/Helper/ViewRenderer.php(933): Zend_Controller_Action_Helper_ViewRenderer->renderScript(‘items/show.php’, NULL)
#15 /mapping/application/libraries/Zend/Controller/Action/Helper/ViewRenderer.php(972): Zend_Controller_Action_Helper_ViewRenderer->render()
#16 /mapping/application/libraries/Zend/Controller/Action/HelperBroker.php(277): Zend_Controller_Action_Helper_ViewRenderer->postDispatch()
#17 /mapping/application/libraries/Zend/Controller/Action.php(527): Zend_Controller_Action_HelperBroker->notifyPostDispatch()
#18 /mapping/application/libraries/Zend/Controller/Dispatcher/Standard.php(308): Zend_Controller_Action->dispatch(‘showAction’)
#19 /mapping/application/libraries/Zend/Controller/Front.php(954): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http))
#20 /mapping/application/libraries/Zend/Application/Bootstrap/Bootstrap.php(106): Zend_Controller_Front->dispatch()
#21 /mapping/application/libraries/Zend/Application.php(384): Zend_Application_Bootstrap_Bootstrap->run()
#22 /mapping/application/libraries/Omeka/Application.php(73): Zend_Application->run()
#23 /mapping/index.php(23): Omeka_Application->run()
#24 {main}

This looks like it might be a bug in the plugin “RelatedContent,” You could try deactivating it to confirm.

That seems to be the case. I’ll try to fix it.

Ok to fix this you need to go into:
RelatedContentPlugin.php

And replace this function:
public function getResultsByElement($element_id, $element_array, $element_weight=1) {
$db = get_db();
$joinCondition = '_advanced_0.record_id = items.id AND _advanced_0.record_type = 'Item' AND _advanced_0.element_id = ';

	$select = $db
		->select()
		->from(array('items' => $db->Item), 'id')
		->joinLeft(array('_advanced_0' => $db->ElementText), $joinCondition . $element_id, array())
		->where("_advanced_0.text IN ('" . implode("','", $element_array) . "')")
		->where("public = 1")
		->order("rand()");
	$results = $db->fetchCol($select);
	
	// multiply by weight, according to importance of element
	return self::countAndMultiply($results, $element_weight);
}

With this one:

public function getResultsByElement($element_id, $element_array, $element_weight = 1) {
$db = get_db();
$joinCondition = '_advanced_0.record_id = items.id AND _advanced_0.record_type = 'Item' AND _advanced_0.element_id = ';

	$select = $db
		->select()
		->from(array('items' => $db->Item), 'id')
		->joinLeft(array('_advanced_0' => $db->ElementText), $joinCondition . $element_id, array())
		->where("public = 1")
		->order("rand()");

	if (!empty($element_array)) {
		$placeholders = implode(',', array_fill(0, count($element_array), '?'));
		$select->where("_advanced_0.text IN ($placeholders)", $element_array);
	} else {
		return []; // Return empty if there's nothing to search for
	}

	$results = $db->fetchCol($select);

	// Multiply by weight, according to importance of element
	return self::countAndMultiply($results, $element_weight);
}

This just escapes it properly and shields the db from sql injection.

Seems to work :slight_smile:

Can you please submit your issue and solution also on the GitHub pages of the plugin? This way we can keep track of the issues and fixes. Thanks

Ok. I meant to do that. I added this to issues.

Thanks. New version 2.1 implements your fix.