DBD::MySQL: How do I take the "LongReadLen" to the functioning? - mysql

How to I have to change this script to get the BLOB output truncated?
#!/usr/bin/env perl
use warnings;
use strict;
use utf8;
use 5.10.1;
use DBI;
my $user = 'username';
my $passwd = 'password';
my $db = 'information_schema';
my $dbh = DBI->connect( "DBI:mysql:dbname=$db", $user, $passwd, {
RaiseError => 1,
AutoCommit => 1,
} ) or die DBI->errstr;
$db = 'test_truncate';
$dbh->do( "DROP DATABASE IF EXISTS $db" );
$dbh->do( "CREATE DATABASE $db" );
$dbh = DBI->connect( "DBI:mysql:dbname=$db", $user, $passwd, {
PrintError => 0,
RaiseError => 1,
AutoCommit => 1,
mysql_enable_utf8 => 1,
} ) or die DBI->errstr;
$dbh->{LongReadLen} = 5;
$dbh->{LongTruncOk} = 1;
my $table = 'table_truncate';
$dbh->do( "CREATE TABLE IF NOT EXISTS $table ( Id INT, my_Blob BLOB )" );
my $sth = $dbh->prepare( "INSERT INTO $table ( Id, my_Blob ) VALUES ( ?, ? )" );
my $blob = '123456789' x 20;
$sth->execute( 1, $blob );
$sth = $dbh->prepare( "SELECT * FROM $table" );
$sth->execute();
while ( my $row = $sth->fetchrow_arrayref() ) {
say for #$row;
}
Outputs:
1
123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789

adjust the output like this
while ( my $row = $sth->fetchrow_arrayref() ) {
print substr($_,0,78)."\n" for #$row;
}

Related

DBD::mysql: ChopBlanks

Why are trailing blanks chopped off from the column value even though ChopBlanks is not enabled?
use DBI;
my $value = ' string ';
my $db = 'my_mysql_db';
my $dbh = DBI->connect( "dbi:mysql:db=$db", 'user', '*', { RaiseError => 1, ChopBlanks => 0 } ) or die DBI->errstr;
my $table = 'test_mysql';
$dbh->do( "CREATE TABLE IF NOT EXISTS $table (col_1 CHAR(64))" );
my $sth = $dbh->prepare( "DELETE FROM $table WHERE col_1 = ?" );
$sth->execute( $value );
$sth = $dbh->prepare( "INSERT INTO $table (col_1) VALUES( ? )" );
$sth->execute( $value );
$sth = $dbh->prepare( "SELECT * FROM $table" );
$sth->execute();
$sth->dump_results;
' string'
1 rows
I believe this is a MySQL thing and not a DBI thing. From 11.3.2 The CHAR and VARCHAR Types:
When CHAR values are stored, they are right-padded with spaces to the specified length. When CHAR values are retrieved, trailing spaces are removed unless the PAD_CHAR_TO_FULL_LENGTH SQL mode is enabled.

Import CSV in Mysql does not work

I like to write a csv import / update to my Mysql database, but it isnt working. I get no error messages.
Can anybody help me to find the error or whats wrong with my script please.
// set local variables
$connect = mysql_connect("localhost","root","") or die('Could not connect: ' . mysql_error());
$handle = fopen("imptest.csv", "r");
// connect to mysql and select database or exit
mysql_select_db("shoptest1", $connect);
while($data = fgetcsv($handle, 30000, ';')) //Jede Zeile durchgehen
{
$Product_ID=$data[0];
$field=$data[1];
$query = 'SELECT Product_ID FROM testprod';
if (!$result = mysql_query($query)) {
continue;
} if ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
// entry exists update
$query = "UPDATE ps_product_lang SET custom_field ='$field' WHERE id_product = '$Product_ID'";
mysql_query($query);
if (mysql_affected_rows() <= 0) {
echo "kein update";
// no rows where affected by update query
}
} else {
echo "kein eintrag";
// entry doesn't exist continue or insert...
}
mysql_free_result($result);
}
fclose($handle);
mysql_close($connect);
?>
The queries you are performing:
SELECT Product_ID FROM testprod
UPDATE nl_product_lang SET custom_field = ? WHERE Product_ID = ?
are suitable to detect whether a product exists and then either UPDATE or INSERT. For only an UPDATE, the SELECT doesn't matter, as there won't be an entry WHERE Product_ID NOT IN (SELECT Product_ID FROM testprod) - if you have a foreign key.
Here's an example of how to do this using PDO.
list( $SQLDSN, $SQLUSER, $SQLPASS ) = [ 'mysql:dbname=shoptest1', 'root', '' ];
$db = new PDO( $SQLDSN, $SQLUSER, $SQLPASS, [
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
] );
$ids = $db->query( "SELECT Product_ID from testprod" )->fetchAll( \PDO::FETCH_COLUMN, 0 );
while ( list( $Product_ID, $field ) = fgetcsv(...) )
if ( in_array( $Product_ID, $ids ) )
query( $db, "UPDATE ps_product_lang SET custom_field = ? WHERE Product_ID = ?",
[ $field, $Product_ID ]
);
else
trigger_warning( "unknown product $Product_ID" );
function query( $db, $sql, $args = null ) {
$sth = $db->prepare( $sql );
$sth->execute( $sql );
return $sth;
}

Why does MySQL return "Access denied for user" error?

Here's an error message I am getting:
Warning: mysql_connect() [function.mysql-connect]: Access denied for
user 'user'#'localhost' (using password: YES) in
/var/www/classes/mysql.class.php on line 29 ERR_DB_CONNECT
I am using this database class:
class mysql
{
public $conn = "";
public $debug = 0;
public $queries = NULL;
public function mysql( $dbUser = "user", $dbPass = "pass", $dbName = "database", $dbHost = "localhost" )
{
global $config;
$this->user = $dbUser;
$this->pass = $dbPass;
$this->name = $dbName;
$this->host = $dbHost;
if ( $this->debug == 1 )
{
$this->queries = array( );
$this->comments = array( );
}
$this->last_result = FALSE;
$this->debug = $config['debug'];
return TRUE;
}
public function connect( )
{
if ( !( $this->conn = mysql_connect( $this->host, $this->user, $this->pass ) ) )
{
exit( "ERR_DB_CONNECT" );
}
$this->select_db( $this->name );
return $this->conn;
}
public function select_db( $db )
{
if ( !mysql_select_db( $db, $this->conn ) )
{
exit( "ERR_MYSQL_SELECT_DB" );
}
$this->query( "set names utf8" );
}
public function query( $query, $comment = "" )
{
if ( !$this->conn )
{
$this->conn = $this->connect( );
}
$start = microtime( );
if ( !( $result = mysql_query( $query, $this->conn ) ) )
{
exit( mysql_error( ) );
}
$end = microtime( );
if ( $this->debug == 1 )
{
list( $usec1, $sec1 ) = explode( " ", $start );
list( $usec2, $sec2 ) = explode( " ", $end );
$diff = round( $sec2 - $sec1 + $usec2 - $usec1, 5 );
$this->queries[] = $query;
$this->comments[] = $comment;
$this->queries['time'][] = $diff;
}
$this->last_result = $result;
return $result;
}
Access denied for user 'user'#'localhost
This means that your account on your database does not have the correct permissions to perform any queries. Check your database; I imagine you're using XAMPP or something similar. To have a look at the database, type in 127.0.0.1 into your browser. If you have a password attached to your database, this might be the reason why you can't access.
As a side note
Do not use the PHP my_sql methods. They are deprecated, which means they are old, disused and vulnerable to security attacks, like SQL Injection. You should use PDO Objects or mysqli.

Don't understand the behavior of the DBD::MySQL attribute "mysql_bind_type_guessing"

Why do I get this error when mysql_bind_type_guessing is enabled and why only with the argument "e" and not with the argument "a"?
#!/usr/bin/env perl
use warnings;
use strict;
use 5.10.1;
use utf8;
use open qw(:utf8 :std);
use DBI;
my $user = '...';
my $passwd = '...';
my $dbh = DBI->connect( "DBI:mysql:dbname=information_schema", $user, $passwd, {
PrintError => 0,
RaiseError => 1,
} ) or die DBI->errstr;
my $db = 'my_test_db';
$dbh->do( "DROP DATABASE IF EXISTS $db" );
$dbh->do( "CREATE DATABASE $db" );
$dbh = DBI->connect( "DBI:mysql:dbname=$db", $user, $passwd, {
PrintError => 0,
RaiseError => 1,
AutoCommit => 1,
mysql_enable_utf8 => 1,
mysql_bind_type_guessing => 1,
} ) or die DBI->errstr;
my $table = 'Abteilung';
$dbh->do( "CREATE TABLE IF NOT EXISTS $table (
AbtNr INT NOT NULL,
Name VARCHAR(30),
PRIMARY KEY(AbtNr)
)" );
my $sth = $dbh->prepare( "INSERT INTO $table ( AbtNr, Name ) VALUES ( ?, ? )" );
my $abteilung_values = [
[ 1, 'EDV' ],
[ 2, 'Verwaltung' ],
[ 3, 'Chefetage' ],
];
for my $row ( #$abteilung_values ) {
$sth->execute( #$row );
}
$sth = $dbh->prepare( "SELECT * FROM $table WHERE name REGEXP ?" );
$sth->execute( 'e' );
while ( my $row = $sth->fetchrow_arrayref() ) {
say "#$row";
}
mysql_bind_type_guessing enabled
execute argument 'e':
# DBD::mysql::st execute failed: Unknown column 'e' in 'where clause' at ./perl3.pl line 46.
execute argument 'a':
# 2 Verwaltung
# 3 Chefetage
mysql_bind_type_guessing disabled
execute argument 'e':
# 1 EDV
# 2 Verwaltung
# 3 Chefetage
execute argument 'a':
# 2 Verwaltung
# 3 Chefetage
The parameter mysql_bind_type_guessing tries to guess whether your input data is numeric or not. If the data is numeric, it won't quote it.
It appears that an inadequate check for numeric-ness is leading it to think that e is a number (e can be a part of numbers, such as 1.2e-3).
Because it is not quoted, MySql tries to treat e as a column name instead, hence the error.
If my guess is correct, I would consider it a bug in DBD::MySQL.
Update: Indeed, this is a bug.

Simulate a DELETE CASCADE in MySQL?

Is it possible to predict the operations that follow a DELETE CASCADE automatically? In my software I would like to give the user a warning with details about the data that would be deleted then.
You can make a copy of the database and put triggers on the after delete
DELIMITER $$
CREATE TRIGGER ad_table1_each AFTER DELETE ON table1 FOR EACH ROW
BEGIN
INSERT INTO log VALUES (null /*autoinc id*/
, 'table1' /*tablename*/
, old.id /*tableid*/
, concat_ws(',',old.field1,old.field2 /*CSV's of fields*/
, NOW() /*timestamp*/
, 'delete'); /*what action*/
REPLACE INTO restore_table1 VALUES (old.id,
, old.field1
, old.field2
, ... );
END $$
DELIMITER ;
The log table is just a table with the following fields:
id integer autoincrement primary key
tablename varchar(45)
table_id integer
fields varchar(6000)
delete_time timestamp
action enum('insert','update','delete')
If you do a SELECT #last_id:= max(id) FROM log before the delete cascade on the copy.
Then you can do a SELECT * FROM log WHERE id > #last_id
and get all the rows that will be deleted in the cascade.
After that you can use the restore_table1 to recreate the rows that were deleted in the cascade in the copy database.
I think you could use Johan's trigger solution in combination with a transaction that you roll back. This avoids both the need for a second database and for the manual restore of the deleted entries.
add the trigger and the log table
for each attempted deletion start a transaction and delete the entries
present the information from the log to your user for approval
if the user agrees commit the transaction, otherwise rollback
I wrote a very quick hack that does exactly what you need in PHP, since I wanted to do the exact same thing and haven't found any resources for that online.
It might be too late for you, but it may help others.
function get_referencing_foreign_keys ($database, $table) {
$query = 'SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, REFERENCED_COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_SCHEMA = "'.$database.'" AND REFERENCED_TABLE_NAME = '.esc($table);
$result = rquery($query);
$foreign_keys = array();
while ($row = mysql_fetch_row($result)) {
$foreign_keys[] = array('database' => $row[0], 'table' => $row[1], 'column' => $row[2], 'reference_column' => $row[3]);
}
return $foreign_keys;
}
function get_foreign_key_deleted_data_html ($database, $table, $where) {
$data = get_foreign_key_deleted_data ($database, $table, $where);
$html = '';
foreach ($data as $key => $this_data) {
$html .= "<h2>$key</h2>\n";
$html .= "<table>\n";
$i = 0;
foreach ($this_data as $value) {
if($i == 0) {
$html .= "\t<tr>\n";
foreach ($value as $column => $column_value) {
$html .= "\t\t<th>".htmlentities($column)."</th>\n";
}
$html .= "\t</tr>\n";
}
$html .= "\t<tr>\n";
foreach ($value as $column => $column_value) {
$html .= "\t\t<td>".htmlentities($column_value)."</td>\n";
}
$html .= "\t</tr>\n";
$i++;
}
$html .= "</table>\n";
}
return $html;
}
function get_foreign_key_deleted_data ($database, $table, $where) {
$GLOBALS['get_data_that_would_be_deleted'] = array();
$data = get_data_that_would_be_deleted($database, $table, $where);
$GLOBALS['get_data_that_would_be_deleted'] = array();
return $data;
}
function get_data_that_would_be_deleted ($database, $table, $where, $recursion = 100) {
if($recursion <= 0) {
die("Deep recursion!");
}
if($recursion == 100) {
$GLOBALS['get_data_that_would_be_deleted'] = array();
}
if($table) {
if(is_array($where)) {
$foreign_keys = get_referencing_foreign_keys($database, $table);
$data = array();
$query = 'SELECT * FROM `'.$table.'`';
if(count($where)) {
$query .= ' WHERE 1';
foreach ($where as $name => $value) {
$query .= " AND `$name` = ".esc($value);
}
}
$result = rquery($query);
$to_check = array();
while ($row = mysql_fetch_row($result)) {
$new_row = array();
$i = 0;
foreach ($row as $this_row) {
$field_info = mysql_fetch_field($result, $i);
$new_row[$field_info->name] = $this_row;
foreach ($foreign_keys as $this_foreign_key) {
if($this_foreign_key['reference_column'] == $field_info->name) {
$to_check[] = array('value' => $this_row, 'foreign_key' => array('table' => $this_foreign_key['table'], 'column' => $this_foreign_key['column'], 'database' => $this_foreign_key['database']));
}
}
$i++;
}
$GLOBALS['get_data_that_would_be_deleted'][$table][] = $new_row;
}
foreach ($to_check as $this_to_check) {
if(isset($this_to_check['value']) && !is_null($this_to_check['value'])) {
get_data_that_would_be_deleted($database, $this_to_check['foreign_key']['table'], array($this_to_check['foreign_key']['column'] => $this_to_check['value']), $recursion - 1);;
}
}
$data = $GLOBALS['get_data_that_would_be_deleted'];
return $data;
} else {
die("\$where needs to be an array with column_name => value pairs");
}
} else {
die("\$table was not defined!");
}
}
Imagine I have a table called "table" in the database "db" and I want to delete the one with the id 180, then I'd call:
print(get_foreign_key_deleted_data_html('db', 'table', array('id' => 180)));
and it prints a full table with all the rows and all the values that would be deleted.
But as I've said, this is a very, very quick and dirty hack. I'd be glad for any bug-report (and there surely are a lot of them!).