ActiveRecord search returns 'Syntax error or access violation' error - activerecord

In my Yii application, I have a model that represents siteconfig table and have four columns:
integer config_id,
string key,
string value,
string update_time.
I created a model using Gii (to ensure that I will not make any mistakes). I don't publish entire code here, cause this is 100% unmodified by me, standard model code generated by Gii. Since my problem is related to search, I only publish important part of generated code (the search() method):
public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->compare('config_id',$this->config_id);
$criteria->compare('key',$this->key,true);
$criteria->compare('value',$this->value,true);
$criteria->compare('update_time',$this->update_time,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
I'm trying to use generated model in normal Yii ActiveRecord search like that:
$etona = new SiteConfigurationRecord();
$crit = new CDbCriteria();
$crit->select = "value";
$crit->condition = "key=:key";
$crit->params = array(":key"=>"sitename");
$etona = $etona->find($crit);
But, instead of getting expected search results, a strange (for me) error occurs:
CDbCommand failed to execute the SQL statement: SQLSTATE[42000]:
Syntax error or access violation: 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 'key='sitename' LIMIT 1' at line 1.
The SQL statement executed was: SELECT value FROM siteconfig t
WHERE key=:key LIMIT 1
Where did I go wrong?

You used key for column name, which is a reserved word in MySQL. Yii uses table alias in queries, but does not take any special care in case of reserverd word used as columns names. So, you have to take care of this by yourself.
For example:
$etona = new SiteConfigurationRecord();
$crit = new CDbCriteria();
$crit->select = "value";
$crit->condition = "t.key=:key"; // 't' is default alias
$crit->params = array(":key"=>"sitename");
$etona = $etona->find($crit);
This should solve your problem.

As #Dmitry explained, SQL doesn't allow you to use the column name key. The Yii call in the code in your answer works because Yii performs parameter binding automatically, using names other than reserved words for the parameters. And it also uses fully-qualified column names (prefixes all column name references with <tablename>., regardless of what invalid column name (reserved words) you pass the findByAttributes method.

now it works.. ^^
i just use this code...
$etona = SiteConfigurationRecord::model()->findByAttributes(array('key'=>'sitename'));
maybe i need to study activerecord more somehow...
but still i don't know why the code above doesn't work

Related

Trying to get property 'stock_name' of non-object error while inserting data

i am getting data using crawl and trying to insert into database. i get abbreviations of company name and have a command to get the full form of the abbreviations. it works fine if the company name already exists in companies table, and when the command handle is run new company name is also inserted and i can get the name from the table but cannot insert the name as it shows error. here is the code:
$data['symbol']=$es[1];
$name=DB::table('companies')
->select('stock_name')
->where('stock_symbol',$data['symbol']=$es[1])
->first();
if(!empty($name->stock_name)){
$data['company_name']= $name->stock_name;
}else{
\Artisan::call("company:handle");
$name=DB::table('companies')
->select('stock_name')
->where('stock_symbol',$data['symbol']=$es[1])
->first();
$data['company_name']= $name->stock_name;
}
You can resolve it by doing
//...
$data['company_name']= $name->stock_name ?? '';
//...
Null coalescing (??) is a new operator introduced in PHP 7. This
operator returns its first operand if it is set and not NULL.
Otherwise it will return its second operand.

Insert using a raw SQL query to avoid SQL injection?

I have personalized tables and a bunch of composite keys in my database so I am using raw SQL queries to perform the CRUD operations needed. I found a way to make an insert following the documentation and it worked, but I am wondering, isn't this method vulnerable to SQL injection? I am using the user's input to insert this data and I don't see where's the sanitization of it, maybe I am just wrong though. Can you guide me? Here's my code:
public function store(Request $request)
{
/*No composite keys here so I am using Eloquent*/
$song = new Song();
$song->code = $request->code;
$song->title = $request->title;
$song->artist = $request->artist;
$song->length = $request->length;
$song->album = $request->album;
$song->save();
$genre = new Genre();
$genre->id_gen = $request->genre;
$genre->id_song = $request->code;
DB::insert('INSERT INTO genres (id_gen, id_song) values (?, ?)', [$genre->id_gen, $genre->id_song]);
return $song;
}
Using an insert in this fashion does not mean a SQL injection risk as this is what is known as a parameterized query. You are generating a query as a string, and then the database system performs its own properly managed replacements to construct the query. I can't recall if this is done at the database or driver level.
What you have done is exactly what Laravel does internally anyway (construct parameterized queries).
In terms of your actual code, you're doing something a little odd. By creating the Genre object, applying your request to it and saving, you're doing an insert anyway. There is no real need for the raw insert;
Be sure to validate your request object!
The query uses placeholders (in other words the query-string can be stored as a template or as a constant).
This gives the sql-engine a chance to convert the query to a prepared-statement.
I do not have any knowledge of laravel but this looks like a legitimate use of prepared statements (and consequently immune to sql injection).
In other words the approach looks safe (atleast w.r.t sql-injection).

codenigiter: sql injection and xss_clean

I am using active record in codenigiter to do some query and I read some docs that say using AR will escape the parameter automaticly.I want to comfirm this and I read the source code of AR class,but I am confused!
I do some test,eg,I access the url as follow is:
http://wrecruit.tudouya.com/company/editProfile/4
then I enable the profiler in CI.
when I add a single quote at the end of url as follows:
http://wrecruit.tudouya.com/company/editProfile/4'
I see the real sql statement the query execute and I got this:
SELECT *
FROM (`wy_company`)
WHERE `id` = '4%27'
the single quote is escaped to '%27',I want to know how this escape happen?maybe it's escaped by the input class?
Url adress is always encoded with urlencode function. This function replace all symbols with codes.
If you want to prevent xss you should to check you parameter. May be like this (in controller Company):
public function editProfile($id)
{
$id = (int)$id;
if($id){
// model code execute - loading DB data
}
}
And in DB query you should use Query Bindings, or use Active record. Them both are prevent injection.

Zend DbTable case insensitive

I have a login system for my webapp that works well using the Zend auth adapter but the problem is I want the email to be case insensitive when a user logs in. I am using Oracle as the back end DB and normally I would user the LOWER(EMAIL)=LOWER(:email) method. I tried to pass that Oracle function in the setIdentityColumn() but I get the error:
The supplied parameters to Zend_Auth_Adapter_DbTable failed to produce
a valid sql statement, please check table and column names for
validity.
protected function _getAuthAdapter()
{
//$dbAdapter = Zend_Db_Table::getDefaultAdapter();
$db = Zend_Registry::get('db');
$authAdapter = new Zend_Auth_Adapter_DbTable($db);
$authAdapter->setTableName('USER_TABLE')
->setIdentityColumn('LOWER(EMAIL)') //Tried to pass LOWER()
->setCredentialColumn('ENCODED_PW')
->setCredentialColumn('PASSWORD');
return $authAdapter;
}
The error is coming from the function _authenticateCreateSelect() in the Zend_Auth_Adapter_DbTable class. The problem is this part of the script:
$dbSelect->from($this->_tableName, array('*', $credentialExpression))
->where($this->_zendDb->quoteIdentifier($this->_identityColumn, true) . ' = ?', $this->_identity);
The quoteIdentifier() method is like PHP quote() and is turning a query like this:
select * from LOWER(:email)
into this:
select * from "LOWER(:email)"
Anyone see a way around this?
Kind Regards
Nathan
Try something like this:
$authAdapter->setTableName('USER_TABLE')
->setIdentityColumn(new Zend_Db_Expr('LOWER(USERID)'))
->setCredentialColumn('PASSWORD');
The problem is that if you pass 'LOWER(USERID)' as a simple string, Zend will put quotes around it, causing it to create an invalid query. Using Zend_Db_Expr will stop Zend doing this.

Issue with where clause in active record class

$params['title'] is a string and $params['feed'] is an integer. Column's 'title' type is varchar and column's 'feed' type is integer. The PHP code is:
$this->db->where('title', $params['title']);
$this->db->where('feed', $params['feed']);
$query = $this->db->get('news');
So everything should be fine, BUT...
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 'riots: intense anger after deaths of three young men AND `feed` = '1'' at line 3
SELECT * FROM (`news`) WHERE `title` = Birmingham riots: intense anger after deaths of three young men AND `feed` = '1'
Filename: C:\path\system\database\DB_driver.php
Line Number: 330
Of course, it's obvious why an error occurred. Is there any way to fix it (apart from not using active record class)? Looked at CI's documentation, but have no idea why this problem occurred.
EDIT
Solved by changing to
$this->db->where('title', (string) $params['title']);
And other clause accordingly. And now have a bunch of identical errors in other models. Well, I guess the only solution is to add var type everywhere manually.
It looks like this SimpleXML class's title property isn't a string.
You could simply cast it to a string as soon as you get it, instead of waiting until you do the insert.
data['title'] = (string) $item->title;

Resources