Resource Link Indetifier using PDO? - mysql

(new to PDO and limited experience with DB's in general) :)
I'm switching over to use PDO in a project..
and even though the data was getting dumped to the DB..there was a strange 'Warning'.. that displayed merging of table I was trying to use..(and another I wasnt)... SELECT()..etc even though I was trying to INSERT()..etc..
using the NON-PDO approach worked fine however (which seems a bit odd)..
example:
include("../_include/db_forms.php");
mysql_query("INSERT INTO $tablename (timestamp, fullname, email, formcode, ipaddress, alldata) VALUES ('" . date("YmdHis") . "','" . mysql_real_escape_string($_POST['name']) . "','" . mysql_real_escape_string($_POST['email']) . "','" . $formcode . "','" . $_SERVER['REMOTE_ADDR'] . "','" . mysql_real_escape_string($allData) . "')");
^ no warning/data dumps to DB just fine
after searching/asking around a bit... it looked like another open database connection for the CMS (for other aspects of the page/header/footer include files..etc) is being used for the other queries, (or vice versa).. and this is causing the warning/problem.
I was told adding a resource link identifier would solve this...
now I understand what it is.., and why it is used (multiple DB's.. this is a reference to what one should be used/opened..etc.)
However.. I'm not exactly sure HOW to implement one.. especially using PDO approach.
here is my PDO approach/code:
require_once("../_include/db_pdo_fulllog.php"); //just a .php file with DEFINE()'s for DB connection details
// new DB routine PDO approach //
$sql = "INSERT INTO $tablename (timestamp, fullname, email, formcode, ipaddress, alldata) VALUES (?,?,?,?,?,?)";
try {
$conn = new PDO("mysql:host=" . DBSERVER . ";dbname=" . DBNAME, DBLOGIN, DBPASSWORD);
$stmt = $conn->prepare($sql);
//$stmt = $conn->prepare("INSERT INTO fulllog(timestamp, fullname, email, formcode, ipaddress, alldata) VALUES (?,?,?,?,?,?)");
//timestamp
$ts = date('Y-m-d H:i:s');
$stmt->bindParam(1, $ts);
//name
$stmt->bindParam(2, $_POST['name']);
//email
$stmt->bindParam(3, $_POST['email']);
//formcode
$stmt->bindParam(4, $formcode);
//ip address
$stmt->bindParam(5, $_SERVER['REMOTE_ADDR']);
//all data
$allData = $_POST['session_title'] .' '. $_POST['session_type'] .' '. $_POST['description'] .' '. $_POST['goals'] .' ' . $_POST['faculty'] .' '. $_POST['chapter'];
$stmt->bindParam(6, $allData);
//execute built statement
$stmt->execute();
}
catch(PDOException $e) {
echo $e->getMessage();
die;
}
How can I add a resource link identifier to this...to stop the warning/error of having the multiple databases in use?
Thanks.
edit:
(since its been requested)
the error/warning message:
Warning: mysql_fetch_row() expects parameter 1 to be resource, boolean given in /usr/www/users/xxxxorg/xxxx.org/admincms/_includes/class.now_cms.php on line 126
Err (Table 'xxxx_forms.fulllogContent' doesn't exist) in query: SELECT n.NodeName, n.ContentId, n.ParentId, n.NodeOrder, n.ParentNode, n.ContentTitle, n.ContentType, n.Status, n.ShowOnNav, n.ShowNav, n.ShowOnSitemap, n.ForceSSL, n.TargetWindow, n.ResourceUrl, n.ShortTitle, n.Keywords, n.Description, n.CCount, n.COrderField, n.COrder, COALESCE(t.TemplateBody, dt.TemplateBody) as Template, COALESCE(ct.TemplateBody, dt.TemplateBody) as ChildTemplate, n.UserGroups, n.Comments, n.Sticky, IF(md.MetaId IS NULL, 0, 1) as HasMetaData FROM fulllogContent n LEFT JOIN fulllogTemplate t ON n.Template=t.TemplateId LEFT JOIN fulllogTemplate ct ON n.ChildTemplate=ct.TemplateId LEFT JOIN fulllogTemplate dt ON dt.TemplateId=dt.TemplateId AND dt.Status=2 LEFT JOIN fulllogSection s ON n.SectionId=s.SectionId LEFT JOIN fulllogMetaDefine md ON s.SectionId=md.SectionId WHERE n.Status=1 AND n.PubDate <= NOW() AND (n.ExpDate >= NOW() OR n.ExpDate = '0000-00-00 00:00:00') ORDER BY CONCAT(IF(n.ParentId=0, '', '/'), n.ParentNode), n.NodeOrder
Again.. my table is -not- 'xxxx_forms.fulllogContent' but in fact: 'xxxx_forms.fulllog'
and most of the other stuff doesnt even apply to my table/DB either.. (nor am I doing a SELECT.. I'm doing an INSERT)..
SO it (somehow) still has the CMS database open/in use.. and is getting merged/mixed up with the one I want.
To re-iterate.. the data IS being dumped to the DB... but the page doesnt refresh/resolve and shows this warning/error..

PDO object IS such a "link identifier" itself - so, you don't need anything else.
Therefore, your problem is not connected to PDO.
To get help on a particular error message you have to post this particular message whole and exact, instead of describing it in vague terms.

Solution to fix this seemed to be changing the define() constant names to be unique/different from the constant names the other connection was using. Thanks for the feedback

Related

SQL - Database search gives error when user form input search criteria contains apostrophe

I have a database of mountains the names of which often contain an apostrophe, e.g. Beinn A'Chroin. All my search criteria work fine except when the user inputs a search via the form with the apostrophe included, i.e. Beinn A'Chroin, and then it throws up an error - Beinn A will work, Chroin will work, but never with the apostrophe. As most users will invariably insert the proper name, including the apostrophe I would prefer always to have it in the table data - Your help appreciated - Thanks John
The relative portion of my code is:
$srch = $_POST['srch'];
// Prepare query
// Named Mountain Search Search for:
if ($srch != '') { //Options for specific walk types in a specific area selected
$query = "SELECT walk, status, distance, report, dateofwalk FROM walkslist WHERE walk LIKE '%$srch%' ORDER BY walk ASC;";
update your code use PDO
try {
$conn = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
$sql = 'SELECT walk, status, distance, report, dateofwalk
FROM walkslist WHERE walk LIKE ? ORDER BY walk ASC;';
$q = $conn->prepare($sql);
$q->execute(array('%$srch%'));
$q->setFetchMode(PDO::FETCH_ASSOC);
while ($r = $q->fetch()) {
}
} catch (PDOException $pe) {
die("Could not connect to the database $dbname :" . $pe->getMessage());
}

medoo select term doesn't work correct

I have the debug modus on and get this
mySQL string:
SELECT "dmd_key","id"
FROM "keys"
WHERE "dmd_key" = '140ec37b981042c8549b07d6d4589295'
AND "website" = 'test.de'
But that string doesn't work for me. I get a database error message. (the standard message..)
If I change my string into this:
SELECT `id`,`dmd_key`
FROM `keys`
WHERE `dmd_key` = '140ec37b981042c8549b07d6d4589295'
AND `website` = 'test.de'
I get results.
I think I have to change something in my settings but I don't know what.
It is my first time with medoo and I think medoo doesn't love me...
Thanks for your help.
This appears to be a problem within Medoo. You're not the only person seeing the issue (1,2). According to this bug report, Medoo creates mySQL code that escapes table names with double quotes, and then changes a mySQL option to cater for it:
case 'mysql':
// Make MySQL using standard quoted identifier
$commands[] = 'SET SQL_MODE=ANSI_QUOTES';
This option seems to be disabled on your web host, rendering Medoo unusable in your environment.
It's tough to put in words how insane this is. A framework should generate SQL that is compatible with the database it runs on. I would switch to a different framework.
SET SQL_MODE=ANSI_QUOTES becomes invalid if MySQL restarts and needs to execute it again.
Or you can goto your medoo.php and update functions to use backticks as below:
protected function table_quote($table)
{
return '`' . $this->prefix . $table . '`';
}
protected function column_quote($string)
{
preg_match('/(\(JSON\)\s*|^#)?([a-zA-Z0-9_]*)\.([a-zA-Z0-9_]*)/', $string, $column_match);
if (isset($column_match[ 2 ], $column_match[ 3 ]))
{
return '`' . $this->prefix . $column_match[ 2 ] . '`.`' . $column_match[ 3 ] . '`';
}
return '`' . $string . '`';
}

MySQL injection penetration

I'm new here so I'll try to make my post as clear and readable as possible.
While browsing some site's log I came across some hacking attempts that I want to recreate/test in a closed server. I made a simple PHP web page that gets a variable named 'id' and without any filtering/validation use it in a query.
Relevant PHP code
$var = $_GET['id'];
echo $_GET['id'] . "<br>\n";
include ( "/var/www/dbconnect.php" );
$mysqli = new mysqli ( $db_host, $db_user, $db_password, "news" );
if ( $mysqli->connect_errno ) { echo "Failed to connect to MySQL: (" . $mysqli- >connect_errno . ") " . $mysqli->connect_error; }
$query = "SELECT id, date, subject FROM news_table WHERE id=" . $var;
//$query = "SELECT id, date, subject FROM news_table WHERE id=250; DROP TABLE test;"; // This won't work because in PHP's implementation multiple statements are not allowed
if ( ! $result = $mysqli->query ( $query ) ) { echo "CALL failed: (" . $mysqli->errno . ") " . $mysqli->error; } else { }
Then I load the page using the following
testserver/test-files/test-mysql-vulnerability.php?id=362099999.1
union select unhex(hex(version())) -- 1=1
and get this result:
CALL failed: (1222) The used SELECT statements have a different number
of columns
The hacker spent 5 minutes sending numerous combinations trying to break into our production server. My production server does not give any indication of success/failure like the error above.
My question is: Can the above hack work when the number of columns don't match? If so how?
tnx
As noted in the comments, don't do this.
To answer your question, though, union is useful in injections because it allows you to use an unrelated table in the output. The error you're seeing is because the original database query wanted a certain number and type of columns, and the injected query wanted only one. In this case we know that we need three columns (from the code), so we want the resultant SQL statement to be
SELECT id, date, subject FROM news_table WHERE id=3 union select 0, 0, unhex(hex(version())) --
(This may not work exactly depending on your data types and my ability to do this off the top of my head).
SELECT id,
date,
subject
FROM news_table
WHERE id = 3
UNION
SELECT NULL AS id,
NULL AS date,
Unhex(Hex(Version())) AS subject
also use mysql instead of mysqli as it is more prone to injections

Iterate through MYSQL database and replace data in every record in one column

I have this VERY inefficient way of updating the phone numbers in my database after cleaning them of all non-digits.
$san_phone = mysql_query('SELECT * FROM table');
while ($row = mysql_fetch_array($san_phone)) {
$row['phone_clean'] = preg_replace('#[^\d]#', '', $row['phone']);
echo $row['id'] . ' - ' . $row['phone_clean'] . '<br>';
mysql_query("UPDATE table SET phone = " . $row['phone_clean'] . " WHERE id = " . $row['id']);
}
That update part of the loop is causing me to timeout after only about 400 of my 2,400 records. It's obvious I'm doing something wrong so be gentle when schooling me. ;)
First off, stop using mysql_ functions as they are being deprecated. Use mysqli_ or PDO functions instead.
The method you are using to UPDATE your records is inefficient. You should instead create a temporary table, INSERT the new records in a single query, and finally run an UPDATE query to replace the data.
You can start out with this:
$san_phone = mysql_query('SELECT id, phone FROM table');
$insertArray = array();
while ($row = mysql_fetch_array($san_phone)) {
$phone_clean = preg_replace('#[^\d]#', '', $row['phone']);
echo $row['id'] . ' - ' . $row['phone_clean'] . '<br>';
$insertArray[] = "(" . $row[id] . ", '" . $phone_clean . "')";
}
$insertQuery = "INSERT INTO tempTable (id, phone) VALUES ";
$insertQuery = implode(", ", $insertArray);
mysql_query($insertQuery);
I've made a quick demo to illustrate this process. t1 is your original table, and t2 is the temporary table that contains the data to replace.
See it in action
You could use something like this user-defined function:
http://www.mysqludf.org/lib_mysqludf_preg/index.php#PREG_REPLACE_SECTION
or https://launchpad.net/mysql-udf-regexp
And rewrite your query to:
UPDATE table
SET phone = PREG_REPLACE('#[^\d]#', '', phone);
Well, multiple calls to your database incurs a speed hit, and (in my experience), it's a painful one. Even two trips to the database can lead to a noticeable delay over one.
To get around this, you want to minimize the calls to your database, which means doing as much in one call as possible. To this end, try rewriting this as a single SQL update where your replacement logic is in SQL itself. That would mean only one trip to the database and a massive speed improvement.
That's easier said than done, since last I checked, MySQL didn't have a regular expression string replacement function. You could try a work-around, or see about some of the UDF's. Another answer provided a link for one. I recommend looking into that.

Get Insert Statement for existing row in MySQL

Using MySQL I can run the query:
SHOW CREATE TABLE MyTable;
And it will return the create table statement for the specificed table. This is useful if you have a table already created, and want to create the same table on another database.
Is it possible to get the insert statement for an already existing row, or set of rows? Some tables have many columns, and it would be nice for me to be able to get an insert statement to transfer rows over to another database without having to write out the insert statement, or without exporting the data to CSV and then importing the same data into the other database.
Just to clarify, what I want is something that would work as follows:
SHOW INSERT Select * FROM MyTable WHERE ID = 10;
And have the following returned for me:
INSERT INTO MyTable(ID,Col1,Col2,Col3) VALUES (10,'hello world','some value','2010-10-20');
There doesn't seem to be a way to get the INSERT statements from the MySQL console, but you can get them using mysqldump like Rob suggested. Specify -t to omit table creation.
mysqldump -t -u MyUserName -pMyPassword MyDatabase MyTable --where="ID = 10"
In MySQL Workbench you can export the results of any single-table query as a list of INSERT statements. Just run the query, and then:
click on the floppy disk near Export/Import above the results
give the target file a name
at the bottom of the window, for Format select SQL INSERT statements
click Save
click Export
Since you copied the table with the SQL produced by SHOW CREATE TABLE MyTable, you could just do the following to load the data into the new table.
INSERT INTO dest_db.dest_table SELECT * FROM source_db.source_table;
If you really want the INSERT statements, then the only way that I know of is to use mysqldump http://dev.mysql.com/doc/refman/5.1/en/mysqldump.htm. You can give it options to just dump data for a specific table and even limit rows.
I wrote a php function that will do this. I needed to make an insert statement in case a record needs to be replaced after deletion for a history table:
function makeRecoverySQL($table, $id)
{
// get the record
$selectSQL = "SELECT * FROM `" . $table . "` WHERE `id` = " . $id . ';';
$result = mysql_query($selectSQL, $YourDbHandle);
$row = mysql_fetch_assoc($result);
$insertSQL = "INSERT INTO `" . $table . "` SET ";
foreach ($row as $field => $value) {
$insertSQL .= " `" . $field . "` = '" . $value . "', ";
}
$insertSQL = trim($insertSQL, ", ");
return $insertSQL;
}
Laptop Lift's code works fine, but there were a few things I figured people may like.
Database handler is an argument, not hardcoded. Used the new mysql api. Replaced $id with an optional $where argument for flexibility. Used real_escape_string in case anyone has ever tried to do sql injection and to avoid simple breakages involving quotes. Used the INSERT table (field...) VALUES (value...)... syntax so that the fields are defined only once and then just list off the values of each row (implode is awesome). Because Nigel Johnson pointed it out, I added NULL handling.
I used $array[$key] because I was worried it might somehow change, but unless something is horribly wrong, it shouldn't anyway.
<?php
function show_inserts($mysqli,$table, $where=null) {
$sql="SELECT * FROM `{$table}`".(is_null($where) ? "" : " WHERE ".$where).";";
$result=$mysqli->query($sql);
$fields=array();
foreach ($result->fetch_fields() as $key=>$value) {
$fields[$key]="`{$value->name}`";
}
$values=array();
while ($row=$result->fetch_row()) {
$temp=array();
foreach ($row as $key=>$value) {
$temp[$key]=($value===null ? 'NULL' : "'".$mysqli->real_escape_string($value)."'");
}
$values[]="(".implode(",",$temp).")";
}
$num=$result->num_rows;
return "INSERT `{$table}` (".implode(",",$fields).") VALUES \n".implode(",\n",$values).";";
}
?>
In PHPMyAdmin you can:
click copy on the row you want to know its insert statements SQL:
click Preview SQL:
you will get the created insert statement that generates it
You can apply that on many rows at once if you select them and click copy from the bottom of the table and then Preview SQl
I use the program SQLYOG where I can make a select query, point at the results and choose export as sql. This gives me the insert statements.
The below command will dump into the terminal without all the extra stuff mysqldump will output surrounding the INSERT. This allows copying from the terminal without it writing to a file. This is useful if the environment restricts writing new files.
mysqldump -u MyUserName -pMyPassword MyDatabase MyTable --where="ID = 10" --compact --no-create-info --complete-insert --quick
Using mysqldump --help I found the following options.
-q, --quick Don't buffer query, dump directly to stdout.
(Defaults to on; use --skip-quick to disable.)
-t, --no-create-info
Don't write table creation info.
-c, --complete-insert
Use complete insert statements.
--compact Give less verbose output (useful for debugging). Disables
structure comments and header/footer constructs. Enables
options --skip-add-drop-table --skip-add-locks
--skip-comments --skip-disable-keys --skip-set-charset.
If you want get "insert statement" for your table you can try the following code.
SELECT
CONCAT(
GROUP_CONCAT(
CONCAT(
'INSERT INTO `your_table` (`field_1`, `field_2`, `...`, `field_n`) VALUES ("',
`field_1`,
'", "',
`field_2`,
'", "',
`...`,
'", "',
`field_n`,
'")'
) SEPARATOR ';\n'
), ';'
) as `QUERY`
FROM `your_table`;
As a result, you will have insers statement:
INSERT INTO your_table (field_1, field_2, ..., field_n) VALUES (value_11, value_12, ... , value_1n);
INSERT INTO your_table (field_1, field_2, ..., field_n) VALUES (value_21, value_22, ... , value_2n);
/...................................................../
INSERT INTO your_table (field_1, field_2, ..., field_n) VALUES (value_m1, value_m2, ... , value_mn);
, where m - number of records in your_table
Within MySQL work bench perform the following:
Click Server > Data Export
In the Object Selection Tab select the desired schema.
Next, select the desired tables using the list box to the right of the schema.
Select a file location to export the script.
Click Finish.
Navigate to the newly created file and copy the insert statements.
you can use Sequel pro to do this, there is an option to 'get as insert statement' for the results obtained
There is a quite easy and useful solution for creating an INSERT Statement for editing without the need to export SQL with just Copy & Paste (Clipboard):
Select the row in a query result window of MySQL Workbench, probably even several rows. I use this even if the row does contain different data than I want to insert in my script or when the goal is to create a prepared statement with ? placeholders.
Paste the copied row which is in your clipboard now into the same table (query results list is an editor in workbench) into the last free row.
Press "Apply" and a windows opens showing you the INSERT statement - DO NOT EXECUTE
Copy the SQL from the window to your clipboard
CANCEL the execution, thus not changing the database but keeping the SQL in your clipboard.
Paste the SQL wherever you want and edit it as you like, like e.g. inserting ? placeholders
You can create a SP with the code below - it supports NULLS as well.
select 'my_table_name' into #tableName;
/*find column names*/
select GROUP_CONCAT(column_name SEPARATOR ', ') from information_schema.COLUMNS
where table_schema =DATABASE()
and table_name = #tableName
group by table_name
into #columns
;
/*wrap with IFNULL*/
select replace(#columns,',',',IFNULL(') into #selectColumns;
select replace(#selectColumns,',IFNULL(',',\'~NULL~\'),IFNULL(') into #selectColumns;
select concat('IFNULL(',#selectColumns,',\'~NULL~\')') into #selectColumns;
/*RETRIEVE COLUMN DATA FIELDS BY PK*/
SELECT
CONCAT(
'SELECT CONCAT_WS(','''\'\',\'\''',' ,
#selectColumns,
') AS all_columns FROM ',#tableName, ' where id = 5 into #values;'
)
INTO #sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
/*Create Insert Statement*/
select CONCAT('insert into ',#tableName,' (' , #columns ,') values (\'',#values,'\')') into #prepared;
/*UNWRAP NULLS*/
select replace(#prepared,'\'~NULL~\'','NULL') as statement;
For HeidiSQL users:
If you use HeidiSQL, you can select the row(s) you wish to get insert statement. Then right click > Export grid rows > select "Copy to clipboard" for "Output target", "Selection" for "Row Selection" so you don't export other rows, "SQL INSERTs" for "Output format" > Click OK.
The insert statement will be inside you clipboard.
In case you use phpMyAdmin (Tested on version 5.x):
Click on "Edit" button next to the row for which you would like to have an insert statement, then on the bottom next to the action buttons just select "Show insert query" and press "Go".
With PDO you can do it this way.
$stmt = DB::getDB()->query("SELECT * FROM sometable", array());
$array = $stmt->fetchAll(PDO::FETCH_ASSOC);
$fields = array_keys($array[0]);
$statement = "INSERT INTO user_profiles_copy (".implode(",",$fields).") VALUES ";
$statement_values = null;
foreach ($array as $key => $post) {
if(isset($statement_values)) {
$statement_values .= ", \n";
}
$values = array_values($post);
foreach($values as $index => $value) {
$quoted = str_replace("'","\'",str_replace('"','\"', $value));
$values[$index] = (!isset($value) ? 'NULL' : "'" . $quoted."'") ;
}
$statement_values .= "(".implode(',',$values).")";
}
$statement .= $statement_values . ";";
echo $statement;
I think that the answer provided by Laptop Lifts is best...but since nobody suggested the approach that I use, i figured i should chime in. I use phpMyAdmin to set up and manage my databases most of the time. In it, you can simply put checkmarks next to the rows you want, and at the bottom click "Export" and chose SQL. It will give you INSERT statements for whichever records you selected. Hope this helps.
You can try this
function get_insert_query($pdo, $table, $where_sth)
{
$sql = "";
$row_data = $pdo->query("SELECT * FROM `{$table}` WHERE $where_sth")->fetch();
if($row_data){
$sql = "INSERT INTO `$table` (";
foreach($row_data as $col_name => $value){
$sql .= "`".$col_name."`, ";
}
$sql = rtrim($sql, ", ");
$sql .= ") VALUES (";
foreach($row_data as $col_name => $value){
if (is_string($value)){
$value = $pdo->quote($value);
} else if ($value === null){
$value = 'NULL';
}
$sql .= $value .", ";
}
$sql = rtrim($sql, ", ");
$sql .= ");";
}
return $sql;
}
To use it, just call:
$pdo = new PDO( "connection string goes here" );
$sql = get_insert_query($pdo, 'texts', "text_id = 959");
echo $sql;
Update for get insert statement for current registers at PhpMyAdmin:
Select the table from you DB to get registers from
"Export" tab at the top menĂº as the image below
Custom export
Move down to "Data creation options"
Once there, select "Insert" function at your preferred syntax
Its very simple. All you have to do is write an Insert statement in a static block and run it as a script as a whole block.
E.g. If you want to get Insert statements from a table (say ENGINEER_DETAILS) for selected rows, then you have to run this block -
spool
set sqlformat insert
select * from ENGINEER_DETAILS where engineer_name like '%John%';
spool off;
The output of this block will be Insert statements.
In MySQL Workbench, right-click the table and select 'Send to SQL Editor'/'Insert Statement'. Clean it up a bit and you're good to go.
Based on your comments, your goal is to migrate database changes from a development environment to a production environment.
The best way to do this is to keep your database changes in your source code and consequently track them in your source control system such as git or svn.
you can get up and running quickly with something like this: https://github.com/davejkiger/mysql-php-migrations
as a very basic custom solution in PHP, you can use a function like this:
function store_once($table, $unique_fields, $other_fields=array()) {
$where = "";
$values = array();
foreach ($unique_fields as $k => $v) {
if (!empty($where)) $where .= " && ";
$where .= "$k=?";
$values[] = $v;
}
$records = query("SELECT * FROM $table WHERE $where", $values);
if (false == $records) {
store($table, array_merge($unique_fields, $other_fields));
}
}
then you can create a migration script which will update any environment to your specifications.