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
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.
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;
}
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.
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.
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!).