Using triggers / routines with table prefixes in mysql - php

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

Related

Undefined table: 7 ERROR: relation "V5TableName" does not exist

I am using an MVC framework (Zend) for my application and I want to find the total size of a table in PostgreSQL (including index). The table name is "V5TableName" - quotes included because table name is case sensitive. I have made sure that there is NO typo involved.
My code to get the table size is shown below:
public function getMyTableSize()
{
$sql = "SELECT pg_size_pretty(pg_total_relation_size( '\"V5TableName\"' ) );";
/* Custom_Db is a custom library in my application which makes the PostgreSQL connection
and queries the database
*/
$tableSize = Custom_Db::query($sql)->fetchColumn();
return $tableSize;
}
When my application calls this function it returns the following error in my logs :
[22-Apr-2020 09:42:37] PID:30849 ERR: SQLSTATE[42P01]: Undefined table: 7 ERROR: relation "V5TableName" does not exist
LINE 1: SELECT pg_size_pretty(pg_total_relation_size( '"V5TableName...
^
query was: SELECT pg_size_pretty(pg_total_relation_size( '"V5TableName"' ) );
If I run this same query in pgAdmin4 it works perfectly fine returning the table size (for instance: 104Mb).
I have tried:
Removing and adding quotes to the table name in the code.
Appending the schema as prefix to the table name (example: 'public."V5TableName"').
NONE of the above seem to work. I am not sure what is going wrong over here.
I also tried to find the total database size in my application (db name: MyDbName - with mixed case spelling) and my query looked something like below:
$sql = "SELECT pg_size_pretty(pg_database_size('MyDbName'))"; // this DID NOT WORK
So I changed it to the one shown below: (it worked)
$sql = "SELECT pg_size_pretty(pg_database_size( current_database() ))"; // this WORKED
I was wondering if there is something similar that could be done to find the table size.
Your query should work. The use of double-quotes seems correct.
SELECT pg_size_pretty(pg_total_relation_size('"V5TableName"'));
First make sure you are connecting to the right database cluster (a.k.a. "server"). It's defined by its data directory, or equally unambiguous by hostname and port number. Read the manual here and here.
Then make sure you are connecting to the right database within that database cluster. A Postgres database cluster consists of 1-n databases. When connecting without specifying the actual database, you end up in the maintenance database named postgres by default. That's the most likely explanation. Check with:
SELECT current_database();
Then check for the right table and schema name:
SELECT * FROM pg_tables
WHERE tablename ~* 'V5TableName'; -- ~* matches case-insensitive
The first riddle should be solved at this point.
Check your DB spelling and possible near-duplicates with:
SELECT datname FROM pg_database;
The call is without double-quotes (like you tried correctly), but requires correct capitalization:
SELECT pg_size_pretty(pg_database_size('MyDbName'));
Note the subtle difference (as documented in the manual):
pg_database_size() takes oid or name. So pass the case-sensitive database name without double-quotes.
pg_total_relation_size() takes regclass. So pass the case-sensitive relation name with double-quotes if you need to preserve capitalization.
pg_database_size() has to differ because there is no dedicated object identifier type for databases (no regdatabase).
The gist of it: avoid double-quoted identifiers in Postgres if at all possible. It makes your life easier.

foreach loop returns nothing

I am trying to pull user data from a Cart66 table I have and put it into a shortcode in wordpress. $account is an integer pulled from session data. The code below returns nothing.
$account =Cart66Session::get(Cart66AccountId);
global $wpdb;
$fname=$wpdb->get_results("SELECT * FROM 'vfp_cart66_accounts' WHERE id = '$account', ARRAY_N");
foreach ($fname AS $row)
{
echo $row;
}
This returns "Array"
return $fname;
Ok firstly, maybe I am the only one who saw this, and it could be the source of your entire problem, but you have a misplaced double quote, at the end of your SQL line, which should live at the end of the actual SQL string, not after the requested return type:
// at the end of this line you have: '$account', ARRAY_N");
// this should be changed to: '$account'", ARRAY_N);
$fname=$wpdb->get_results("SELECT * FROM 'vfp_cart66_accounts' WHERE id = '$account', ARRAY_N");
Even the first person who answered the question did not correct you, so I am assuming he didn't see it either. Secondly, using single quotes (') to escape a table name is invalid. If it is quoted at all, use backticks (`). Single quotes indicate a string, not an database, table, or field, all three of which should only be quoted with backticks (except on utility queries like SHOW). Use this instead:
select * from `vfp_cart66_accounts` where id = '$account'
Thirdly, as your commenters point out, you could be vulnerable to SQL Injection. Make sure to use the tools that WP gives you, and do this, or similar, instead:
$fname = $wpdb->get_results(
$wpdb->prepare(
'select * from `vfp_cart66_accounts` where id = %d',
$account
),
ARRAY_N
);
Lastly, you are requesting an array from the DB, but you are trying to echo it as if it were a scalar value. This explains why printing the value of $row yields "Array". When you convert an array() to a string, by default, you get "Array", since arrays can be complex data that may not be beautifully converted to a string. As a correction of this, you can do one of two things.
First, if you need the entire resulting array that represents the entire row of the table, then you can simply change your echo code to this:
foreach ($fname as $row) {
// print the fname of the row
echo $row['fname'];
// do the other stuff you need to do with $row
...
}
OR, if you simply need the fname field out of that table, for the given id, you could use a different $wpdb function, called $wpdb->get_var(), which gets one specific field from the first entry of the resulting data from the database, coupled with some minor SQL changes:
// use the get_var() function instead
$fname = $wpdb->get_var(
$wpdb->prepare(
// 1) change the 'fields' of your sql to only get the `fname` field
// 2) also add limit 1, to reduce load by only asking for one row
// NOTE: #2 is optional really, because WP does this for you when using get_var,
// but is good practice to only ask for what you need. so do it
'select fname from `vfp_cart66_accounts` where id = %d limit 1',
$account
),
ARRAY_N
);
echo $fname; // print the value of field fname from vfp_cart66_accounts for id $account
Now. I don't have specific knowledge of Cart66. That being said, if the above changes to PHP, WordPress, and SQL syntax do not yield results, then you are probably having one of the following other problems instead:
there is a different PHP error somewhere in the code, causing this to never run
this code is never called, and thus it is never executed
you misspelled the table name, which is causing an SQL error
the table exists, but does not have a field named id
both table and field exist, but there are no entries in the table
some other random thing that is not coming to mind
DEBUG #1
For #1, you could try turning on error_reporting() and display_errors early in the code execution. In a normal, run of the mill PHP script you could add the following two lines somewhere early in the code:
error_reporting(E_ALL);
ini_set('display_errors', 1);
However, you are using WordPress, so you will need to do something like this in your wp-config.php file:
// find the line that looks like this and comment it out
// define('WP_DEBUG', false);
// add these two lines directly below it
define('WP_DEBUG', true);
ini_set('display_errors', 1);
DEBUG #2
Make sure your code is running. Don't be afraid to throw a die() statement directly above it, to make sure it is running. Something like this:
// add a die() before everything
die('I am running. Awesome!');
// revised code
$account = Cart66Session::get(Cart66AccountId);
global $wpdb;
$fname = $wpdb->get_var(
$wpdb->prepare(
'select fname from `vfp_cart66_accounts` where id = %d limit 1',
$account
),
ARRAY_N
);
echo $fname;
DEBUG #3
To debug #3, you need either access to a commandline tool for MySQL or some type of GUI interface like phpMyAdmin, so that you can run a query directly from the database. Here is the query you should run:
show tables like 'vfp_cart66_%';
This is an example of one of the only places in SQL that you should ever quote a table name in single quotes. Running this will yield a list of all the tables that start with vfp_cart66_. If you get no results, then your table name is wrong. If your results do not include vfp_cart66_accounts, then your table name is wrong. If you see vfp_cart66_accounts, you are good to go.
DEBUG #4
This one will need to be run directly from the DB or through something like phpMyAdmin also. You are trying to make sure you have the correct field name. The way you do that is:
show create table `vfp_cart66_accounts`;
Assumedly, the field you are calling id would be the auto_incremented field in the table. Thus you are looking for a line, similar to this one:
`id` bigint(20) NOT NULL AUTO_INCREMENT,
Make sure that the line that has AUTO_INCREMENT on it, begins with:
`id`
If it does not, and the name is something else other than id, then you probably have the wrong field name.
DEBUG #5
Make sure you actually have data to display. From your mysql console or phpMyAdmin, run:
select * from `vfp_cart66_accounts` limit 1;
If you bet any results, then you have data, and you are good.
DEBUG #3 - #5 (alternate methods)
Another option you have is to dump the $wpdb object, directly after you run the query, because it contains the last error you received from MySQL. You can do this like so:
$fname = $wpdb->get_var(
$wpdb->prepare(
'select fname from `vfp_cart66_accounts` where id = %d limit 1',
$account
),
ARRAY_N
);
// dump a readable version of the $wpdb object
echo '<pre>';
print_r($wpdb);
die('</pre>');
Often times, reading the MySQL error message helps narrow down the problem in your SQL syntax.
DEBUG #6
If none of this has helped at all, then you will need to use your experience to trackdown a random bug in either your plugins or theme, what could literally be anything. You may as well not even dig in core WP code because, while it does have a couple minor bugs unrelated to your problem, which are getting repaired as we speak, it is one of the most stable CMS platforms out there. It is used by more of the top 10 million sites on the internet than any other CMS, for a good reason. It works, it is up-to-date, and most of all, it is stable.
I really hope you found this helpful or at least learned something from it. Hopefully others find it useful as well.
$fname=$wpdb->get_results(
"SELECT * FROM `vfp_cart66_accounts` WHERE id = '$account'",
ARRAY_N"
);

MySQL custom global defined variable

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

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));

Cannot simply use PostgreSQL table name ("relation does not exist")

I'm trying to run the following PHP script to do a simple database query:
$db_host = "localhost";
$db_name = "showfinder";
$username = "user";
$password = "password";
$dbconn = pg_connect("host=$db_host dbname=$db_name user=$username password=$password")
or die('Could not connect: ' . pg_last_error());
$query = 'SELECT * FROM sf_bands LIMIT 10';
$result = pg_query($query) or die('Query failed: ' . pg_last_error());
This produces the following error:
Query failed: ERROR: relation "sf_bands" does not exist
In all the examples I can find where someone gets an error stating the relation does not exist, it's because they use uppercase letters in their table name. My table name does not have uppercase letters. Is there a way to query my table without including the database name, i.e. showfinder.sf_bands?
From what I've read, this error means that you're not referencing the table name correctly. One common reason is that the table is defined with a mixed-case spelling, and you're trying to query it with all lower-case.
In other words, the following fails:
CREATE TABLE "SF_Bands" ( ... );
SELECT * FROM sf_bands; -- ERROR!
Use double-quotes to delimit identifiers so you can use the specific mixed-case spelling as the table is defined.
SELECT * FROM "SF_Bands";
Re your comment, you can add a schema to the "search_path" so that when you reference a table name without qualifying its schema, the query will match that table name by checked each schema in order. Just like PATH in the shell or include_path in PHP, etc. You can check your current schema search path:
SHOW search_path
"$user",public
You can change your schema search path:
SET search_path TO showfinder,public;
See also http://www.postgresql.org/docs/8.3/static/ddl-schemas.html
I had problems with this and this is the story (sad but true) :
If your table name is all lower case like : accounts
you can use: select * from AcCounTs and it will work fine
If your table name is all lower case like : accounts
The following will fail:
select * from "AcCounTs"
If your table name is mixed case like : Accounts
The following will fail:
select * from accounts
If your table name is mixed case like : Accounts
The following will work OK:
select * from "Accounts"
I dont like remembering useless stuff like this but you have to ;)
Postgres process query different from other RDMS. Put schema name in double quote before your table name like this, "SCHEMA_NAME"."SF_Bands"
Put the dbname parameter in your connection string. It works for me while everything else failed.
Also when doing the select, specify the your_schema.your_table like this:
select * from my_schema.your_table
If a table name contains underscores or upper case, you need to surround it in double-quotes.
SELECT * from "Table_Name";
I had a similar problem on OSX but tried to play around with double and single quotes. For your case, you could try something like this
$query = 'SELECT * FROM "sf_bands"'; // NOTE: double quotes on "sf_Bands"
This is realy helpfull
SET search_path TO schema,public;
I digged this issues more, and found out about how to set this "search_path" by defoult for a new user in current database.
Open DataBase Properties then open Sheet "Variables"
and simply add this variable for your user with actual value.
So now your user will get this schema_name by defoult and you could use tableName without schemaName.
You must write schema name and table name in qutotation mark. As below:
select * from "schemaName"."tableName";
I had the same issue as above and I am using PostgreSQL 10.5.
I tried everything as above but nothing seems to be working.
Then I closed the pgadmin and opened a session for the PSQL terminal.
Logged into the PSQL and connected to the database and schema respectively :
\c <DATABASE_NAME>;
set search_path to <SCHEMA_NAME>;
Then, restarted the pgadmin console and then I was able to work without issue in the query-tool of the pagadmin.
For me the problem was, that I had used a query to that particular table while Django was initialized. Of course it will then throw an error, because those tables did not exist. In my case, it was a get_or_create method within a admin.py file, that was executed whenever the software ran any kind of operation (in this case the migration). Hope that helps someone.
In addition to Bill Karwin's answer =>
Yes, you should surround the table name with double quotes. However, be aware that most probably php will not allow you to just write simply:
$query = "SELECT * FROM "SF_Bands"";
Instead, you should use single quotes while surrounding the query as sav said.
$query = 'SELECT * FROM "SF_Bands"';
You have to add the schema first e.g.
SELECT * FROM place.user_place;
If you don't want to add that in all queries then try this:
SET search_path TO place;
Now it will works:
SELECT * FROM user_place;
Easiest workaround is Just change the table name and all column names to lowercase and your issue will be resolved.
For example:
Change Table_Name to table_name and
Change ColumnName to columnname
It might be silly for a few, but in my case - once I created the table I could able to query the table on the same session, but if I relogin with new session table does not exits.
Then I used commit just after creating the table and now I could able to find and query the table in the new session as well. Like this:
select * from my_schema.my_tbl;
Hope this would help a few.
Make sure that Table name doesn't contain any trailing whitespaces
Try this: SCHEMA_NAME.TABLE_NAME
I'd suggest checking if you run the migrations or if the table exists in the database.
I tried every good answer ( upvote > 10) but not works.
I met this problem in pgAdmin4.
so my solution is quite simple:
find the target table / scheme.
mouse right click, and click: query-tool
in this new query tool window, you can run your SQL without specifying set search_path to <SCHEMA_NAME>;
you can see the result:

Categories