Perl SQL::Parser table alias substitution: works for SELECT column names but not for WHERE column names - sql

I'm trying to parse some SQL queries stored in a log database -- I don't want to submit them to a SQL database, just to extract the fields used in the SELECT and WHERE clause.
I've been fiddling with several SQL parsers in Java, Python and Perl. The one that seems to work better for my problem are SQL::Parser and SQL::Statement. With those I was able to write the following code:
#!/usr/bin/perl
use strict;
use SQL::Parser;
use SQL::Statement;
use Data::Dumper;
my $sql = "SELECT sl.plate,sp.fehadop FROM sppLines AS sl ".
"JOIN sppParams AS sp ON sl.specobjid = sp.specobjid ".
"WHERE fehadop < -3.5 ";
my $parser = SQL::Parser->new();
my $stmt = SQL::Statement->new($sql,$parser);
printf("COMMAND [%s]\n",$stmt->command);
printf("COLUMNS \n");
my #columns = #{$stmt->column_defs()};
foreach my $column ( #columns)
{
print " ".$column->{value}."\n";
}
printf("TABLES \n");
my #tables = $stmt->tables();
foreach my $table ( #tables)
{
print " ".$table->{name}."\n";
}
printf("WHERE COLUMNS\n");
my $where_hash = $stmt->where_hash();
print Dumper($where_hash);
Sorry if it is too long, it is the smallest, self-contained example I could devise.
The output of this code is:
COMMAND [SELECT]
COLUMNS
spplines.plate
sppparams.fehadop
TABLES
spplines
sppparams
WHERE COLUMNS
$VAR1 = {
'arg1' => {
'value' => 'fehadop',
'type' => 'column',
'fullorg' => 'fehadop'
},
'op' => '<',
'nots' => {},
'arg2' => {
'str' => '-?0?',
'fullorg' => '-3.5',
'name' => 'numeric_exp',
'value' => [
{
'fullorg' => '3.5',
'value' => '3.5',
'type' => 'number'
}
],
'type' => 'function'
},
'neg' => 0
};
The parser returns the name of columns (obtained through a call to $stmt->column_defs()) already renamed with the real tables names (e.g. spplines.plate instead of s1.plate) -- this is what I want.
I also want the names of the columns used in the WHERE clause.
I already know how to recursively parse the results of $stmt->where_hash() (didn't include the code to make the post clear), but even from dumping its contents I can see that the column names are not associated with the tables.
I would like to ensure that the columns names in the WHERE clause are also preceded by the tables name. After parsing the results of $stmt->where_hash() I would get sppparams.fehadop instead of fehadop.
Is this possible with SQL::Parser?
Thanks
(big edit -- tried to make the question clearer)

Since SQL::Statement has an eval_where, I suspect there might be a better way, but you can try a function like this:
get_column($stmt->column_defs(), $where_hash->{arg1});
sub get_column {
my ($columns, $arg) = #_;
return $arg->{fullorg} if ($arg->{type} ne 'column');
foreach my $col (#$columns) {
return $col->{value} if ($col->{fullorg} eq $arg->{fullorg});
my ($name) = ( $col->{fullorg} =~ /([^.]+)$/);
return $col->{value} if ($name eq $arg->{fullorg});
}
return $arg->{fullorg};
}

Related

Extra, blank row from PDO select on Sqlite

This involves a Sqlite database, PHP 7 and PDO. The query code is:
...
$stmt = $pdo->query('SELECT * FROM images');
while($row = $stmt->fetch(\PDO::FETCH_ASSOC)){
$images[] = [
"image_id" => $row["image_id"],
"date" => $row["date"],
"photographer" => $row["photographer"],
...
];
}
echo $stmt->rowCount() . " rows<br>";
echo count($images) . " images<br>";
var_dump($images);
return $images;
}
(Note: This is based on http://www.sqlitetutorial.net/sqlite-php/query/ . It will be revised soon to do prepared statements, enumerating cols, etc., once the problem described here is solved.)
The echos report "0 rows" and "2 images". The var_dump() outputs:
array(2) { [0]=> array(0) { } [1]=> array(14) { ["image_id"]=> ...
So clearly there's an extra, empty array in the first position in the outer array. In the calling code, which collects the $image array as return value, count($array) gives 2 not 1 (and code expecting name/value pairs in each row breaks).
The problem is, there's only one row in the table. This appears clearly on the command line: sqlite> select * from images; gets one row and:
sqlite> select count(*) as c from images;
1
What's wrong here?
Different array syntax solved it.
$stmt = $pdo->query('SELECT * FROM images');
$images = array();
while($row = $stmt->fetch(\PDO::FETCH_ASSOC)){
$images[] = array(
"image_id" => $row["image_id"],
"date" => $row["date"],
"photographer" => $row["photographer"],
...
);
}
I'm still not clear on the reason, but this way avoids the anomalous empty row.

How to return a JSON array from sql table with PhalconPHP

I have several tables that have JSON arrays stored within fields.
Using PHP PDO I am able to retrieve this data without issue using:
$query1 = $database->prepare("SELECT * FROM module_settings
WHERE project_token = ? AND module_id = ? ORDER BY id DESC LIMIT 1");
$query1->execute(array($page["project_token"], 2));
$idx = $query1->fetch(PDO::FETCH_ASSOC);
$idx["settings"] = json_decode($idx["settings"]);
This returns a string like:
{"mid":"","module_id":"1","force_reg_enable":"1","force_reg_page_delay":"2"}
Attempting to gather the same data via PhalconPHP
$result = Modulesettings::findFirst( array(
'conditions' => 'project_token = "' . $token . '"' ,
'columns' => 'settings'
) );
var_dump($result);
Provides a result of
object(Phalcon\Mvc\Model\Row)#61 (1) { ["settings"]=> string(167) "{"text":"<\/a>
<\/a>
","class":""}" }
What do I need to do different in Phalcon to return the string as it is stored in the table?
Thank you.
You have 2 approach
First :
Get the settings with this structure :
$settings = $result->settings;
var_dump($settings);
Second :
First get array from resultset, then using the array element :
$res = $result->toArray();
var_dump($res['settings']);
Try it.
You can decode json right in your Modulesettings model declaration:
// handling result
function afterFetch() {
$this->settings = json_decode($this->settings);
}
// saving. Can use beforeCreate+beforeSave+beforeUpdate
// or write a Json filter.
function beforeValidation() {
$this->settings = json_encode($this->settings);
}

Conditions in JOINed tables shows error CakePHP

I have two tables employee_personals where all the personal record of the employee is stored and telephone_bills where the telephone bills paid to a particular employee is stored for each month. Now in my employeePersonalsController.php I have a function called api_show_employees() which is similar to below :
function api_show_employees() {
//$this->autoRender = false;
//Configure::write("debug",0);
$office_id = '';
$cond = '';
if(isset($_GET['office_id']) && trim($_GET['office_id']) != '') {
$office_id = $_GET['office_id'];
$cond['EmployeePersonal.office_id'] = $office_id;
}
if(isset($_GET['telephoneBillTo']) && isset($_GET['telephoneBillFrom']) ) {
if($_GET['telephoneBillTo'] != '' && $_GET['telephoneBillFrom'] != '') {
$cond['TelephoneBill.bill_from'] = $_GET['telephoneBillFrom'];
$cond['TelephoneBill.bill_to'] = $_GET['telephoneBillTo'];
}
}
$order = 'EmployeePersonal.name';
// $employee = $this->EmployeePersonal->find('all');
$employee = $this->EmployeePersonal->find('all',array('order' => $order,'conditions'=>$cond));
//return json_encode($employee);
}
This functions basically finds all the employees who paid bills in the given period. But I am getting an error
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'TelephoneBill.bill_from' in 'where clause'
Models : EmployeePersonal.php:
var $hasMany = array(
'TelephoneBill' => array(
'className' => 'TelephoneBill',
)
);
TelephoneBill.php
public $name = 'TelephoneBill';
var $hasMany = array('EmployeePersonal');
NB: If I skip the bill_from and bill_to conditions, I am getting the results , with TelephoneBill array !
TLDR: use Joins instead.
Details/Notes:
1) it looks like you're using recursive. Don't do that. Use Containable instead.
2) You can't limit the parent model based on conditions against data from a contained/recursive-included table - instead, use Joins.
2b) Or, you could query from the other direction, and query your TelephoneBill with conditions, then contain the EmployeePersonal.

How to generate list of tables for DB using RoseDB

I have to list the tables for a given database using RoseDB . I know the mysql command for it :
SHOW TABLES in DB_NAME;
How do I implement this in rose DB ? Pleas help
It's not really a Rose::DB-specific question. Simply use the database handle how you would normally in DBI:
package My::DB {
use Rose::DB;
our #ISA = qw(Rose::DB);
My::DB->register_db(
domain => 'dev',
type => 'main',
driver => 'mysql',
...
);
My::DB->default_domain('dev');
My::DB->default_type('main');
}
use Carp;
my $db = My::DB->new();
my $sth = $db->dbh->prepare('SHOW TABLES');
$sth->execute || croak "query failed";
while (my $row = $sth->fetchrow_arrayref) {
print "$row->[0]\n";
}

Trying to get property of non-object Yii

I dont get this error, there is a row in database.
$tip = StringHelper::trimmer($_GET['tip']);
$sql = 'SELECT id FROM contact_reasons WHERE alias = "' . $tip . '"';
$model = ContactReasons::model()->findAllBySql( $sql );
die($model->id);
if(!is_null($model)) {
$this->render('kontakt', array(
'model' => $model,
));
} else {
$this->renderText('Tražena stranica ne postoji.');
}
I used debug to see if there is a response, and even used query on database, and it returns a row with ID. I get this error on line with die();
Please note that, findAllBySql returns an array of CActiveRecords, while findBySql returns a single CActiveRecord. You may also use parameter binding for your SQL statements to prevent SQL injection.
see also http://www.yiiframework.com/doc/api/1.1/CActiveRecord