I've got a stored procedure which looks like this:
-- GET INVITES
DROP PROCEDURE IF EXISTS pda.get_invite;
DELIMITER //
CREATE PROCEDURE pda.get_invite ( code varchar(100), invitator_id bigint )
BEGIN
SELECT * FROM pda_invites
WHERE (pda_invites.invitator_id = invitator_id || invitator_id IS NULL)
AND (pda_invites.code = code || code IS NULL);
END //
CALL get_invite ( 'cec95191-23db-11e9-86d7-26374278e97f', NULL );
That works in phpmyadmin and deliver the desired result.
Now on php side, I've got some kind of query builder in a procedure class with registered arguments, which looks like this:
public function toSQL ( ) {
// Make sure params are set
if ( $this->ready == false ) {
throw new Exception ( 'Called unready procedure ' . $this->PROCEDURE_NAME . '.' );
return false;
}
// Build query
$sql= 'CALL ' . $this->PROCEDURE_NAME . '( ';
$i = 0;
foreach ( $this->args as $arg ) {
$param = $this->params[ $i ][ $arg['NAME'] ];
// Apply valid null values
if ( $param == null || $param == 'null' ) {
$sql = $sql . 'NULL';
// Apply varchar strings
} else if ( $arg['TYPE'] == 'varchar' ) {
$sql = $sql . '\'' . $param . '\'';
// Apply numeric values
} else {
$sql = $sql . $param;
}
if ( ($i+1) < $this->args_count ) {
$sql = $sql . ', ';
}
$i++;
}
$sql = $sql . ' );';
return $sql;
}
In my DB Service I trigger that by calling:
$sql = $procedure->toSQL();
$result = $con->query($sql);
echo($sql);
echo json_encode($result);
That results in following equivalent query string which is also reflected by the mysql log:
CALL get_invite( 'cec95191-23db-11e9-86d7-26374278e97f', NULL );
But the problem and question is that it produces an empty result:
{"current_field":null,"field_count":null,"lengths":null,"num_rows":null,"type":null}
Have anyone a clue why it coulds behave this way?
Related
When is Array defined in string (like in sample below), is possible get the information of this Arrays?
$id = 1;
$query =
"
SELECT *
FROM category
WHERE
id > " . [$id, 'int'] . " AND
title LIKE " . ['%test%', 'str'] . "
ORDER BY id ASC
";
$select = sql ($query);
var_dump($query) // output
//SELECT * FROM category WHERE id > Array AND title LIKE Array ORDER BY id ASC
/*
var_dump ($query->my_defined_arrays_are)
[0] => 1
[1] => '%test%'
*/
i need this for better orientation in most complex queries for $sql->bind_param();
SOLUTION:
I created small function bind() which create and store the bind data and then send it to the sql() function where are processed. This works on all queries types (SELECT, INSERT, UPDATE, DELETE..etc.).
Important for me is clear and easy readable code.
Query:
$query =
"
SELECT *
FROM category
WHERE
id > " . bind('1', 'int') . " AND
title LIKE " . bind('%test%', 'str') . "
ORDER BY id ASC
";
$select = sql ($query, $bind);
/*
var_dump ($query)
SELECT * FROM category WHERE id > {Bind_Array} AND title LIKE {Bind_Array} ORDER BY id ASC
*/
Functions:
function bind($param, $type) {
global $bind;
$bind[] = $param . ', ' . $type;
return '{Bind_Array}';
}
function sql($query, $bind_param = []) {
global $settings, $bind;
$bind = null; // clean variable; must be empty for all next operations
$mysqli = new mysqli (
$settings['db_hostname'],
$settings['db_username'],
$settings['db_password'],
$settings['db_database']
);
if ($mysqli->connect_errno) {
echo 'Failed to connect to MySQL: (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error;
return;
}
if (!$mysqli->set_charset('utf8')) {
echo 'Error loading character set utf8: (' . $mysqli->character_set_name() . ') ' . $mysqli->error;
return;
}
if ($bind_param != null) {
unset ($tmp_bind, $tmp_type);
// reserve first key
$tmp_bind[0] = null;
// replace all occurences string in query
$query = preg_replace ('/({Bind_Array})/', ' ?', $query);
// create parameters from array
foreach ($bind_param as $params) {
// explode and clean parametres
$param = array_map ('trim', explode (',', $params));
if (count ($param) != 2) {
echo 'Too much or less parameters!';
return;
}
// first is content
$tmp_bind[] = $param[0];
// second is type; create right format
$tmp_type .= str_replace (array ('int', 'str', 'double'), array ('i', 's', 'd'), $param[1]);
}
// set types to the first reserved key
$tmp_bind[0] = $tmp_type;
// replace previdous incoming data with new created
$bind_param = $tmp_bind;
}
if (!($sql = $mysqli->prepare($query))) {
echo 'Prepare failed: (' . $mysqli->errno . ') ' . $mysqli->error;
$mysqli->close();
return;
}
if (!empty ($bind_param) && $type = array_shift ($bind_param)) {
// $sql->bind_param()
call_user_func_array (array ($sql, 'bind_param'), array_merge (array ($type), array_map (function (&$item) { return $item; }, $bind_param)));
}
if (!$sql->execute()) {
echo 'Execute failed: (' . $sql->errno . ') ' . $sql->error;
$sql->close();
return;
}
if (strpos ($query, 'SELECT ') !== false || strpos ($query, 'SHOW ') !== false) {
// proceed this only if is SELECT or SHOW query
if (!($res = $sql->get_result())) {
echo 'Getting result set failed: (' . $sql->errno . ') ' . $sql->error;
$res->close();
return;
}
// store data to $output for use
for ($row_no = ($res->num_rows - 1); $row_no >= 0; $row_no --) {
$res->data_seek($row_no);
$output[$row_no] = $res->fetch_assoc();
}
$res->close();
}
$sql->close();
unset ($tmp_bind);
return $output;
}
The answer to your question is "No". When you add an array to a string, you get a string, and the original array is lost in the string "Array".
So -- why are you doing this?
My guess is that you're trying to come up with a compact representation of a query - a single object containing information on the query and its parameters (if any). This can be done, but requires some code to go with it.
For example you can create a class of your own holding the query in PDO format, with placeholders:
SELECT * FROM category WHERE id > :param1 ...
and also an array of parameters with their types:
[ 'param1' => 'int', ... ]
Then you might need a __toString() method to yield a string representation that is of use for you. The above representation can immediately be used by PDO.
Now, to specify a query with its parameters, you can use a compact representation not too unlike the one you used, leveraging an array of arrays (note that there is no string concatenation here, but rather array concatenation):
$query = ["SELECT * FROM tbl WHERE id >",['int']," AND ..." ...];
To convert from this "shorthand" to the more useable PDO_String / PDO_Param_Array representation, you could walk $query concatenating all non-string elements with placeholder parameters:
$queryString = '';
$queryParams = [ ];
$n = 1;
foreach ($query as $item) {
if (is_string($item)) {
$queryString .= $item;
} else {
$queryString .= ':param' . $n;
$queryParams[":param{$n}"] = $item[0];
$n++;
}
}
I am using WAMP v.2.5 on a Windows10 machine. My project is a PHP project running off a MySQL DB. It includes numerous AJAX calls, which work fine. I have one specific call however which is giving me a 'Unexpected end of input' error.
The call is made from a View, is directed to a global ajax handler PHP script which forwards the request to the Controller, which then asks the Model for the response. The appropriate Model method is being fired. The method contains error checking and will throw exceptions for an empty result. The DB Query within is valid, and returns results when used in the console. 9 times out of 10 however, the ajax fn will complete without receiving / reading the result of the query and thus generates the above error. Sometimes it will work fine.
When placed on a live server, everything works as it should. It's almost as if the script was running too quickly on the local machine to wait for the DB response or for any exception to be thrown.
Can anyone tell me how to properly test what's happening, or have a solution to the above problem?
EDIT:
Trail of affected code:
$(document).ready(function()
{
//some code
updateFilteredScheduleList();
//some code
});
function updateFilteredScheduleList()
{
var opts = $.extend(true, {}, dialogOptions);
getFilteredScheduleResults()
.done(function(returnedData)
{
var returnedDataObj = parseAjaxJSONResponse(returnedData);
if(returnedDataObj.hasOwnProperty('success'))
buildScheduleList(returnedDataObj.response);
})
.error(function(xhr, options, error)
{
opts.message = error;
displayDialog(opts);
return false;
});
}
function getFilteredScheduleResults()
{
var values = getFilterValues();
values.controller = 'WSVisits';
values.method = 'getFilteredScheduleResults';
console.log(values);
return $.ajax({
type: 'post',
cache: false,
data: values,
url: controllersAjaxPath
});
}
function getFilterValues()
{
var values = {};
//get values of view filters
return values;
}
function parseAjaxJSONResponse(data)
{
var opts = $.extend(true, {}, dialogOptions);
try
{
var tmp = JSON.parse(data);
if(tmp.hasOwnProperty('error'))
{
opts.message = tmp.error;
displayDialog(opts);
return false;
}
return tmp;
}
catch(e)
{
opts.message = e.message;
displayDialog(opts);
return false;
}
}
PHP method (slightly edited):
function getFilteredScheduleResults($args = null)
{
$id = intval($args['MyID']);
$region_id = (!$id) ? ( intval($args['RegionID']) > 0) ? intval($args['RegionID']) : 0 : 0;
$county_id = (!$id) ? ( intval($args['CountyID']) > 0) ? intval($args['CountyID']) : 0 : 0;
$language_id = (!$id) ? ( intval($args['LanguageID']) > 0) ? intval($args['LanguageID']) : 0 : 0;
$center_id = (!$id) ? ( intval($args['CenterID']) > 0) ? intval($args['CenterID']) : 0 : 0;
$type_id = (!$id) ? ( intval($args['TypeID']) > 0) ? intval($args['TypeID']) : 0 : 0;
$support_type_id = (!$id) ? ( intval($args['SupportTypeID']) > 0) ? intval($args['SupportTypeID']) : 0 : 0;
$address_token = (!$id) ? ( trim($args['AddressContains']) !== '') ? trim($args['AddressContains']) : null : null;
$purpose_id = (intval($args['PurposeID']) > 0) ? intval($args['PurposeID']) : 0;
$associate_id = (intval($args['AssociateID']) > 0) ? intval($args['AssociateID']) : 0;
if(!empty($args['From']))
{
$from_obj = DateTime::createFromFormat('d/m/Y', $args['From']);
$args['From'] = (!$from_obj) ? null : $from_obj->format('Y-m-d');
}
if(!empty($args['To']))
{
$to_obj = DateTime::createFromFormat('d/m/Y', $args['To']);
$args['To'] = (!$to_obj) ? null : $to_obj->format('Y-m-d');
}
$sql = " /*query*/ WHERE 1 ";
if($id)
$sql.= " AND ( s.MyID = :MyID ) ";
else
{
if($region_id)
$sql.= " AND ( RegionID = :RegionID ) ";
if($county_id)
$sql.= " AND ( CountyID = :CountyID ) ";
if($language_id)
$sql.= " AND ( LanguageID = :LanguageID ) ";
if($center_id)
$sql.= " AND ( CenterID = :CenterID ) ";
if($type_id)
$sql.= " AND ( s.TypeID = :TypeID ) ";
if($support_type_id)
$sql.= " AND ( SupportTypeID = :SupportTypeID ) ";";
if(!is_null($address_token))
$sql.= " AND ( UPPER(CONCAT_WS(' ', Add1, Add2, Add3, CityTown)) LIKE UPPER(:AddressToken) ) ";
}
$sql.= " GROUP BY s.MyID ORDER BY MyName ASC ";
$db = new Database();
try
{
$db->query($sql);
if($id)
$db->bind(':MyID', $id);
else
{
if($region_id)
$db->bind(':RegionID', $region_id);
if($county_id)
$db->bind(':CountyID', $county_id);
if($language_id)
$db->bind(':LanguageID', $language_id);
if($center_id)
$db->bind(':CenterID', $center_id);
if($type_id)
$db->bind(':TypeID', $type_id);
if($support_type_id)
$db->bind(':SupportTypeID', $support_type_id);
if(!is_null($address_token))
$db->bind(':AddressToken', '%' . $address_token . '%');
}
$db->execute();
$tmp = $db->fetchAllAssoc();
$get_assignments_only = (!empty($args['AssignmentsOnly']));
$returned = [];
$sql = " SELECT VisitID FROM visits_ws WHERE MyID = :MyID ";
if($purpose_id)
$sql.= " AND ( VisitPurposeID = :Purpose ) ";
if($associate_id)
$sql.= " AND ( ( Associate1ID = :AssociateID ) OR ( Associate2ID = :AssociateID ) OR ( Associate3ID = :AssociateID ) OR ( Associate4ID = :AssociateID ) ) ";
if(!empty($args['From']))
$sql.= " AND ( VisitDate >= :From ) ";
if(!empty($args['To']))
$sql.= " AND ( VisitDate <= :To ) ";
$db->query($sql);
foreach($tmp as $i => $t)
{
$db->bind(':MyID', $t['MyID']);
if($purpose_id)
$db->bind(':Purpose', $purpose_id);
if($associate_id)
$db->bind(':AssociateID', $associate_id);
if(!empty($args['From']))
$db->bind(':From', $args['From']);
if(!empty($args['To']))
$db->bind(':To', $args['To']);
$db->execute();
$visits = $db->fetchAllAssoc();
if( ($get_assignments_only) && (empty($visits)) )
continue;
if( ( ($purpose_id) || ($associate_id) || (!empty($args['From'])) || (!empty($args['To'])) ) && (empty($visits)) )
continue;
$tmp[$i]['HasVisits'] = (empty($visits)) ? 0 : 1;
$tmp = $schools[$i];
unset($tmp['Name']);
$schools[$i]['Address'] = build_address($tmp);
unset($schools[$i]['Add1']);
unset($schools[$i]['Add2']);
unset($schools[$i]['Add3']);
unset($schools[$i]['CityTown']);
unset($schools[$i]['CityPostCode']);
unset($schools[$i]['Name']);
unset($schools[$i]['LanguageID']);
unset($schools[$i]['PrincipalID']);
unset($schools[$i]['ContactID']);
unset($schools[$i]['TypeID']);
unset($schools[$i]['CenterID']);
unset($schools[$i]['SupportTypeID']);
unset($schools[$i]['CountyID']);
unset($schools[$i]['AreaCodeID']);
unset($schools[$i]['NetworkCodeID']);
unset($schools[$i]['RegionID']);
$returned[] = $tmp[$i];
}
return ['jct_success'=>'ok', 'response'=>$returned];
}
catch(PDOException $e)
{
return ['jct_error'=>$e->getMessage()];
}
}
Found the culprit:
I had to update my Apache max_input_vars to a higher limit to allow the number of individual parameters being returned to actually be returned. Post size was not the issue.
public function test_passing_string() {
$this - > load - > model(array('registration/Registration_model', 'Jawaban_lab_model'));
$registration = new Registration_model();
$jawaban_lab = new Jawaban_lab_model();
$id = "kuda4";
$jawaban_lab - > load($id); //load jawaban_lab from id
$manualy_written_registration_number = "REG/FM/130102-0001";
echo "registration number from jawaban_lab->registration_number : ".$jawaban_lab - > registration_number
.
"<br> registration number from manualy_written_registration_number : ".$manualy_written_registration_number;
//$registration->load($jawaban_lab->registration_number);
$registration - > load($manualy_written_registration_number);
echo "<br> patient id : ".json_encode($registration - > PatientID);
}
Before go to the question, I will explain my code.
On test_passing_string() function, I call 2 model, and create object for each model there are $registration and $jawaban_lab.
To load data from model I create a load() function. load() has two parameters: column_value and column_name. The default value for column_name is that model's Primary Key.
BUT
The problem comes from
$registration->load($jawaban_lab->registration_number);
I can't retrieve any $registration object data, then I test it by passing the value manually by write this:
$manualy_written_registration_number = "REG/FM/130102-0001";
$registration - > load($manualy_written_registration_number);
And the result appear, doesn't that mean my load() function is fine?
Then I check value inside $jawaban_lab->registration_number by echoing it, surprisingly it display same value as my $manualy_written_registration_number variable.
This is screenshoot in my browser when I run test_passing_string() function:
Using $manualy_written_registration_number value
Using $jawaban_lab->registration_number value
Why can't I use the value from
$jawaban_lab->registration_number even though it has the same value as
my manually writen registraiton number?
public function load($column_value, $column_name = NULL) {
$query = NULL;
if ($column_name != NULL) {
// using custom column.
$query = $this->dbs->get_where($this::DB_TABLE, array(
$column_name => $column_value
));
} else {
// using column primary key .
$query = $this->dbs->get_where($this::DB_TABLE, array(
$this::DB_TABLE_PK => $column_value
));
}
if ($query->row()) {
$this->populate($query->row());
}
}
I use multiple database using CodeIgniter 3, registration_model from SQL server and jawaban_lab from MySQL, jawaban lab have column registration_number to store registration_model primary key
var_dump
First of all thanks to rlanvin and Nirajan N Raju
from rlanvin's comment, i find out the problem is come from codeigniter's query helper, because when i enable codeigniter profiling sql server query return "SELECT CASE WHEN (##OPTIONS | 256) = ##OPTIONS THEN 1 ELSE 0 END AS qi"
so i think codeigniter might be cannot generate query so i create the query manually
i change
public function load($column_value, $column_name = NULL) {
$query = NULL;
if ($column_name != NULL) {
// using custom column.
$query = $this->dbs->get_where($this::DB_TABLE, array(
$column_name => $column_value
));
} else {
// using column primary key .
$query = $this->dbs->get_where($this::DB_TABLE, array(
$this::DB_TABLE_PK => $column_value
));
}
if ($query->row()) {
$this->populate($query->row());
}
}
to this
public function load($column_value, $column_name = NULL) {
$query = NULL;
if ($column_name != NULL) {
$query = $this->dbs->query("SELECT * FROM " . $this::DB_TABLE . " WHERE " . $column_name . " LIKE '" . trim($column_value) . "'");
} else {
$query = $this->dbs->query("SELECT * FROM " . $this::DB_TABLE . " WHERE " . $this::DB_TABLE_PK . " LIKE '" . trim($column_value) . "'");
}
if ($query->row()) {
$this->populate($query->row());
}
}
I'm using the last version of ADOdb PHP (5.18) to make some queries in a SQL Server database.
I select data in this way:
$rS = $localDb->Execute($sql);
if (!$rS) {
echo "Error: " . $localDb->ErrorMsg() . "\n";
}
else {
$tot = $rS->RecordCount();
echo " " . $tot . " record da inserire...\n";
while (!$rS->EOF) {
$id = $rS->fields['id'];
$field1 = $rS->fields['field1'];
$field2 = $rS->fields['field2'];
$rS->MoveNext();
}
}
All works, and I fetch data, but when a field in the database's current row is NULL, the relative element in $rS->fields has the value of the value of the last not-NULL row for the same field.
This is a big problem because I don't have correct data in the current row.
I tried search for this problem but I did not find any solution.
Coud you help me, please?
Look into adodb5/adodb.inc.php and find the GetRowAssoc function. It should be the following defintion:
function GetRowAssoc($upper=ADODB_ASSOC_CASE_UPPER)
{
$record = array();
if (!$this->bind) {
$this->GetAssocKeys($upper);
}
foreach($this->bind as $k => $v) {
if( array_key_exists( $v, $this->fields ) ) {
$record[$k] = $this->fields[$v];
} elseif( array_key_exists( $k, $this->fields ) ) {
$record[$k] = $this->fields[$k];
} else {
# This should not happen... trigger error ?
$record[$k] = null;
}
}
return $record;
}
I want to fetch contents with multiple filters, right now there's only one.
For Example:
SELECT * FROM Table1 WHERE status=true AND category = 'Camera' AND
model = 'Samsung' AND type = 'New'
I want to create an array for it. But as I'm a newbie in this one not getting a lead.
function getAllRequests($filter){
if(empty($filter)){
$addfilter = '';
}else{
$addfilter = 'AND cat_id=' . $filter;
}
}
$sql = 'SELECT * FROM Table1 WHERE status=true' . $filter;
Any help will be appreciated.
This will get you closer to the solution, though it will not replace the cat_id in the query, which will certainly be wrong - though impossible to do too much more without the array structure:
function getAllRequests($filter)
{
$addfilter="";
if(!empty($filter))
{
foreach($filter as $val)
{
$addfilter. = ' AND cat_id=' . $val .'\'';
}
}
return $addFilter;
}
$myFilters=getAllRequests($filter);
$sql = 'SELECT * FROM Table1 WHERE status=true' . $myFilters;
On the other hand, if your array is strucutred in a way like this:
array{ category => camera, model => samsung); // etc
you could use the following:
function getAllRequests($filter)
{
$addfilter="";
if(!empty($filter))
{
foreach($filter as $key => $val)
{
$addfilter. = " AND `$key` = '$val'";
}
}
return $addFilter;
}
$myFilters=getAllRequests($filter);
$sql = 'SELECT * FROM Table1 WHERE status=true' . $myFilters;
Edit: You can loop through all the filters in the following manner:
function getAllRequests()
{
$addfilter="";
if(!empty($_REQUEST))
{
foreach($_REQUEST as $key => $val)
{
$addfilter. = " AND `$key` = '$val'";
}
}
return $addFilter;
}
$myFilters=getAllRequests();
$sql = 'SELECT * FROM Table1 WHERE status=true' . $myFilters;
You don't need to pass the $_REQUEST (which will work for both GET and POST) as it already a superglobal.
function getAllRequests($filter){
if(empty($filter)){
$addfilter = '';
}else{
$addfilter = 'AND cat_id=' . $filter;
}
}
$sql = 'SELECT * FROM Table1 WHERE status=true' . $addfilter;
You can use another approach, which is using optional parameters and it will make your WHERE clause dynamic as you want. In this approach you pass all parameters' values directly to the sql query which should look like so:
SELECT *
FROM Table1
WHERE 1 = 1
AND (#status IS NULL OR status = #statusParam)
AND (#category IS NULL OR category = #categoryParam)
AND (#model IS NULL OR model = #modelParam)
AND (#type IS NULL OR type = #typeParam)
Then If any of the parameters #statusParam, #categoryParam, #modelParam or #typeParam passed to the query with NULL values, then the comparison with the column holding that value will be ignored. I used the predicate 1 = 1, in case all the values passed to the query with all NULL values in the case all the WHERE clause will be ignored as it won't presented, since WHERE 1 = 1 always true and it will be like SELECT * FROM Table1.
use this
function getAllRequests($filter){
if(empty($filter)){
$addfilter = '';
}else{
$addfilter .= 'AND cat_id=' . $filter;
}
return $addfilter;
}
$sql = 'SELECT * FROM Table1 WHERE status=true' . getAllRequests($filter);
When you are sending array make sure it has indexes
$conditions = array('category' => 'Camera', 'model' => 'Samsung' , 'type' => 'New')
Now loop through it. in your else condition
foreach($conditions as $key =>$value){
$addfilter .= 'AND ' . $key . ' = ' . $value;
}