String Escape in Like Query not working (CodeIgniter) - php

I have been testing my query with random strings to see if there will be results that will be displayed.
One string that I tested is cal, so the query should be simple, right?
SELECT c.`ID` as id, CONCAT(p.`Name`, ' > ', c.`Name`) as name, 'cal' as q
FROM `cq_provice` p JOIN `cq_city` c ON c.`provinceID` = p.`ID`
WHERE (CONCAT(p.`Name`, ' > ', c.`Name`) LIKE '%cal%') ORDER BY p.`Name`, c.`Name`
LIMIT 0, 50
However, with my 2 prepared sql queries: one using the codeigniter way and one using the usual:
#1
$this->db->select('c.ID as id');
$this->db->select('CONCAT(p.Name, \' > \', c.Name) as name');
$this->db->select($term.' as q');
$this->db->from('cq_provice p');
$this->db->join('cq_city c', 'c.provinceID = p.ID');
$this->db->like('CONCAT(p.Name, \' > \', c.Name)', $this->db->escape($this->input->post('search')), 'both');
$this->db->order_by('p.Name', 'ASC');
$this->db->order_by('c.Name', 'ASC');
$this->db->limit($result_count, $offset);
$query = $this->db->get();
#2
$query = "SELECT c.`ID` as id, CONCAT(p.`Name`, ' > ', c.`Name`) as name, ".$term." as q
FROM `cq_provice` p JOIN `cq_city` c ON c.`provinceID` = p.`ID`
WHERE (CONCAT(p.`Name`, ' > ', c.`Name`) LIKE
'%".$this->db->escape($this->input->post('search'))."%')
ORDER BY p.`Name`, c.`Name` LIMIT ".$offset.", ".$result_count;
And both of them is giving me the same query statement:
SELECT c.`ID` as id, CONCAT(p.`Name`, ' > ', c.`Name`) as name, 'cal' as q
FROM `cq_provice` p JOIN `cq_city` c ON c.`provinceID` = p.`ID`
WHERE (CONCAT(p.`Name`, ' > ', c.`Name`) LIKE '%'cal'%')
ORDER BY p.`Name`, c.`Name` LIMIT 0, 50
My problem lies in the WHERE statement WHERE (CONCAT(p.Name, ' > ', c.Name) LIKE '%'cal'%' wherein the LIKE statement should be '%cal%' so I tried to use trim and regex but they gave me the same result:
SELECT c.`ID` as id, CONCAT(p.`Name`, ' > ', c.`Name`) as name, 'cal' as q
FROM `cq_provice` p JOIN `cq_city` c ON c.`provinceID` = p.`ID`
WHERE (CONCAT(p.`Name`, ' > ', c.`Name`) LIKE 'Êl%')
ORDER BY p.`Name`, c.`Name` LIMIT 0, 50
How do I fix this? How can I prevent my query to translate my string as special characters?
Any help is highly appreciated.

$this->db->select(array('c.ID as id','CONCAT(p.Name, " > ", c.Name) as name',$term.' as q'));
$this->db->from('cq_provice p');
$this->db->join('cq_city c', 'c.provinceID = p.ID');
$this->db->like('CONCAT(p.Name, " > ", c.Name)', $this->db->escape($this->input->post('search')));
$this->db->order_by('p.Name ASC, c.Name ASC');
$this->db->limit($result_count, $offset);
$query = $this->db->get();
or with MATCH, AGAINST
$field = $this->db->escape($this->input->post('search'));
$this->db->select(array('c.ID as id','CONCAT(p.Name, " > ", c.Name) as name',$term.' as q'));
$this->db->from('cq_provice p');
$this->db->join('cq_city c', 'c.provinceID = p.ID');
$this->db->where('MATCH (p.Name,c.Name) AGAINST ('.$field.')', NULL, FALSE);
$this->db->order_by('p.Name ASC, c.Name ASC');
$this->db->limit($result_count, $offset);
$query = $this->db->get();

Related

Paramaterize Case Statement Using ODBC

I am selecting data from an MS SQL DB using a similar query to this:
$result = odbc_prepare(
$connection,
"SELECT
t1.id, thumbnail, description, brand, vendor,
case
when ".implode(' AND ', $exactSearchTermDesc)." then 1
else 0
end as exactdescriptionmatch,
case
when ".implode(' AND ', $exactSearchTermMarkDesc)."". $exactThesaurusMarkDesc ."
then 1 else 0
end as exactcontentmatch,
case
when ".implode(' AND ', $searchTermDesc)."" . $thesaurusDesc ." then 1
else 0
end as descriptionmatch,
case
when ".implode(' AND ', $searchTermMarkDesc)."". $thesaurusMarkDesc ." then 1
else 0
end as contentmatch
FROM Martin.dbo.item_search"
);
odbc_execute($result);
How do I parameterize the variables in the case statements?
I tried, for example, just trying to paramaterize the first variable with this to no avail...
$exactSearchTermDesc = implode(' AND ', $exactSearchTermDesc);
$result = odbc_prepare(
$connection,
"SELECT
t1.id, thumbnail, description, brand, vendor,
case when ? then 1 else 0 end as exactdescriptionmatch,
case when ".implode(' AND ', $exactSearchTermMarkDesc)."". $exactThesaurusMarkDesc ." then 1 else 0 end as exactcontentmatch,
case when ".implode(' AND ', $searchTermDesc)."" . $thesaurusDesc ." then 1 else 0 end as descriptionmatch,
case when ".implode(' AND ', $searchTermMarkDesc)."". $thesaurusMarkDesc ." then 1 else 0 end as contentmatch
FROM Martin.dbo.item_search"
);
odbc_execute($result array($exactSearchTermDesc));
The variables are as below. $terms is the user input.
$searchTerms = explode(' ', $terms);
$exactSearchTermDesc = array();
$exactSearchTermMarkDesc = array();
$searchTermDesc = array();
$searchTermMarkDesc = array();
foreach ($searchTerms as $term) {
$term = trim($term);
if (!empty($term)) {
$exactSearchTermDesc[] = "description LIKE '$term %'";
$exactSearchTermMarkDesc[] = "contains(marketingDescription, '$term')";
$searchTermDesc[] = "description LIKE '%$term%'";
$searchTermMarkDesc[] = "marketingDescription LIKE '%$term%'";
$searchTermVendor[] = "vendor LIKE '%$term%'";
}
}
$exactThesaurusDesc = " Or " . implode(' AND ', $exactThesaurusDesc);
$exactThesaurusMarkDesc = " Or " . implode(' AND ', $exactThesaurusMarkDesc);
$thesaurusDesc = " Or " . implode(' AND ', $thesaurusDesc);
$thesaurusMarkDesc = " Or " . implode(' AND ', $thesaurusMarkDesc);
$thesaurusVendor = " Or " . implode(' AND ', $thesaurusVendor);
Consider re-factoring your SQL process. Rather than piecing together dynamic SQL components at application layer (i.e., PHP) that can impact readability and maintainability, consider multiple self-joins to a temp table of terms with below definition:
CREATE TABLE tmp_terms (
id IDENTITY(1,1) NOT NULL,
term VARCHAR(255),
thesauraus_indicator BIT
)
Specifically, run an aggregate query across joined matches where MIN returns 0 or 1. With this approach, the same SQL query is maintained but the underlying terms populated by PHP will be the dynamic component.
Below runs the non-thesaurus part of previous CASE logic. Also, since CONTAINS cannot use another column but only a literal value, JOIN expressions use LIKE with the leading and trailing wildcard.
SELECT
i.id, i.thumbnail, i.description, i.brand, i.vendor,
MIN(case
when exact_desc.term IS NOT NULL
then 1
else 0
end) AS exact_description_match,
MIN(case
when market_match.term IS NOT NULL
then 1
else 0
end) AS market_match,
MIN(case
when desc_match.term IS NOT NULL
then 1
else 0
end) AS desc_match,
MIN(case
when vendor_match.term IS NOT NULL
then 1
else 0
end) AS vendor_match
FROM Martin.dbo.item_search i
LEFT JOIN tmp_terms exact_desc
ON i.description LIKE CONCAT(exact_desc.term, ' %')
AND exact_desc.thesaurus_indicator = 0
LEFT JOIN tmp_terms market_match
ON i.marketingDescription LIKE CONCAT('%', market_match.term, '%')
AND market_match.thesaurus_indicator = 0
LEFT JOIN tmp_terms desc_match
ON i.description LIKE CONCAT('%', desc_match.term, '%')
AND desc_match.thesaurus_indicator = 0
LEFT JOIN tmp_terms vendor_match
ON i.vendor LIKE CONCAT('%', vendor_match.term, '%')
AND vendor_match.thesaurus_indicator = 0
GROUP BY i.id, i.thumbnail, i.description, i.brand, i.vendor
To integrate the thesaurus match, use UNION in CTE or subquery before aggregation in outer query.
WITH sub AS
(SELECT
i.id, i.thumbnail, i.description, i.brand, i.vendor,
exact_desc.term AS exact_desc_term, market_match.term AS market_match_term,
desc_match.term AS desc_match_term, vendor_match.term AS vendor_match_term
FROM Martin.dbo.item_search i
LEFT JOIN tmp_terms exact_desc
ON i.description LIKE CONCAT(exact_desc.term, ' %')
AND exact_desc.thesaurus_indicator = 0
LEFT JOIN tmp_terms market_match
ON i.marketingDescription LIKE CONCAT('%', market_match.term, '%')
AND market_match.thesaurus_indicator = 0
LEFT JOIN tmp_terms desc_match
ON i.description LIKE CONCAT('%', desc_match.term, '%')
AND desc_match.thesaurus_indicator = 0
LEFT JOIN tmp_terms vendor_match
ON i.vendor LIKE CONCAT('%', vendor_match.term, '%')
AND vendor_match.thesaurus_indicator = 0
UNION
SELECT
i.id, i.thumbnail, i.description, i.brand, i.vendor,
th_exact_desc.term, th_market_match.term,
th_desc_match.term, th_vendor_match.term
LEFT JOIN tmp_terms th_exact_desc
ON i.description LIKE CONCAT(th_exact_desc.term, ' %')
AND th_exact_match.thesaurus_indicator = 1
LEFT JOIN tmp_terms th_market_match
ON i.marketingDescription LIKE CONCAT('%', th_market_match.term, '%')
AND th_market_match.thesaurus_indicator = 1
LEFT JOIN tmp_terms th_desc_match
ON i.description LIKE CONCAT('%', th_desc_match.term, '%')
AND th_desc_match.thesaurus_indicator = 1
LEFT JOIN tmp_terms th_vendor_match
ON i.vendor LIKE CONCAT('%', th_vendor_match.term, '%')
AND th_vendor_match.thesaurus_indicator = 1
)
SELECT sub.id, sub.thumbnail, sub.description, sub.brand, sub.vendor,
MIN(case
when sub.exact_desc_term IS NOT NULL
then 1
else 0
end) AS exact_description_match,
MIN(case
when sub.market_match_term IS NOT NULL
then 1
else 0
end) AS market_match,
MIN(case
when sub.desc_match_term IS NOT NULL
then 1
else 0
end) AS desc_match,
MIN(case
when sub.vendor_match_term IS NOT NULL
then 1
else 0
end) AS vendor_match
FROM sub
GROUP BY sub.id, sub.thumbnail, sub.description, sub.brand, sub.vendor
Note: Above queries may need adjustment to final end use needs and testing. Performance may vary depending on number of search terms. But ultimately, the concept is to interact with data in sets and not long concatenated CASE WHEN... logic that grows with search terms.
Finally, in PHP simply clean out and populate the temp table with new values using parameterization and run any of the above final query:
odbc_exec($connection, "DELETE FROM tmp_terms");
$stmt = odbc_prepare($connection,
"INSERT INTO tmp_terms (term, thesaurus_indicator)
VALUES (?, ?)");
// NON-THESAURUS TERMS
foreach($search_terms as $t) {
odbc_execute($stmt, array($t, 0));
}
// THESAURUS TERMS
foreach($th_search_terms as $t) {
odbc_execute($stmt, array($t, 1));
}
// RUN FINAL SELECT QUERY
$result = odbc_exec($connection, "my_final_above_query");
while(odbc_fetch_row($result)){
for($i=1; $i<=odbc_num_fields($result); $i++){
// ... odbc_result($result,$i);
}
}

How to Fix "A Database Error Occurred" in Codeigniter for IF condition in MySQL?

I am new to Codeigniter, Having error in mysql while using IF Statement,
Here is my Code
$this->db->select('A.city_id, A.`name` AS city_name, B.`name` AS state_name, C.`name` AS country_name, IF(`A.visible`,"Yes","No") AS active');
$this->db->from('abs_city AS A');
$this->db->join('abs_state AS B', 'A.state_id = B.state_id AND B.visible = 1 AND A.country_id = B.country_id ', 'inner');
$this->db->join('abs_countries AS C', 'A.country_id = C.country_id AND B.visible = 1 ', 'inner' );
#$this->db->where('A.visible = 1');
return $this->db->get()->result();
Error in Browser
While removing tag(`) following query runs in phpmyadmin
SELECT
`A`.`city_id`,
`A`.`name` AS city_name,
`B`.`name` AS state_name,
`C`.`name` AS country_name,
IF (A.visible, "Yes", "No") AS active
FROM
(`vbs_abs_city` AS A)
INNER JOIN `vbs_abs_state` AS B ON `A`.`state_id` = `B`.`state_id`
AND B.visible = 1
AND A.country_id = B.country_id
INNER JOIN `vbs_abs_countries` AS C ON `A`.`country_id` = `C`.`country_id`
AND B.visible = 1
Please help me solve this, Thanks on advance.
You can call the select method with FALSE as the last parameter, like this
$this->db->select('A.city_id, A.`name` AS city_name, B.`name` AS state_name, C.`name` AS country_name, IF(`A.visible`,"Yes","No") AS active',false);
That will prevent CI to add the ` in your query
try update your query to
$this->db->select('A.city_id, A.`name` AS city_name, B.`name` AS state_name, C.`name` AS country_name, IF(`A.visible`,"Yes","No") AS active',false);
$this->db->from('abs_city AS A');
$this->db->join('abs_state AS B', 'A.state_id = B.state_id AND B.visible = 1 AND A.country_id = B.country_id ', 'inner');
$this->db->join('abs_countries AS C', 'A.country_id = C.country_id AND B.visible = 1 ', 'inner' );
#$this->db->where('A.visible = 1');
return $this->db->get()->result();
Instead of adding the false parameter to your SELECT statement (which is inherently insecure), try changing the statement to:
$this->db->select('A.city_id, A.`name` AS city_name, B.`name` AS state_name, C.`name` AS country_name, IF(A.visible,"Yes","No") AS active');
(just remove the backticks around A.visible)
This is a more secure version than adding the FALSE parameter. The latter will prevent the automatic escaping that query builder adds to the built statement, rendering the whole select statement less secure, which is not advisable

Mysql query convert into codeigniter active record

I have this query to convert into the Active record
SELECT (SELECT image FROM bc_item_image WHERE item_id = i.id LIMIT 1) as item_image, i.description, i.condition,c.cat_name as category_name,i.id as ID FROM bc_item i,bc_category c WHERE i.cat_id = c.id and i.user_id !='$user_id' and i.status = '1' and i.is_bartered = '0' and i.is_cancel = '0' and FIND_IN_SET('$subcat_id',i.interested_cat) order by i.display_count desc
use
$this->db->query('here your SQL query');
$this->db->select(" i.description, i.condition,c.cat_name as category_name,i.id,(SELECT image FROM bc_item_image WHERE item_id = i.id LIMIT 1) as item_image");
$this->db->from('bc_item as i');
$this->db->join('bc_category as c', 'i.cat_id = c.id');
$this->db->where('i.status', 1);
$this->db->where('i.is_bartered', 0);
$this->db->where('i.user_id','!=', $user_id);
$this->db->where('i.is_cancel', 0);
$this->db->where("FIND_IN_SET('".$subcat_id."','i.interested_cat')");
$query = $this->db->get();

SQL Join SUM not properly working

I am having a problem producing a result with my query. I wanted to produce SUM of the result of rows from another table which is related to the primary table.
What I failed to output is the sum of payments, and the sum of payables.
PROBLEM #1 - I cannot produce a correct output of the Payments. The
query seemed to produce an formula of numrows * sum_of_payments
PROBLEM #2 - I cannot produce an output of the total payables.
I am using Codeigniter 3 and this is my model.
function fetch_billing_records($case_id, $patient_id, $status) {
$this->db->join('cases', 'cases.id = billing.case_id', 'left');
$this->db->join('patients', 'patients.id = cases.patient_id', 'left');
$this->db->join('users', 'users.username = billing.user', 'left');
$this->db->join('billing_payments', 'billing_payments.billing_id = billing.id', 'left');
$this->db->join('billing_items', 'billing_items.billing_id = billing.id', 'left');
$this->db->join('services', 'services.title = billing_items.service', 'left');
$this->db->select('
billing.id,
billing.remarks,
billing.status,
billing.created_at,
billing.updated_at,
users.name as user,
users.username,
cases.id as case_id,
cases.title as case_title,
patients.id as patient_id,
CONCAT(patients.lastname, ", ", patients.fullname) as patient_name,
SUM(billing_payments.amount) as payments,
SUM((services.amount - billing_items.discount)*billing_items.qty) as payables
');
if(is_int($case_id)) {
$this->db->where('billing.case_id', $case_id);
}
if(is_int($patient_id)) {
$this->db->where('patients.id', $patient_id);
}
if(is_int($status)) {
$this->db->where('billing.status', $status);
}
$this->db->group_by('billing.id');
$this->db->where('billing.is_deleted', 0);
$query = $this->db->get("billing");
log_message('error', $this->db->last_query());
if ($query->num_rows() > 0) {
return $query->result_array();
}
return false;
}
I saved the Last Query with the Last Query and Log Helpers, and it seemed that the query for my instance is
SELECT `billing`.`id`, `billing`.`remarks`, `billing`.`status`, `billing`.`created_at`, `billing`.`updated_at`, `users`.`name` as `user`, `users`.`username`, `cases`.`id` as `case_id`, `cases`.`title` as `case_title`, `patients`.`id` as `patient_id`, CONCAT(patients.lastname, ", ", patients.fullname) as patient_name, SUM(billing_payments.amount) as payments, SUM((services.amount - billing_items.discount)*billing_items.qty) as payables
FROM `billing`
LEFT JOIN `cases` ON `cases`.`id` = `billing`.`case_id`
LEFT JOIN `patients` ON `patients`.`id` = `cases`.`patient_id`
LEFT JOIN `users` ON `users`.`username` = `billing`.`user`
LEFT JOIN `billing_payments` ON `billing_payments`.`billing_id` = `billing`.`id`
LEFT JOIN `billing_items` ON `billing_items`.`billing_id` = `billing`.`id`
LEFT JOIN `services` ON `services`.`title` = `billing_items`.`service`
WHERE `billing`.`is_deleted` =0
To guide you more with my present problem, this is the schema of the related tables:
and this is the schema of the entire database.
Use group by statement. because mysql aggregate functions work well with group by. if you want billing sum against patient then use following query
SELECT
`billing`.`id`,
`billing`.`remarks`,
`billing`.`status`,
`billing`.`created_at`,
`billing`.`updated_at`, `
users`.`name` as `user`,
`users`.`username`,
`cases`.`id` as `case_id`,
`cases`.`title` as `case_title`,
`patients`.`id` as `patient_id`,
CONCAT(patients.lastname, ", ", patients.fullname) as patient_name,
SUM(billing_payments.amount) as payments,
SUM((services.amount - billing_items.discount)*billing_items.qty) as payables
FROM `billing`
LEFT JOIN `cases` ON `cases`.`id` = `billing`.`case_id`
LEFT JOIN `patients` ON `patients`.`id` = `cases`.`patient_id`
LEFT JOIN `users` ON `users`.`username` = `billing`.`user`
LEFT JOIN `billing_payments` ON `billing_payments`.`billing_id` = `billing`.`id`
LEFT JOIN `billing_items` ON `billing_items`.`billing_id` = `billing`.`id`
LEFT JOIN `services` ON `services`.`title` = `billing_items`.`service`
WHERE `billing`.`is_deleted` =0
GROUP BY patients.id

CodeIgniter 3 Query ESCAPE '!' Ignoring the WHERE statement?

I am building a site search with select boxes to get certain categories & regions.
If selected am I including it in the query, but for some reason, using LIKE is it ignoring my WHERE section of the query!?!? Why is that or what am I doing wrong?
When I echo the query built by Codeigniter I get the fololowing:
(Note the ESCAPE '!')
Query echo:
SELECT SQL_CALC_FOUND_ROWS null as rows, ads.id AS id, location, provLabel, text,
adcat.id AS catid, ads.subcatid AS subcatid, ads.province R_rand, r_option, addate,
adcat.name AS catname, adsubcat.name AS subname, f_value, adtitle, ads.area, regionLabel,
adlink
FROM `ads`
JOIN `search_town` ON `search_town`.`townId`=`ads`.`townId`
JOIN `search_region` ON `search_region`.`regionId`=`ads`.`area`
JOIN `search_prov` ON `search_prov`.`provId`=`ads`.`province`
JOIN `adcat` ON `adcat`.`id`=`ads`.`catid`
JOIN `adsubcat` ON `adsubcat`.`id`=`ads`.`subcatid`
LEFT JOIN `adfields` ON `adfields`.`ad_id`=`ads`.`id`
WHERE `ads`.`catid` != 8 AND `ads`.`adactive` = 1 AND `scam` =0 AND `ads`.`province` = '1'
AND `ads`.`catid` = '3' AND `text` LIKE '%Nissan%' ESCAPE '!'
OR `f_value` LIKE '%Nissan%' ESCAPE '!'
GROUP BY `ads`.`id`
ORDER BY `addate` DESC
LIMIT 10
Here is the actual query in the controller:
public function get_search($fsearch, $fcategory, $fprovince, $farea, $limit, $start)
{
if($fcategory >=1){
$incl_cat=" AND ads.catid='$fcategory'";
}else{
$incl_cat='';
}
if($fprovince>=1){
$incl_prov=" AND ads.province='$fprovince'";
}else{
$incl_prov='';
}
if($farea >= 1){
$incl_area=" AND ads.area='$farea'";
}else{
$incl_area='';
}
$this->db->select('SQL_CALC_FOUND_ROWS null as rows, ads.id AS id, location, provLabel, text, adcat.id AS catid, ads.subcatid AS subcatid,ads.province
R_rand, r_option, addate, adcat.name AS catname, adsubcat.name AS subname, f_value, adtitle, ads.area, regionLabel,
adlink', FALSE);
$this->db->from('ads');
$this->db->join('search_town', 'search_town.townId=ads.townId');
$this->db->join('search_region', 'search_region.regionId=ads.area');
$this->db->join('search_prov', 'search_prov.provId=ads.province');
$this->db->join('adcat', 'adcat.id=ads.catid');
$this->db->join('adsubcat', 'adsubcat.id=ads.subcatid');
$this->db->join('adfields', 'adfields.ad_id=ads.id', 'left');
$where = "ads.catid!=8 AND ads.adactive=1 AND scam=0 $incl_prov $incl_cat $incl_area";
$this->db->where($where);
$this->db->like('text', $fsearch);
$this->db->or_like('f_value', $fsearch);
$this->db->group_by("ads.id");
$this->db->order_by('addate', 'DESC');
$this->db->limit($limit, $start);
$query = $this->db->get();
$return = $query->result_array();
echo $this->db->last_query();
$total_results=$this->db->query('SELECT FOUND_ROWS() count;')->row()->count;
$this->session->set_userdata('tot_search', $total_results);
return $return;
}
Your WHERE condition is
a AND b AND c AND d AND ... AND x OR y
If y is true the whole WHERE condition is true.
Perhaps you mean:
a AND b AND c AND d AND ... AND (x OR y)
The ESCAPE character defaults to \, but it looks like codeigniter has changed this to ! for you (presumably because \ is already an escape in PHP, so sometimes you need multiples and this can be confusing).
Currently this is irrelevant to your query. This would only be used if you need to match a % or an _ with !% or !_ (default \% or \_).

Categories