MySQL custom global defined variable - php

In my database design, I tend to store some variable that is meant to be acting as a ROLE or TYPE as SMALLINT.
For example:
CREATE TABLE `house` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` smallint(11) NOT NULL,
And in PHP, I do:
define('HOUSE_SMALL_TYPE', '0');
define('HOUSE_MEDIUM_TYPE', '1');
So in PHP, in SELECT queries I do:
$this->db->query("SELECT * FROM house
WHERE type = ?;", HOUSE_SMALL_TYPE);
My questions are:
In the PHP part, is there is a better way to do this?
In the MySQL itself, does MySQL also has global define functionality (like the define in PHP)?
I also want to do kind of
SELECT * FROM house WHERE type = HOUSE_SMALL_TYPE;
in MySQL query.
My purpose is that when I do SELECT in MySQL, no way I'm going to keep mapping the value 0,1,2 with its real meaning. Just convenience for viewing the tables values, without changing the structure table and fields.

Since MySQL 5.5 it's not possible to set a global user-defined variable.
A work-around might be to create a stored procedure that would return what you need.
DROP PROCEDURE IF EXISTS HOUSE_SMALL_TYPE;
DELIMITER //
CREATE PROCEDURE HOUSE_SMALL_TYPE ()
BEGIN
SELECT 0;
END//
DELIMITER ;
and then call it.
CALL HOUSE_SMALL_TYPE();
The DROP statement is required in order to be able to modify it.

IMHO, MySQL has a huge gap in this area, apparently in the latter versions. One alternative might have been to resort to setting OS environment variables, but how such values can be retrieved from within MySQL, I've been unable to see.
There's a whole page here: https://dev.mysql.com/doc/refman/5.0/en/setting-environment-variables.html teaching us how to "set" OS environment variables in the shell, but not a word on actually calling such variables in MySQL.
As another workaround, using a FUNCTION might be considered more lightweight than a STORED PROCEDURE, like so:
CREATE DEFINER=`root`#`localhost` FUNCTION `DEFAULT_COUNTRY_CODE`() RETURNS CHAR(4)
DETERMINISTIC
RETURN '+234';
Elsewhere in your query, you can then do:
SELECT CONCAT(DEFAULT_COUNTRY_CODE(), "-", telephone) FROM contacts WHERE CountryCode = "NGA"

Your approach is fine, if you want to see the values in MySQL instead of 1, 2, 3 etc. then consider this:
define('HOUSE_SMALL_TYPE', 'HOUSE_SMALL_TYPE');
define('HOUSE_MEDIUM_TYPE', 'HOUSE_MEDIUM_TYPE');
Then in MySQL you can use:
SELECT * FROM house WHERE type = 'HOUSE_SMALL_TYPE';
You just need to remember that you cannot just jam any value you like into house.type without having support for it in PHP.
Even better consider this:
class HouseType {
const SMALL = 'SMALL';
const MEDIUM = 'MEDIUM';
}
or
class House {
const TYPE_SMALL = 'SMALL';
const TYPE_MEDIUM = 'MEDIUM';
}
because then you can use HouseType::SMALL or House::TYPE_SMALL in your PHP code rather than using a global define. By doing this you may benefit from code completion in some IDE's.

Since MySQL 5.5 it's not possible to set a global user-defined variable, another workaround could be helping table like
create table glob_var(key varchar(10) unique not null, val varchar(10) not null);

I suggest using MySQL variables:
SET HOUSE_SMALL_TYPE = 0;
SET HOUSE_MEDIUM_TYPE = 1;
Then, in your queries you may use these variables:
SELECT * FROM house WHERE type = #HOUSE_SMALL_TYPE;
This method defines session variables:
If you change a session system variable, the value remains in effect
until your session ends or until you change the variable to a
different value. The change is not visible to other clients.
If you want to define global MySQL variables (available to all sessions):
SET GLOBAL HOUSE_SMALL_TYPE = 0;
SET GLOBAL HOUSE_MEDIUM_TYPE = 1;
To indicate explicitly that a variable is a global variable, precede
its name by GLOBAL or ##global.. The SUPER privilege is required to
set global variables.
Documentation:
SET statement
Using system variables
User-defined variables

Related

Postgresql custom function returning table

I am using PostgreSQL 9.1.11.
I need to return result of SELECT to my php script. The invocation in php is like this:
$res = $pdb->getAssoc("SELECT * FROM my_profile();");
The class code to illustrate what is going on in php
public function getAssoc($in_query) {
$res = pg_query($this->_Link, $in_query);
if($res == FALSE) {
return array("dberror", iconv("utf-8", "windows-1251", pg_last_error($this->_Link)));
}
return pg_fetch_all($res);
}
Next comes my function in Postgres. I fully re-create database by dropping in a script when I update any function. (The project is in the early stage of development.) I have little to no experience doing stored procedures.
I get this error:
structure of query does not match function result type
CONTEXT: PL/pgSQL function "my_profile" line 3 at RETURN QUERY )
Trying to write:
CREATE FUNCTION my_profile()
RETURNS TABLE (_nick text, _email text) AS $$
BEGIN
RETURN QUERY SELECT (nick, email) FROM my_users WHERE id = 1;
END;
$$
LANGUAGE 'plpgsql' SECURITY DEFINER;
Table structure is:
CREATE TABLE my_users(
id integer NOT NULL,
nick text,
email text,
pwd_salt varchar(32),
pwd_hash character(128),
CONSTRAINT users_pk PRIMARY KEY (id)
);
When I return 1 column in a table the query works. Tried to rewrite procedure in LANGUAGE sql instead of plpgsql with some success, but I want to stick to plpgsql.
The Postgres 9.1.11, php-fpm I am using is latest for fully updated amd64 Debian wheezy.
What I want to do is to return a recordset containing from 0 to n rows from proc to php in an associative array.
This part is incorrect:
RETURN QUERY SELECT (nick, email) FROM my_users WHERE id = 1;
You should remove the parentheses around nick,email otherwise they form a unique column with a ROW type.
This is why it doesn't match the result type.
#Daniel already pointed out your immediate problem (incorrect use of parentheses). But there is more:
Never quote the language name plpgsql in this context. It's an identifier, not a string literal. It's tolerated for now since it's a wide-spread anti-pattern. But it may be considered a syntax error in future releases.
The SECURITY DEFINER clause should be accompanied by a local setting for search_path. Be sure to read the according chapter in the manual.
Everything put together, it could look like this:
CREATE FUNCTION my_profile()
RETURNS TABLE (nick text, email text) AS
$func$
BEGIN
RETURN QUERY
SELECT m.nick, m.email FROM my_users m WHERE m.id = 1;
END
$func$
LANGUAGE plpgsql SECURITY DEFINER SET search_path = public, pg_temp;
Replace public whit the actual schema of your table.
To avoid possible naming conflicts between OUT parameters in RETURNS TABLE ... and table columns in the SELECT statement I table-qualified column names with the given alias m.

Can't add 1 to a user-variable in SQL incrementally

It's possibly due to the variable #count not being recognised as an integer but in my research I've come across many example similar to #count = #count + 1; That's why I'm confused as to why this breaks my code when introduced:
$setCount = "SELECT #count:=5";
$uncategorise = "UPDATE pictures
SET category = '0',
pictureorder = #count,
#count:=#count+1
WHERE category = '$categoryID'
AND username = '$username';
$queryCount = mysql_query($setCount) or die(mysql_error());
$queryUncat = mysql_query($uncategorise) or die(mysql_error());
because this is an user defined variable,
which only stand/available for the same session/connection
You can store a value in a user-defined variable in one statement and then refer to it later in another statement. This enables you to pass values from one statement to another. User-defined variables are connection-specific. That is, a user variable defined by one client cannot be seen or used by other clients. All variables for a given client connection are automatically freed when that client exits.
There is an easy solution -- php mysqli_multi_query
You could create a field in the DB that does the counting and then retrieve the data from there.

serialize not working for me in drupal

i am trying to insert data to database but it removing braces'{}' while inserting i am using this code.
<pre><code>
require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
$aa['alt']="happy alt";
$aa['title']="happy title";
$sldata=serialize($aa);
$sql="Insert into test(pval) values('".$sldata."')";
echo $sql;
db_query($sql);
</pre></code>
my db structure is as
<pre><code>
CREATE TABLE IF NOT EXISTS `test` (
`sl` int(11) NOT NULL AUTO_INCREMENT,
`pval` text NOT NULL,
PRIMARY KEY (`sl`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
</pre></code>
suggest me what is wrong here..
Drupal uses {} arround the tables names, to be able to do some manipulations on those names -- like prefix them, if you have configured it to do so.
So, you must not use {} in your query -- except arround tables names, of course.
Instead of brutaly injecting your serialized-string into the SQL query, you must use place-holders in it -- and pass the corresponding values to db_query(), which will take care of escaping what has to be :
$sldata = serialize($aa);
$sql = "insert into {test} (pval) values('%s')";
db_query($sql, $sldata);
Here :
As the pval field is a string in database, I used a %s place-holder
And the first value passed to db_query() (after the SQL query itself, of course) will be injected by drupal, to replace that first (and only, here) placeholder.
And, for more informations, you might want to take a look at Database abstraction layer.
instead of just serialize, you could base64_encode to bypass curlies being a problem.
http://php.net/manual/en/function.base64-encode.php
base64_encode(serialize($aa));
Then on the retrieving side of the data
unserialize(base64_decode($db_data));

Using triggers / routines with table prefixes in mysql

I'm used to use mysql table prefixes in my php scripts. Yet triggers and routines sometimes are very useful too. Ok. Let's say i have a table: 'pre_customers'. And a procedure sth like
CREATE FUNCTION `get_all_clients`() RETURNS int(11)
BEGIN
DECLARE sum INT ;
SELECT COUNT(id) INTO sum FROM pre_customers ;
RETURN sum;
END
No big deal, just for example. And there is also a constant
<?php
define( 'DB_PREFIX', 'pre_' ) ;
It's being used for changing table prefixes. If i need to make an sql-request in the script i make it like this
$query = "SELECT * FROM " . DB_PREFIX . "customers" ;
$result = mysql_query( $query ) ;
...
Alright, but if i want to change this prefix in the php-script along with the table names it's gonna ruin all stored routines and triggers, they still will apply to 'pre_customers' table. So the question is is there a common practice how normally programmers solve this problem.
So the question is is there a common practice how normally programmers solve this problem.
Create a dump
Create some kind of template using that dump, with pre_ replaced with %db_prefix%
When you need to change prefix - replace prefix in the template and import it to mysql

"SELECT * FROM users WHERE id IN ( )" == FAIL

I have a function that I use called sqlf(), it emulates prepared statements. For instance I can do things like:
$sql = sqlf("SELECT * FROM Users WHERE name= :1 AND email= :2",'Big "John"','bj#example.com') ;
For various reasons, I cannot use prepared statements, but I would like to emulate them. The problem that I run into is with queries like
$sql = sqlf("SELECT * FROM Users WHERE id IN (:1)",array(1,2,3) );
My code works, but it fails with empty arrays, e.g. the following throws a mysql error:
SELECT * FROM Users WHERE id IN ();
Does anyone have any suggestions? How should I translate and empty array into sql that can be injected into an IN clause? Substituting NULL will not work.
Null is the only value that you can guarantee is not in the set. How come it is not an option? Anything else can be seen as part of the potential set, they are all values.
I would say that passing an empty array as argument for an IN() clause is an error. You have control over the syntax of the query when calling this function, so you should also be responsible for the inputs. I suggest checking for emptiness of the argument before calling the function.
Is there a possibility that you could detect empty arrays withing sqlf and change the SQL to not have the IN clause?
Alteratively, you could postprocess the SQL before passing it to the "real" SQL executor so that "IN ()" sections are removed although you'd have to do all sorts of trickery to see what other elements had to be removed so that:
SELECT * FROM Users WHERE id IN ();
SELECT * FROM Users WHERE a = 7 AND id IN ();
SELECT * FROM Users WHERE id IN () OR a = 9;
would become:
SELECT * FROM Users;
SELECT * FROM Users WHERE a = 7;
SELECT * FROM Users WHERE a = 9;
That could get tricky depending on the complexity of your SQL - you'd basically need a full SQL language interpreter.
If your prepare-like function simply replaces :1 with the equivalent argument, you might try having your query contain something like (':1'), so that if :1 is empty, it resolves to (''), which will not cause a parse error (however it may cause undesirable behavior, if that field can have blank values -- although if it's an int, this isn't a problem). It's not a very clean solution, however, and you're better off detecting whether the array is empty and simply using an alternate version of the query that lacks the "IN (:1)" component. (If that's the only logic in the WHERE clause, then presumably you don't want to select everything, so you would simply not execute the query.)
I would use zero, assuming your "id" column is a pseudokey that is assigned numbers automatically.
As far as I know, automatic key generators in most brands of database begin at 1. This is a convention, not a requirement (auto-numbered fields are not defined in standard SQL). But this convention is common enough that you can probably rely on it.
Since zero probably never appears in your "id" column, you can use this value in the IN() predicate when your input array is empty, and it'll never match.
The only way I can think to do it would be to make your sqlf() function scan to see if a particular substitution comes soon after an "IN (" and then if the passed variable is an empty array, put in something which you know for certain won't be in that column: "m,znmzcb~~1", for example. It's a hack, for sure but it would work.
If you wanted to take it even further, could you change your function so that there are different types of substitutions? It looks like your function scans for a colon followed by a number. Why not add another type, like an # followed by a number, which will be smart to empty arrays (this saves you from having to scan and guess if the variable is supposed to be an array).

Categories