Perl DBI and temporary tables - perl

I am using the Perl DBI and DB2.
When I run this code:
sub MergePolygonNameTable()
{
my $table = "THESCHEMA.NAME";
print "Merging into ${table} table. ", scalar localtime, "\n";
eval
{
$DbHandle->do("
declare global temporary table session.TEMP_NAME
(POLICY_MASTER_ID INT
)
;
");
$DbHandle->do("
CREATE UNIQUE INDEX session.TEMP_NAME_IDX1 ON session.TEMP_NAME
(POLICY_MASTER_ID ASC
)");
$DbHandle->do("
insert into session.TEMP_NAME
(POLICY_MASTER_ID
)
SELECT pm.ID as POLICY_MASTER_ID
FROM THESCHEMA.POLICY_MASTER pm
");
$DbHandle->do("
MERGE INTO THESCHEMA.NAME as t
USING session.TEMP_NAME as s
ON t.POLICY_MASTER_ID = s.POLICY_MASTER_ID
WHEN MATCHED
) THEN
UPDATE SET t.UPDATED_DATETIME = CURRENT_TIMESTAMP
WHEN NOT MATCHED THEN
INSERT
(POLICY_MASTER_ID
) VALUES
(s.POLICY_MASTER_ID
)
;
");
};
if ($#)
{
print STDERR "ERROR: $ExeName: Cannot merge into ${table} table.\n$#\n";
ExitProc(1);
}
}
The problem is that the THESCHEMA.NAME is empty after the run.
I suspect that DBI does not keep the contents of the temporary table after the do(). But the DBI does not allow me to put more than one statement in a do().
How do I get temporary tables to work in the DBI?

The ON COMMIT DELETE ROWS is the default option of the DECLARE GLOBAL TEMPORARY TABLE statement.
Use the ON COMMIT PRESERVE ROWS option to preserve the rows upon explicit or implicit COMMIT.
Like this:
declare global temporary table session.TEMP_NAME
(POLICY_MASTER_ID INT
)
ON COMMIT PRESERVE ROWS
;

Related

Having trouble in saving scalar variable into DB (sqlite3)?

# I am saving output to an array and the array looks like this:-
60=20130624-09:45:02.046|21=1|38=565|52=20130624-09:45:02.046|35=D|10=085|40=1|9=205|100=MBTX|49=11342|553=2453|34=388|1=30532|43=Y|55=4323|54=1|56=MBT|11=584|59=0|114=Y|8=FIX.4.4|
# Then i converted this array to scalar variable like this:-
$scal=join('' , #arr);
# And now I am trying to save this into db:-
my $st = qq(INSERT INTO demo (fix)
VALUES ($scal));
my $r = $dbh->do($st) or die $DBI::errstr;
#And my table schema is:-
CREATE TABLE demo (fix varchar);
And I keep getting errors :- DBD::SQLite::db do failed: near ":45": syntax error at pdb.pl line 92, <STDIN> line 1.
DBD::SQLite::db do failed: near ":45": syntax error at pdb.pl line 92, <STDIN> line 1.
Any help will be appreicated
The way you denote your array is a bit weird. Usually you would write it as
my #arr = ( '60=20130624-09:45:02.046',
'21=1',
'38=565',
... );
or whatever your actual content is. But this is not the problem here because you flatten it to the string $scal anyway.
One way to insert this string into your DB is to put ticks (') around it:
my $st = qq(INSERT INTO demo (fix) VALUES ('$scal'));
my $r = $dbh->do($st) or die $DBI::errstr;
But this is bad because it's vulnerable to SQL injection (http://imgs.xkcd.com/comics/exploits_of_a_mom.png).
Consider the case your string is foo'); delete from demo; --. The final result would then be
INSERT INTO demo (fix) VALUES ('foo'); delete from demo; --')
The second reason why this is bad: Your string could contain ticks ($scal="foo's bar") and that also would mess up the resulting INSERT statement:
INSERT INTO demo (fix) VALUES ('foo's bar');
Conclusion: it's always better to use parameterized queries:
my $st = 'INSERT INTO demo (fix) VALUES (?)';
my $r = $dbh->do($st, undef, $scal) or die $DBI::errstr;
The undef is for additional SQL options (I've rarely seen anything different from undef here). The following parameters are replaced for the ?s in the statement. The DB driver does all the quoting for you. The more ? you use, the more parameters you must supply to do():
my $st = 'INSERT INTO sample_tbl (col1, col2, col3) VALUES (?, ?, ?)';
my $r = $dbh->do($st, undef, 'foo', 42, $scal) or die $DBI::errstr;

How to Perl Placeholder Variable for a DBD Mysql

I recently had some help with dbi and using placeholders in Perl for Mysql queries. However I am having an issue when using multiple statements for the previously declared or prepared variable in the dbi script.
Code:
use strict;
use warnings;
use DBI qw(:sql_types);
my $dbh = DBI->connect("DBI:mysql:...", ...);
## TABLE CREATION
$dbh->do("USE test;")
$dbh->do("CREATE TEMPORARY TABLE day5 (id INT, temp VARCHAR(4), time TIME, sumadd INT(11))");
$dbh->do("CREATE TEMPORARY TABLE humid (temp VARCHAR(4), i24 INT (10))");
$dbh->do("INSERT INTO day5 (id,temp,time) VALUES(1,'30','03:00:00') ");
$dbh->do("INSERT INTO humid (temp,i24) VALUES('30',8321) ");
## FAILING CODE
my $inter1 = 'i24'; # Generated value
my $sth = $dbh->prepare("SET \#sumadd5 = (SELECT ? FROM humid WHERE temp ='30') ");
$sth->bind_param( 1, $inter1 );
$sth->finish();
$dbh->do("UPDATE day5 SET sumadd= (SELECT \#sumadd5) WHERE time= '03:00:00' ");
my $sumadd = $dbh->selectrow_array("SELECT sumadd FROM day5");
print "$sumadd\n";
$dbh->disconnect();
$sumadd is undefined, but I expect 8321.
My question is how can I make it so the #sumadd5 variable can be inserted into the above query [UPDATE day5 SET sumadd=]? I have added some remarks in the syntax in the spirit of clarity.
IF I RUN MANUALLY BY APPLYING THE SYNTAX INTO MYSQL REMOVING PERL SPECIFICS MY TABLE UPDATE. IF I RUN THE SCRIPT NOTHING HAPPENS TO THE TABLE AND NO ERRORS ARE DISPLAYED.
I suspect that the break is with the UPDATE, however I can't confirm the $inter1 is being passed to the placeholder.
Problem #1
You never execute the statement you prepare. Use
my $sth = $dbh->prepare(q{...});
$sth->bind_param(1, $inter1);
$sth->execute(); <---
$sth->finish();
Shortcut #1:
my $sth = $dbh->prepare(q{...});
$sth->execute($inter1);
$sth->finish();
Shortcut #2:
$dbh->do(
q{...},
undef,
$inter1,
);
Problem #2
SELECT ? ...
behaves as if you had done
SELECT "i24" ... // As opposed to: SELECT i24 ...
just like
SELECT time ...
behaves as if you had done
SELECT '03:00:00' ...`
You can't build the SQL statement you are executing. You need to build it before you execute it. Use
$dbh->do(q{
SET #sumadd5 = (
SELECT }.( $dbh->quote_identifier($inter1) ).q{
FROM humid
WHERE temp = '30'
)
});

SQLite: Add columns on the fly if they don't exist

sub insert {
my ($self) = #_;
my $data = $self->{data};
my $keys = join( ', ', keys $data);
my $values = join( ', ', map qq('$_'), values $data);
my $sql = "INSERT INTO tbl ($keys) VALUES ($values);";
my $sth = $self->{dbh}->prepare($sql);
$sth->execute();
}
I have a method that inserts the contents of a hash ref into a one table sqlite database. I was wondering if there was a simple way to add a column to the table if the hash key is not already a column. Obviously if one of the keys is not a column name, the insert will fail. Can I capitalize on that failure, add the missing columns, and redo the insert. Or would I have to check all columns against all the keys each time I want to insert into the database? (All keys have TEXT values)
Alter your table based on the info you get using PRAGMA
my $inf_query = $db->prepare("PRAGMA table_info('tbl')");
$inf_query->execute();
my #inf = map { $_->[1] } #{$inf_query->fetchall_arrayref()};
#inf will be an array containing the columns present in the table, and you can use that info to construct your ALTER query.
Edited to return an array you can use to grep ;)

perl script to search for multiple lines from a file with start and end keywords

I am trying to search in a .sql file for sql statement which starts with CREATE TABLE followed by fields values then keywords [TB_DATA and TB_INDX] and ends by ; it in multiple lines
.sql file statement is in multiple lines
-- CREATE TABLE HDTB_COD;
CREATE TABLE HDTB_CODE( IDPK VARCHAR(256) NOT NULL)
IN TB_DATA INDEX
IN TB_INDX;
CREATE TABLE HDTB_RES
(ARTID VARCHAR(256) NOT NULL)
IN TB_DATA INDEX
IN TB_INDX;
-- DROP TABLE HDTB_COD;
CREATE TABLE HDTB_DE ( IDPK VARCHAR(256)
NOT NULL);
-------------output----------------------
CREATE TABLE HDTB_CODE( IDPK VARCHAR(256) NOT NULL)
IN TB_DATA INDEX IN TB_INDX;
CREATE TABLE HDTB_RES(ARTID VARCHAR(256) NOT NULL)
IN TB_DATA INDEX IN TB_INDX;
perl -n -e 'chomp; next if (/^--/);#p=() if /CREATE TABLE/; push #p,$_; if (/IN TB_DATA INDEX IN TB_INDX;/) { print "#p\n"; }' t.sql
How it works
chomp; # remove newlines
next if (/^--/); #skip lines that are SQL comments
#p = () if /CREATE TABLE/; #start of a table definition, clear array #p
push #p, $_; # put current line into array #p
#condition found, print #p
if (/IN TB_DATA INDEX IN TB_INDX;/) { print "#p\n"; }
Here is an example of how to create quick-and-dirty parsing pipelines. Once you understand the basic pattern, it's easy to add more filtering steps (with grep) or transforming steps (with map)
# Slurp entire file.
my $sql = do { local $/ = undef; <> };
# 1. Grab the CREATE TABLE statements.
# 2. Retain only the statements of interest.
# 3. Modify the statements as needed before printing.
print
map { "$_\n" } # 3b. Add trailing newlines.
map { s/\s+/ /g; $_ } # 3a. Normalize whitespace.
grep { /IN TB_INDX/ } # 2b. Filter.
grep { /IN TB_DATA INDEX/ } # 2a. Filter.
$sql =~ /^(CREATE TABLE .+?;)\s*$/gsm; # 1. Grab.

Perl DBI insert and select

I want to copy a single record from a table, modify some of the fields in the record and insert into the same table. The table has 90 columns.
Thought of using insert..select in one statement but there are 90 columns and i need to tell the column name in the select query. How can i do it in a better way in perldbi. Pls provide me an example.
Fetch and cache the column names for your table using the NAME statement attribute
my $sth = $dbh->prepare('SELECT * FROM my_table where 1=0');
$sth->execute;
my $cols = $sth->{NAME};
then use $cols to construct your insert...select using some replacement function to inject your modifications in the select.
my %mods_for_column = ( 'foo' => 'foo+10', 'bar' => 'trim(bar)' );
my $inscols = join(',', #$cols);
my $selcols = join(',',
map { exists($mods_for_column($_)) ? $mods_for_column($_) : $_ } #$cols
);
my $sql = "insert into my_table ($inscols) select $selcols from my_table where mumble...";