Why does DBI implicitly change integers to strings? - mysql

I have a MySQL table with following structure.
alid bigint(20),
ndip varchar(20),
ndregion varchar(20),
occ_num int(3),
Delta_Flag int(1)
After selecting data from the table, I am getting all the data quoted and as a string value.
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use FindBin;
use lib $FindBin::Bin;
use Database;
my $pwd = $FindBin::Bin;
my $db = Database->new( 'mysql', "$pwd/config.ini" );
my $db1 = Database->new( 'mysql', "$pwd/config2.ini" );
my #tables = qw( AutoTT_AlarmStatus_Major1 );
for my $table ( #tables ) {
my $query_select = "SELECT alid, ndip, ndregion, occ_num, Delta_Flag FROM $table LIMIT 1";
my $result = $db->db_get_results( $query_select );
print Dumper( $result );
for my $item ( #{$result} ) {
# Here I want to prepare, bind and insert this data
# into other table with same structure
}
}
Database.pm
sub db_get_results {
my $self = shift;
my $qry = shift;
my $sth = $self->{dbh}->prepare( $qry );
$sth->execute();
my #return = ();
while ( my #line = $sth->fetchrow_array ) {
push #return, \#line;
}
return \#return;
}
Output:
$VAR1 = [
[
'1788353',
'10.34.38.12',
'North Central',
'1',
'1'
]
];
Why is DBI implicitly converting all integers to strings?

As #choroba notes in his answer, it's not the DBI that's doing anything with the data. It's just passing through what the driver module (DBD::mysql in your case) returned.
In the General Interface Rules & Caveats section of the DBI docs it says:
Most data is returned to the Perl script as strings. (Null values are returned as undef.) This allows arbitrary precision numeric data to be handled without loss of accuracy. Beware that Perl may not preserve the same accuracy when the string is used as a number.
I wrote that back in the days before it was common to configure perl to support 64-bit integers, and long-double floating point types were unusual. These days I recommend that drivers return values in the most 'natural' Perl type that doesn't risk data loss.
For some drivers that can be tricky to implement, especially those that support returning multiple result sets, with different numbers of columns, from a single handle, as DBD::mysql does.
I skimmed the DBD::mysql docs but didn't see any mention of this topic, so I looked at the relevant code where I can see that the current DBD::mysql is returning numbers as numbers. There's also lots of references to recent changes in this area in the Change log.
Perhaps you're using an old version of DBD::mysql and should upgrade.

That's how the DBD driver for MySQL works. Other databases might behave differently. For example, in SQLite, numbers remain numeric:
#!/usr/bin/perl
use warnings;
use strict;
use DBI;
use Data::Dumper;
my $dbh = 'DBI'->connect('dbi:SQLite:dbname=:memory:', q(), q());
$dbh->do('CREATE TABLE t (id INT, val VARCHAR(10))');
my $insert = $dbh->prepare('INSERT INTO t VALUES (?, ?)');
$insert->execute(#$_) for [ 1, 'foo' ], [ 2, 'bar' ];
my $query = $dbh->prepare('SELECT id, val FROM t');
$query->execute;
while (my $row = $query->fetchrow_arrayref) {
print Dumper($row);
}
__END__
$VAR1 = [
1,
'foo'
];
$VAR1 = [
2,
'bar'
];

This is nothing DBI does in general. As it already was pointed out many database-drivers (DBD::xy) of the DBI system convert numbers to strings. AFAIK its not possible to avoid that.
What you can do is ask the statement handle for corresponding native type or (easier in your case) wether the column of your resultset is numeric in the mysql-DB or not. Here is an example
Given this basic database:
mysql> create table test (id INT,text VARCHAR(20));
Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO test VALUES (1,'lalala');
Query OK, 1 row affected (0.00 sec)
you can lookup wether the column is numeric or not by using the driver-specific field 'mysql_is_num':
Reference to an array of boolean values; TRUE indicates, that the respective column contains numeric values.
(from DBD::mysql)
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use DBI;
use DBD::mysql;
use Data::Dumper;
my $dsn = "DBI:mysql:database=test;host=localhost";
my $dbh = DBI->connect($dsn,'user','pass') or die "$!";
my $sql = "SELECT * FROM test WHERE id = ?";
my $sth = $dbh->prepare($sql);
$sth->execute(1);
my $num_fields = $sth->{'NUM_OF_FIELDS'};
my $num_mask = $sth->{'mysql_is_num'};
my $result;
my $cnt = 0;
while (my $line = $sth->fetchrow_arrayref){
for (my $i = 0; $i < $num_fields; $i++){
if ($num_mask->[$i]){
$line->[$i] + 0;
}
$result->[$cnt] = $line;
}
$cnt++;
}
print Dumper($result);
I hope this helps. As it was written in a hurry, please excuse the style. Of course i'm open to any suggestions.

Related

UTF-8 decoding when reading a MySQL column of type JSON (vs. TEXT), using Perl DBI + DBD::mysql

Here is the problem, in a working unit test. I think it's either a bug in DBI + DBD::mysql, with respect to how it handles MySQL JSON columns, or a bug in my brain.
use strict;
use warnings;
use utf8;
use Test2::V0;
use Test2::Plugin::UTF8;
use Test2::Plugin::NoWarnings echo => 1;
use DBI;
use DBD::mysql 4.041; # 4.041+ required for utf8mb4
use JSON 4.01 qw//;
use Encode;
#
# setup $dbh, create test table
#
my $dbname = '';
my $host = 'localhost';
my $user = '';
my $pass = '';
my $dbh = DBI->connect(
"DBI:mysql:" . ($dbname || '') . ";host=" . $host,
$user,
$pass || undef,
{ RaiseError => 1, PrintError => 0, AutoCommit=> 1 }
);
$dbh->{'mysql_enable_utf8mb4'} = 1;
$dbh->{'charset'} = 'utf8';
$dbh->do(
"CREATE TABLE IF NOT EXISTS `test` ("
. "id int unsigned, "
. "`my_json` json NOT NULL, "
. "`my_text` mediumtext NOT NULL "
. ") ENGINE=InnoDB "
. "DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
);
#
# create and insert test data
#
# A use case for spam! Got this junk from spam inbox
my $utf8str = "ion?• été e😍💋🔥ulière vs• Ch• 😊⭐👉🏻🔞🍆Sho是ab 期待您x";
my $hash = { test => $utf8str };
my $json = JSON->new->encode( $hash );
my $id = time;
$dbh->do("INSERT INTO test SET id=?, my_json=?, my_text=?", undef, $id, $json, $json);
#
# retrieve test data and check it
#
my ( $my_json, $my_text ) = $dbh->selectrow_array("SELECT my_json, my_text FROM test WHERE id=$id");
is( $my_text, $json ); # ok
is( $my_json, $json ); # fails. got {"test": "ion?\nâ\N{U+80}¢ ét ....
is( decode('UTF-8', $my_json), $json ); # ok'ish. mysql adds a space between "test":"..." but value looks ok
#
# another test, independent of JSON encoder, using hand-built json
#
$id++;
$json = '{"test":"' . $utf8str . '"}';
$dbh->do("INSERT INTO test SET id=?, my_json=?, my_text=?", undef, $id, $json, $json);
( $my_json, $my_text ) = $dbh->selectrow_array("SELECT my_json, my_text FROM test WHERE id=$id");
is( $my_text, $json ); # ok
is( $my_json, $json ); # fails. got {"test": "ion?\nâ\N{U+80}¢ ét ....
printf "%vX", $my_json; # 7B.22.74.65.73.74.22.3A.20.22.69.6F.6E.3F.E2.80.A2.20.C3.A9.74.C3.A9.20.65.F0.9F.98.8D.F0.9F.92.8B.F0.9F.94.A5.75.6C.69.C3.A8.72.65.20.76.73.E2.80.A2.20.43.68.E2.80.A2.20.F0.9F.98.8A.E2.AD.90.F0.9F.91.89.F0.9F.8F.BB.F0.9F.94.9E.F0.9F.8D.86.53.68.6F.E6.98.AF.61.62.20.E6.9C.9F.E5.BE.85.E6.82.A8.78.22.7D
printf "%vX", $json; # 7B.22.74.65.73.74.22.3A.22.69.6F.6E.3F.2022.20.E9.74.E9.20.65.1F60D.1F48B.1F525.75.6C.69.E8.72.65.20.76.73.2022.20.43.68.2022.20.1F60A.2B50.1F449.1F3FB.1F51E.1F346.53.68.6F.662F.61.62.20.671F.5F85.60A8.78.22.7D
is( decode('UTF-8', $my_json), $json ); # ok'ish. mysql adds a space between "test":"..." but value looks ok
#
# cleanup
#
$dbh->do("DROP TABLE `test`");
$dbh->disconnect();
done_testing();
My understanding is that the JSON standard requires UTF-8. In addition, MySQL also requires/uses UTF-8 with regard to JSON columns, as described here:
MySQL handles strings used in JSON context using the utf8mb4 character
set and utf8mb4_bin collation. Strings in other character sets are
converted to utf8mb4 as necessary.
(https://dev.mysql.com/doc/refman/8.0/en/json.html)
My understanding is also that DBI handles the UTF-8 encoding/decoding, and should be returning decoded UTF-8 as it is doing for the mediumtext column, as stated here:
This attribute determines whether DBD::mysql should assume strings stored
in the database are utf8. This feature defaults to off.
When set, a data retrieved from a textual column type (char, varchar,
etc) will have the UTF-8 flag turned on if necessary. This enables
character semantics on that string.
https://metacpan.org/pod/DBD::mysql#mysql_enable_utf8
However, it appears not to be for JSON columns. Explicit decoding appears to be required after retrieving data from a JSON column.
So which is it... bug in DBI/DBD::mysql, or bug in my brain?
EDIT: Good news, it's not my brain. Bad news, appears to be a known bug. https://github.com/perl5-dbi/DBD-mysql/issues/309
So, the answer I'm seeking now is a backward-compatible workaround, i.e., a workaround that won't break if/when DBD::mysql is fixed. Double-decoding would not be good.
So, the answer I'm seeking now is a backward-compatible workaround,
i.e., a workaround that won't break if/when DBD::mysql is fixed.
Double-decoding would not be good.
You could try to determine if the JSON decode bug is present by creating a test table where you insert a non-ascii character that has a known UTF-8 encoding with byte length greater than one. For example:
$dbh->do("DROP TABLE IF EXISTS json_decode_test");
$dbh->do("CREATE TABLE json_decode_test (id int unsigned, `my_json` json NOT NULL)");
my $unicode_str = "是"; # This character will always have a UTF-8 encoding with
# byte length > 1
my $hash = { test_str => $unicode_str };
my $json = JSON->new;
my $json_str = $json->encode( $hash );
my $id = time;
my $attrs = undef;
$dbh->do("INSERT INTO json_decode_test SET id=?, my_json=?", $attrs, $id, $json_str);
my ( $json_str2 ) = $dbh->selectrow_array(
"SELECT my_json FROM json_decode_test WHERE id=$id");
my $hash2 = $json->decode( $json_str2 );
my $unicode_str2 = $hash2->{test_str};
# If the json unicode bug is present, $unicode_str2 will not be decoded. Instead
# it will be a string of length 3 representing the UTF-8 encoding of $unicode_str
# (printf "%vX\n", $unicode_str2) gives output : E6.98.AF
my $json_unicode_bug = (length $unicode_str2) > 1;
if ( $json_unicode_bug ) {
say "unicode bug is present..";
# need to run decode_utf8() on every returned json object from DBI
}
Change SQL request to create test table as following
my $query = "
CREATE TABLE IF NOT EXISTS `test` (
`id` INT UNSIGNED,
`my_json` JSON NOT NULL,
`my_text` MEDIUMTEXT NOT NULL
) ENGINE=InnoDB DEFAULT
CHARSET=utf8mb4
COLLATE=utf8mb4_unicode_ci
";
$dbh->do($query);
F0.9F.98.8D -- UTF-8 encoding -- this is good
1F60D -- Unicode codepoint -- not useful in `MEDIUMTEXT`
Do
SELECT HEX(my_json) FROM test WHERE id = ...
to see what is inside MySQL; it should be F09F988D.
You should not be encoding or decoding any string that is written to or read from MySQL.

perl script to connect two databases at same time

I am trying to connect to 2 databases on the same instance of MySQL from 1 Perl script.
I am using this in a migration script where I am grabbing data from the original database and inserting it into the new one.
Connecting to 1 database and then trying to initiate a second connection with the same user just changes the current database to the new one.
#!/usr/bin/perl
use DBI;
use strict;
my $driver = "mysql";
my $database1 = "db1";
my $dsn1 = "DBI:$driver:database=$database1";
my $userid = "userhead";
my $password = "pwdhead";
my $database2 = "db2";
my $dsn2 = "DBI:$driver:database=$database2";
my $dbh1 = DBI->connect($dsn1, $userid, $password ) or die $DBI::errstr;
my $dbh2 = DBI->connect($dsn2, $userid, $password ) or die $DBI::errstr;
my $sth = $dbh2->prepare("INSERT INTO Persons") $dbh1->prepare("SELECT *FROM Persons");
$sth->execute() or die $DBI::errstr;
print "Number of rows found :" + $sth->rows;
In the above example i am trying to copy from one database table to another datbase table. but i am getting error while running the script. Please help me out
At a guess, you're trying to use the same database handle to connect to both databases. If you need to operate two separate connections then you need two separate handles
This program uses the data_sources class method to discover all of the available MySQL databases and creates a connection to each of them, putting the handles in the array #dbh. You can use each element of that array as normal, for instance
my $stmt = $dbh[0]->prepare('SELECT * FROM table)
It may be that you prefer to set up the #databases array manually, or the username and password may be different for the two data sources, so some variation on this may be necessary
use strict;
use warnings 'all';
use DBI;
my $user = 'username';
my $pass = 'password';
my #databases = DBI->data_sources('mysql');
my #dbh = map { DBI->connect($_, $user, $pass) } #databases;
Update
You need to select data from the source table, fetch it one row at a time, and insert each row into the destination table
Here's an idea how that might work, but you need to adjust the number of question marks in the VALUES of the INSERT statement to match the number of columns
Note that, if you're just intending to copy the whole dataset, there aree better ways to go about this. In particular, if you have any foreign key constraints then you won't be able to add data until the table it it is dependent on is populated
#!/usr/bin/perl
use strict;
use warnings 'all';
use DBI;
my $userid = "userhead";
my $password = "pwdhead";
my ($dbase1, $dbase2) = qw/ db1 db2 /;
my $dsn1 = "DBI:mysql:database=$dbase1";
my $dsn2 = "DBI:mysql:database=$dbase2";
my $dbh1 = DBI->connect($dsn1, $userid, $password ) or die $DBI::errstr;
my $dbh2 = DBI->connect($dsn2, $userid, $password ) or die $DBI::errstr;
my $select = $dbh1->prepare("SELECT * FROM Persons");
my $insert = $dbh2->prepare("INSERT INTO Persons VALUES (?, ?, ?, ?, ?)");
$select->execute;
while ( my #row = $select->fetchrow_array ) {
$insert->execute(#row);
}
If you need to handle the columns from the source data separately then you can use named scalars instead of the array #row. Like this
while ( my ($id, $name) = $select->fetchrow_array ) {
my $lastname = '';
$insert->execute($id, $name, $lastname);
}
Plan A (especially if one-time task):
Run mysqldump on the source machine; feed the output to mysql on the target machine. This will be much faster and simpler. If you are on a Unix machine, do it with an exec() from Perl (if you like).
Plan B (especially if repeated task):
If the table is not "too big", do one SELECT to fetch all the rows into an array in Perl. Then INSERT the rows into the target machine. This can be sped up (with some effort) if you build a multi-row INSERT or create a CSV file and use LOAD DATA instead of INSERT.

Perl module to log in database and do a simple query?

I can't seem to figure out the issue with my .pm file and script. I am fairly new to Perl.
I have a database with name "project" and there is table with name "mailing".
mailing table has 7 entries, which I want to display using module.
So, I have this custom module to log in to database and do a query. This module is names as DB.pm
DB.pm is stored on my FEDORA 20 at /root/mysql/GUI/DB.pm.
DB.pm is defined as follows:
package GUI::DB;
use strict;
use DBI;
use vars qw(#ISA #EXPORT);
use Exporter;
#ISA = qw(Exporter);
#EXPORT = qw(dbConnect query);
#
# dbConnect - connect to the database, get the database handle
#
sub dbConnect {
# Read database settings from config file:
print "Works";
my $dsn = "DBI:mysql:project";
my $dbh = DBI->connect( $dsn,
'root',
'mydatabasepassword',
{ RaiseError => 1 }
);
return $dbh;
}
#
# query - execute a query with parameters
# query($dbh, $sql, #bindValues)
#
sub query {
my $dbh = shift;
my $sql ="SELECT * FROM mailing";
my #bindValues = #_; # 0 or serveral parameters
my #returnData = ();
# issue query
my $sth = $dbh->prepare($sql); //**line number 39 that is giving** error
if ( #bindValues ) {
$sth->execute(#bindValues);
} else {
$sth->execute();
}
if ( $sql =~ m/^select/i ) {
while ( my $row = $sth->fetchrow_hashref ) {
push #returnData, $row;
}
}
# finish the sql statement
$sth->finish();
return #returnData;
}
1;
Now I want to use this module inside my per script. This is what I tried:
#!/usr/bin/perl
use warnings;
use strict;
use lib '/root/mysql/';
use GUI::DB qw(dbConnect query);
dbConnect();
query();
This is the error I'm getting -->
Can't call method "prepare" on an undefined value at /root/mysql/GUI/DB.pm line 39.
Please help me with this. I am not sure how to proceed. I am guessing it has something to do with argument passing. Nothing is wrong with database. It works fine from CLI.
Thanks :)
_x_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X__X_X_X_X_X__X
TILL HERE IT IS RESOLVED
_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X_X__X_X_X
FURTHER ISSUE is SQL command is not working.
In the mailing table of my database I have email id with different domains.
For example, some id's are xyz#gmail.com, 12343#gmail.com , bae#yahoo.com as so on and I am assuming new email ids will be added to mailing tables every day with different domains.
I am trying to write a scripts that updates another table which holds a daily count of email addresses by their domain name. This is what I tried:
#!/usr/bin/perl
use warnings;
use strict;
use lib '/root/mysql/';
use 5.016;
use Data::Dumper;
use GUI::DB qw(dbConnect query);
my $data = dbConnect();
my #domain = query($data, "SELECT substr(addr,locate('\#',addr)+1) as maildomain, count (*) as mailcount FROM mailing GROUP BY maildomain ORDER BY mailcount DESC");
for my $key (#domain){
say Dumper ($key);
}
But I am getting an error,
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '*) as mailcount FROM mailing GROUP BY maildomain ORDER BY mailcount DESC' at line 1 at /root/mysql/GUI/DB.pm line 44.
Same SQL statement works from CLI with no issues.
Any help would be appreciated. :)
1) Your error is saying that $dbh is undefined here:
sub query {
my $dbh = shift;
...
# issue query
my $sth = $dbh->prepare($sql); #<***LOOK HERE***
...which means $dbh must be undefined here:
sub query {
my $dbh = shift; #<***LOOK HERE***
...
# issue query
my $sth = $dbh->prepare($sql);
2) Let's see why. Your dbConnect() method returns $dbh:
sub dbConnect {
# Read database settings from config file:
print "Works";
my $dsn = "DBI:mysql:project";
my $dbh = DBI->connect(
$dsn,
'root',
'mydatabasepassword',
{ RaiseError => 1 }
);
return $dbh; #<***LOOK HERE*****
}
3) But, you call dbConnect() like this:
dbConnect();
Because you never save the return value anywhere, $dbh is discarded.
4) Furthermore, you call query() like this:
query();
Yet, you defined query() like this:
sub query {
my $dbh = shift;
The query() sub believes that the first argument will be the database handle--but you didn't call query() with any arguments.
You need to do this:
my $data_base_handle = dbConnect();
my #results = query($data_base_handle);
#do something with #results
Response to comment:
I printed #results, this is what I see HASH(0x1d05be8)
HASH(0x1d05ba0) HASH(0x1d05b58) HASH(0x1d05b10) HASH(0x1d05ac8)
HASH(0x1d05a80) HASH(0x1d05a38)
You wrote:
my $row = $sth->fetchrow_hashref;
...which asks DBI to return each row as a reference to a hash. Then you wrote:
push #returnData, $row;
...which pushed each hash reference into an array. So query() returns an array of hash references. The notation HASH(0x1d05be8) is what perl outputs when you print a hash reference.
If you want to see what's in those hashes, then do this:
use 5.016; #enable say()
use Data::Dumper;
...
...
for my $href (#results) {
say Dumper($href);
}
To access the data in a hash reference, you can do this:
use strict;
use warnings;
use 5.016;
use Data::Dumper;
my $href = {
c => 3,
a => 1,
b => 2,
};
my %hash = %{$href}; #dereference, {}, the reference into a hash, %
for my $key ( keys %hash ) {
say "$key $hash{$key}";
}
--output:--
c 3
a 1
b 2
Response to next comment:
(Answer posted in comments under op.)
By the way, perl is pretty good at text processing, so if you couldn't figure out the problem with your query, you could process the email addresses with perl:
use strict;
use warnings;
use 5.012;
use Data::Dumper;
use DBI;
use DBD::mysql;
# CONFIG VARIABLES
my $db_type = "mysql";
my $database = "my_db";
my $host = "localhost";
my $port = "3306";
my $user = "root";
my $pword = "";
# DATA SOURCE NAME
my $dsn = "dbi:$db_type:$database:$host:$port";
# PERL DBI CONNECT
my $dbh = DBI->connect($dsn, $user, $pword);
# PREPARE THE QUERY
my $tablename = "mailing";
my $select =<<"END_OF_SELECT";
select addr from $tablename
END_OF_SELECT
my $addr_aref = $dbh->selectcol_arrayref($select); #Returns a reference to a flat array containing all the email addresses
$dbh->disconnect;
my %count_for;
for my $addr (#{$addr_aref}) {
$addr =~ s/.*#//;
$count_for{$addr}++;
}
say Dumper(\%count_for);
--output:--
$VAR1 = {
'google.com' => 2,
'gorilla.com' => 1,
'yahoo.com' => 3
};

Perl Import large .csv to MySQL, don't repeat data

I am trying to import several .csv files into a mysql database, the script below works except that it only imports the first row of my csv data into the database. Both my tables are populated with exactly one data entry.
Any help would be appreciated.
Thank you
#!/usr/bin/perl
use DBI;
use DBD::mysql;
use strict;
use warnings;
# MySQL CONFIG VARIABLES
my $host = "localhost";
my $user = "someuser";
my $pw = "somepassword";
my $database = "test";
my $dsn = "DBI:mysql:database=" . $database . ";host=" . $host;
my $dbh = DBI->connect($dsn, $user, $pw)
or die "Can't connect to the DB: $DBI::errstr\n";
print "Connected to DB!\n";
# enter the file name that you want import
my $filename = "/home/jonathan/dep/csv/linux_datetime_test_4.26.13_.csv";
open FILE, "<", $filename or die $!;
$_ = <FILE>;
$_ = <FILE>;
while (<FILE>) {
my #f = split(/,/,$_);
if (length($f[4]) < 10) {
print "No Weight\n";
}
else {
#insert the data into the db
print "insert into datetime_stamp\n";
}
my $sql = "INSERT INTO datetime_stamp (subject, date, time, weight)
VALUES('$f[1]', '$f[2]', '$f[3]', '$f[4]')";
print "$sql\n";
my $query = $dbh->do($sql);
my $sql = "INSERT INTO subj_weight (subject, weight) VALUES('$f[1]', '$f[2]')";
my $query = $dbh->do($sql);
close(FILE);
}
As has been commented, you close the input file after reading the first data entry, and so only populate your database with a single record.
However there are a few problems with your code you may want to consider:
You should set autoflush on the STDOUT file handle if you are printing diagnostics as the program runs. Otherwise perl won't print the output until either it has a buffer full of text to print or the file handle is closed when the program exits. That means you may not see the messages you have coded until long after the event
You should use Text::CSV to parse CSV data instead of relying on split
You can interpolate variables into a double-quoted string. That avoids the use of several concatenation operators and makes the intention clearer
Your open is near-perfect - an unusual thing - because you correctly use the three-parameter form of open as well as testing whether it succeeded and putting $! in the die string. However you should also always use a lexical file handle as well instead of the old-fashioned global ones
You don't chomp the lines you read from the input, so the last field will have a trailing newline. Using Text::CSV avoids the need for this
You use indices 1 through 4 of the data split from the input record. Perl indices start at zero, so that means you are droppping the first field. Is that correct?
Similarly you are inserting fields 1 and 2, which appear to be subject and date, into fields called subject and weight. It seems unlikely that this can be right
You should prepare your SQL statements, use placeholders, and provide the actual data in an execute call
You seem to diagnose the data read from the file ("No Weight") but insert the data into the database anyway. This may be correct but it seems unlikely
Here is a version of your program that includes these amendments. I hope it is of use to you.
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
use Text::CSV;
use IO::Handle;
STDOUT->autoflush;
# MySQL config variables
my $host = "localhost";
my $user = "someuser";
my $pw = "somepassword";
my $database = "test";
my $dsn = "DBI:mysql:database=$database;host=$host";
my $dbh = DBI->connect($dsn, $user, $pw)
or die "Can't connect to the DB: $DBI::errstr\n";
print "Connected to DB!\n";
my $filename = "/home/jonathan/dep/csv/linux_datetime_test_4.26.13_.csv";
open my $fh, '<', $filename
or die qq{Unable to open "$filename" for input: $!};
my $csv = Text::CSV->new;
$csv->getline($fh) for 1, 2; # Drop header lines
my $insert_datetime_stamp = $dbh->prepare( 'INSERT INTO datetime_stamp (subject, date, time, weight) VALUES(?, ?, ?, ?)' );
my $insert_subj_weight = $dbh->prepare( 'INSERT INTO subj_weight (subject, weight) VALUES(?, ?)' );
while (my $row = $csv->getline($fh)) {
if (length($row->[4]) < 10) {
print qq{Invalid weight: "$row->[4]"\n};
}
else {
#insert the data into the db
print "insert into datetime_stamp\n";
$insert_datetime_stamp->execute(#$row[1..4]);
$insert_subj_weight->execute(#$row[1,4]);
}
}

DBI/DBD::mysql/mysql_type_name: How to distinguish between Blob and Text?

This example outputs two times "blob". How could I find out, if the datatype of a column is TEXT?
#!/usr/bin/env perl
use warnings;
use strict;
use DBI;
use Data::Dumper;
my$dbh = DBI->connect( "DBI:mysql:dbname=test", 'user', 'passwd', {
RaiseError => 1,
AutoCommit => 1,
} ) or die DBI->errstr;
my $table = 'my_test_table';
$dbh->do( "DROP TABLE IF EXISTS $table" );
$dbh->do( "CREATE TABLE $table ( Foo TEXT, Bar BLOB )" );
my $sth = $dbh->prepare( "INSERT INTO $table ( Foo, Bar ) VALUES( ?, ? )" );
$sth->execute( 'a', 'a' );
$sth = $dbh->prepare( "SELECT * FROM $table" );
$sth->execute();
my $col_types = $sth->{mysql_type_name};
print Dumper $col_types;
Output:
$VAR1 = [
'blob',
'blob'
];
Generally when you work with Database you know what the data fields are and you use fetch_hashref or fetch_arrayref functions and get the required data(in your case fields Foo(Text) and Bar(Blob)) , I didn't get what are you trying to achieve here? , If you need to get the data from Foo/Bar you should use fetch_arrayref or hashref functions, I also see that with 'use strict' you still managed to pass bareword 'mysql_type_name' to statement handle $sth.
This is because BLOB and TEXT are (nearly) the same thing in mysql: The BLOB and TEXT Types.
I am not sure why DBD::mysql can not distinguish the two. You may want to use VARCHAR instead of TEXT.