Weird backticks behaviour in Active Record in CodeIgniter 2.0.3 - codeigniter

Previously my all queries were running fine in CI version 2.0 but when I upgraded to 2.0.3 some of my SELECT queries were broken.
CI is adding backticks (``) automatically, but in older version its running as it is.
CI user manual have instructed to add second parameter in
db->select
as
FALSE
but still it's not working.
Code is as following:
class Company_model extends MY_Model
{
----------------
$this->db->select(' count('.$fieldname. ') as num_stations');
$this->db->select(" CONCAT_WS(',', clb_company.address1, clb_company.address2, clb_company.city, clb_company.state, clb_company.zipcode ) as companyAddress");
$this->db->from($this->_table);
$this->db->join($this->_table_device, $fieldname1. " = ". $fieldname2, 'LEFT');
$this->db->where($blablafield , '0');
----------------
The error is as follows:
Error Number: 1064
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
'FROM (`clb_device`) JOIN `clb_company` ON `clb_company`.`id` = `clb_device`.`com' at line 2
SELECT `clb_device`.`id` as deviceId, `clb_pricing_specifications`.`name` as pricingSpecName, `clb_company`.`name` as companyName, `clb_device`.`mac_address` as deviceMacAddress,
`clb_device`.`reseller_model_number` as deviceModelNumber, `clb_pricing_spec_grouping`.`pricing_master_spec_id` as pricingSpecId, `clb_device`.`address` as deviceAddress,
`clb_device`.`is_home` as deviceIsHomeCharger, CONCAT(clb_company.portal_line1, `'/'`, `clb_device`.`name)` as deviceDisplayName FROM (`clb_device`) JOIN `clb_company`
ON `clb_company`.`id` = `clb_device`.`company_id` LEFT JOIN `clb_pricing_group_devices` ON `clb_device`.`id` = `clb_pricing_group_devices`.`device_id` and clb_pricing_group_devices.is_active = 1
LEFT JOIN `clb_pricing_spec_grouping` ON `clb_pricing_group_devices`.`pricing_spec_id` = `clb_pricing_spec_grouping`.`pricing_master_spec_id` LEFT JOIN `clb_pricing_specifications` ON
`clb_pricing_spec_grouping`.`pricing_spec_id` = `clb_pricing_specifications`.`id` WHERE clb_company.vendor_id is not null AND cast(substr(clb_devi
ce.software_version, 1, 3) as decimal(2,1)) > 2.0 AND clb_device.device_state > 0 GROUP BY `clb_device`.`id` ORDER BY CONCAT(trim(clb_company.portal_line1), `'/'`, trim(clb_device.name)) desc LIMIT 20
Have a look at CONCAT(trim(clb_company.portal_line1), `'/'`, trim(clb_device.name))
Please suggest the workaround.

Use this line before your query:
$this->db->_protect_identifiers=false;
This will stop adding backticks to the built query.

The solution is very simple:
In the database configuration file (./application/config/database.php) add a new element to array with default settings.
$db['default']['_protect_identifiers']= FALSE;
This solution is working for me and more elegant and professional.

All other answers are really old, this one works with CI 2.1.4
// set this to false so that _protect_identifiers skips escaping:
$this->db->_protect_identifiers = FALSE;
// your order_by line:
$this -> db -> order_by('FIELD ( products.country_id, 2, 0, 1 )');
// important to set this back to TRUE or ALL of your queries from now on will be non-escaped:
$this->db->_protect_identifiers = TRUE;

class Company_model extends MY_Model
{
----------------
$this->db->select(" count('$fieldname') as num_stations",false);
$this->db->select(" CONCAT_WS(',', clb_company.address1, clb_company.address2, clb_company.city, clb_company.state, clb_company.zipcode ) as companyAddress",false);
$this->db->from($this->_table);
$this->db->join($this->_table_device, $fieldname1. " = ". $fieldname2, 'LEFT');
$this->db->where($blablafield , '0');
----------------
The false you were talking about is what is needed, can you try the code above and copy and paste to us the output of
echo $this->db->last_query();
This will show us what the DB class is creating exactly and we can see whats working / what isn't. It may be something else (you haven't given the error from that is generated sometimes sql errors can be misleading.)
From the docs:
$this->db->select() accepts an optional second parameter. If you set it to FALSE, CodeIgniter will not try to protect your field or table names with backticks. This is useful if you need a compound select statement.

CI will only protect your ACTIVE RECORD calls, so if you are running $this->db->query(); you will be fine, and based on the notes you should be safe with AD calls like so to disable backticks (not sure why you say they don't work, but I don't see your full code, so I can't be sure)
$this->db->select('(SELECT SUM(payments.amount) FROM payments WHERE payments.invoice_id=4') AS amount_paid', FALSE);
$query = $this->db->get('mytable');
make sure FALSE is without single quotes (makes it a string), and it might not validate (not tested by me).

I think you should check DB_driver.php file, there is a variable named as protect_identifier, the point is when you will check with older version of CI, you will see that there is a condition which is missing in new version,escape variable which is checked for nullability, paste that condition from older version and you will be OK

CI_DB_active_record::where() has a third param for escaping, this has worked better for me than switching on and off CI_DB_driver::_protect_identifiers
public function where($key, $value = NULL, $escape = TRUE)
Not sure what CI version this was added in.
HTH someone

Here's a trick that worked for me. Replace this line
$this->db->join($this->_table_device, $fieldname1. " = ". $fieldname2, 'LEFT');
with this:
$this->db->join($this->_table_device, $fieldname1. " IN(". $fieldname2 .")", 'LEFT');
this will prevent CI from escaping your field. It's not ideal but it's better than the alternatives.

I just read a simple solution for this...
I changed the value of var $_escape_char (system/database/drivers/mysql/mysql_driver.php, line 36..
It was
var $_escape_char = '`';
Changed to
var $_escape_char = ' ';
and now it works... But i am affraid if I made any security issues..
Thanks

Related

How to use #JDBCDBColumn for typ ahead in xpages

Using Notes 9 and extension library's data controls...
I want to use the #JDBCDbColumn() to get the type ahead values from an Oracle table.
Is that possible at all? Does it work the same way the standard DBColumn in the type ahead?
Note that the DBColumn is looking in a huge table, but I specify a where clause that should filter what the JDBCDBColumn returns.
Somehow, nothing happens. Work sfine with Notes data though, but I need to get this working with the Oracle data.
Thanks!
Update 1: Code I have...
#JdbcExecuteQuery(
"oracle",
"select distinct postal_code from cifadmin.postal_codes where postal_code like '"
+ getComponent("CodePostal").getValue() + "%'")
Update 2: Here is the code I have right now, but it doesn't return anything:
var CodePostal = getComponent("rsSearchQuery").getValue();
if(!!CodePostal) {
var params = [CodePostal];
var a = #JdbcDbColumn("oracle", "postal_codes", "postal_code", "postal_code like ?", params);
return #Unique(a);
} else {
return "--";
}
OK, I got it working thanks to all your comments and a bit of intuition!!!
Here is the working code:
var sql = "SELECT DISTINCT POSTAL_CODE FROM cifadmin.POSTAL_CODES WHERE POSTAL_CODE LIKE'" + getComponent("PostalCode").getValue() +"%' ORDER BY POSTAL_CODE";
var res = #JdbcExecuteQuery("oracle", sql);
var values = new Array();
while (res.next()) {
values.push(res.getString("POSTAL_CODE"));
}
return values;
The thing that got it working was when I have forced 'values' as an array. Without that, it just doesn't work.
Picky and sensitive, XPages are!!!
Thanks to all for your help ;)
Just tested it the first time ever: works fine for me except I didn't use a WHERE clause in my sample and another type of DB here.
<xp:inputText id="inputText1" styleClass="form-control">
<xp:typeAhead mode="partial" minChars="1" ignoreCase="true">
<xp:this.valueList><![CDATA[#{javascript:#JdbcDbColumn("postgres", "xpagesdemo.names", "lastname")}]]></xp:this.valueList>
</xp:typeAhead>
</xp:inputText>
See live demo here: http://www.notesx.net/postgres.nsf/typeahead.xsp
Ben, I think you mean you want to put the wildcard to the end of the parameter? Not sure of your data and if you are entering an exact match? Did you try:
var params = [CodePostal+ "%"];
Also, not sure if you need to enter "cifadmin.postal_codes" for your table as you did in your sql?
Howard

Doctrine 1 allowing SQL Injection?

I found this code on a legacy app:
$salt = $this->generateSalt();
$new_pass_update = Doctrine_Query::create()
->update('User')
->set('password', '"'. $this->hash($newPass, $salt) .'"')
->set('salt', "sleep(10)") // $salt) <- I replaced this
->where('email = ?', array($mail))
->getDql();
die($new_pass_update);
I was shocked to see this Dql generated as output:
UPDATE User SET password = "3dbe00a167653a1aaee01d93e77e730e"
salt = sleep(10) WHERE email = ?
First of all, I didn't expect to see the quotation marks around the password value. I thougt that Doctrine would do that for me, so I tried the second argument without them, but I was shocked to see this Dql generated as output:
UPDATE User SET password = "3dbe00a167653a1aaee01d93e77e730e"
salt = sleep(10) WHERE email = ?
If I change ->getDql() for -> execute() that's exactly the query that is executed and the db sleeps for 10 seconds.
Why is doctrine behaving like this?
As Gumbo pointed out, the right API to use with Doctrine 1.* update syntax is:
$new_pass_update = Doctrine_Query::create()
->update('User')
->set('password', "?", $this->hash($newPass, $salt))
->set('salt', "?", $salt)
->where('email = ?', array($mail))
->execute();
so, the second argument should be "?" and the third one, the associated value.

CodeIgniter like query with single quote in search string

I have been working on this for like more than 4 hours. I have select query using active record I am doing: $this->db->like ('items.name',$search);
everything works fine but whenever there is single quote (') in the $search string it gives this error:
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 's%' OR default_items.short LIKE 'faith\'s%' LIMIT 5' at line 5
I have just checked now that it is adding double back slashes \\ instead of single in my active record for LIKE query. I tried in MySQL bt removing one slash and it is working.
My code:
$q = "faith's";
$query = $this->db->select('items_categories.slug as category_slug, items_categories.name as cat_name, items.name, items.price_value, items.cover_photo, items.slug');
$query->select('default_items.short as short',false);
$query->select('date(default_items.date_created) as date_created',false);
$query->join('items_categories','items_categories.id=items.root_id','inner');
$query->join('users','items.company_id=users.id','inner');
$query->like('items.name',$q);
$query->or_like('items.short',$q);
$query->limit(5);
$result = $query->get($this->_table);
$both_prod_results = $result->result();
I am using pyrocms 2.x.
You can try the following code maybe it can help you, but you have to add \ before every' in your requests :
$value = "faith\'s";
$sql_request = "`short` LIKE '%". $value ."%'";
$query = $this->db
->select('*')
->where($sql_request, null, false)
->get('default_items');
$result = $query->result();
dump($result);
i think i need to answer my own question.
Well this is a hack(don't think if it is secure)
I have patched my MYSQLI Driver:
i have replaced this:
return str_replace(array($this->_like_escape_chr, '%', '_'),
array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),
$str);
with this:
return str_replace(array($this->_like_escape_chr, '%', '_'),
array($this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),
$str);
it was adding extra slash. and also don't think it will allow sql injections etc anything.
if anyone knows this is right then please comment.
Thanks
Umair

CodeIgniter ActiveRecord join() method wrapping numbers with backticks

This is my active record code in CodeIgniter:
$this->db->...
$this->db->join('post_likes', 'post_likes.user_id="'.$this->db->escape($online_user).'" AND post_likes.post_id=post.id', 'left');
And this is how it is interpreted:
LEFT JOIN `post_likes` ON `post_likes`.`user_id`="`3"` AND post_likes.post_id=post.id
it gives the error:
`user_id`="`3"`
How to write a direct number in active record?
Update:
removing escape
to test it on your computer you dont need to have a database. Just trying this code shows the error:
$this->db->select('*')
->from('mytable')
->join('post_likes', 'post_likes.user_id="3" AND post_likes.post_id=post.id', 'left');
$query=$this->db->get('');
var_dump($this->db->last_query());
exit(0);
result:
A Database Error Occurred
Error Number: 1064
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 '` AND post_likes.post_id=post.id' at line 3
SELECT * FROM (`mytable`) LEFT JOIN `post_likes` ON `post_likes`.`user_id`="`3"` AND post_likes.post_id=post.id
You SHOULD not use the double quotes in SQL query:
$this->db->join('post_likes', "post_likes.user_id = $online_user AND post_likes.post_id=post.id", 'left');
Update:
This is a bug in the current CI stable version (fixed in v3.0-DEV), CI ActiveRecord methods (which doesn't implement really ActiveRecord) are prepared for simple usages.
I fixed this issue before by hacking the core files (by adding a parameter to join method to disable _protect_identifires).
There we go:
In system/database/DB_active_rec.php line #310, add $escape as 4th parameter:
public function join($table, $cond, $type = '', $escape = TRUE)
And change $match[3] = ... to:
if ($escape === TRUE)
{
$match[3] = $this->_protect_identifiers($match[3]);
}
So, you can use join($table, $cond, $type = '', $escape = FALSE) to disable escaping.
In addition, setting _protect_identifires globally to FALSE is not in a correct direction.
the only option remains is using custom query():
$sql = "SELECT * FROM some_table WHERE id = ?"
$this->db->query($sql, array(3));
Try this
$this->db->join('post_likes', "post_likes.user_id=$the_userid AND
post_likes.post_id=post.id", 'left');
or
$this->db->join('post_likes', 'post_likes.user_id="'.$the_userid.'" AND
post_likes.post_id=post.id', 'left');
Update:
Define
$db['default']['_protect_identifiers']= FALSE;
in "application/config/database.php" at the end.
Simple solution would be to temporarily set the protect_identifiers off before join query, like so:
$this->db->_protect_identifiers = false;
After making join query you could set it back to true
Works for me in CodeIgniter version 2.1.2
try this one
$this->db->join('post_likes', 'post_likes.user_id="{$online_user}" AND post_likes.post_id=post.id', 'left');
please let me know if you face any problem.
Dont use $this->db->escape
$this->db->join('post_likes', 'post_likes.user_id="'.$online_user.'" AND post_likes.post_id=post.id', 'left');

Add a clause to a MySQL statement without quotes using CodeIgniter's Active Record functions

I wanted to update a row using active records in codeigniter, and I only want to increment a field value (received_qty = received_qty +1) , I realized that I can do that in usual sql, but I cant in codeigniter active records
$update['received_qty'] = 'received_qty + 1';
$update['received_date'] = date("Y-m-d");
$this->db->where($where);
$this->db->update('vrs_distribution', $update);
anyone know how to do it using active records?
This will work.
$this->db->set('received_qty', 'received_qty + 1', FALSE);
$this->db->set('received_date', date("Y-m-d"));
$this->db->where($where);
$this->db->update('vrs_distribution');
ActiveRecord escapes everything put into a set(), where() and many other methods. Where and set can both take an optional third parameter of $escape which is a boolean. Set it to FALSE and CodeIgniter will not escape anything, meaning your field increment wont be treated as a string.
Or you can do:
$this->db->query('UPDATE vrs_distribution SET received_qty = received_qty + 1, received_date = CURDATE() WHERE id = ' . $id);
You would need to modify WHERE clause to suit you though
status set to zero(Update)
$this->db->set('IsCurrent', '0');
$this->db->where('AcademicYearID',$academicYearId);
$this->db->update('academicyear');
I was going to ask a similar question just a little bit different, but the problem was the same: I needed to update a date with an interval (expiry_date = expiry_date + interval 3 month) and Phil Sturgeon's answer did solve the problem.
However, what I realized is that you can still use the array for the fields that can have quotes, meaning that you could write:
$this->db->set('received_qty', 'received_qty + 1', FALSE);
$this->db->set('expired_date', 'CURDATE() + INTERVAL 10 DAY', FALSE); //extra example 1
$update['received_date'] = date("Y-m-d");
$update['receiver'] = $receiver_name; //extra example 2
$this->db->where($where);
$this->db->update('vrs_distribution', $update);
Where $this->db->last_query() would output:
UPDATE `vrs_distribution`
SET `received_qty` = received_qty + 1,
`expiry_date` = CURDATE() + INTERVAL 10 DAY, #extra example 1
`received_date` = '2015-07-01 16:00:00',
`receiver` = 'strike_noir', #extra example 2
WHERE #where clause with backticks
Notice that the fields where set() was used do not have quotes and appear in the first place. The rest of the query has backticks (letting "CodeIgniter protect the remaining fields").
You seem pretty close, there isn't an 'increment by one' command in CI's ActiveRecord (or in SQL for that matter).
$update['received_qty']++;
$update['received_date'] = date("Y-m-d");
$this->db->where($where);
$this->db->update('vrs_distribution', $update);

Resources