Reputation: 465
I am trying to create tables in perl using DBI. This is my code:
#! usr/bin/perl
use strict;
use warnings;
use 5.014;
use DBI;
my $user = "user";
my $password = "password";
my $hostname = "localhost";
my $database = 'database';
my $dsn = "DBI:mysql:database=$database;host=$hostname;";
my $dbh = DBI->connect($dsn, $user, $password, { RaiseError => 1});
my $create = "-- drop table if exists DEVICE;
CREATE TABLE DEVICE(
NAME CHAR(60),
ID CHAR(36) NOT NULL,
SHORT_ID INT UNSIGNED,
MODEL CHAR(50),
SERIAL_NUMBER VARCHAR(50),
IP_ADDRESS CHAR(50),
LOCATION CHAR(50),
DV_VERSION CHAR(20),
OS_VERSION CHAR(20),
DEVICE_GROUP CHAR(50),
MANAGED TINYINT NOT NULL,
CONSTRAINT PK_DEVICE PRIMARY KEY (ID)
) Engine = InnoDB;
-- drop table if exists SEGMENT;
CREATE TABLE SEGMENT(
ID CHAR(100) NOT NULL,
DEVICE_ID CHAR(36) NOT NULL,
NAME CHAR(60),
IP_ADDRESS CHAR(50),
SLOT_INDEX INT NOT NULL,
SEGMENT_INDEX INT NOT NULL,
CONSTRAINT PK_SEGMENT PRIMARY KEY (ID),
KEY(DEVICE_ID),
CONSTRAINT FK_PARENT_DEV FOREIGN KEY (DEVICE_ID) REFERENCES DEVICE(ID)
) Engine = InnoDB;
";
my $sth = $dbh->prepare($create);
$sth->execute;
I get the following error when I run this:
DBD::mysql::st execute failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CREATE TABLE SEGMENT(
ID CHAR(100) NOT NULL,
DEVICE' at line 18 at file.pl line 47.
When I copy/paste the exact same string that is in $create directly in mysql it works perfectly.
Please help, thanks.
Edit: Sorry, I should have clarified. I am pulling what is in $create from another source. This is only part of it and I can't edit the text. I guess I need to know how to split it into an array and execute them one by one.
Upvotes: 0
Views: 965
Reputation: 1715
You are doing everything right, just a slight change, execute those two queries separately, put this code, after $create
:
my $delim = "-- "; // delimeter
my @sqls = split($delim, $create); //split based on delimiter
foreach my $sql (@sqls) { // take each sql string
$sql = $delim . $sql; // append delimiter, as it was removed while splitting the sql string
my $sth = $dbh->prepare($sql); // prepare the sql statement
$sth->execute; // execute it
}
exit 0;
PS: this is a tested code.
Upvotes: 1
Reputation: 179064
If pulling in raw sql from an external source isn't a bad enough idea, you could always make it worse:
open my $fh, "mysql |";
print $fh "$create\n";
close $fh;
Between the "mysql" and the |
pipe, you need the credentials --user=username, etc., and the name of the default database.
Or, with DBI, if the SQL statements are properly crafted, like mydqldump generates, then ;\n
will always be the statement delimiter, so you should have reasonable luck splitting the string... in fact, you can even throw away the semicolon -- MySQL doesn't actually care.
If you have anything more complex, like stored procedures preceded by a DELIMITER
declaration, you'll need to parse for those statements, modify your expectation for a delimiter accordingly, and discard the delimiter statements along with the alternate delimiter. You don't need, and in fact cannot use, delimiter declarations and custom delimiters with DBI -- you just send the procedure declaration in one ->do() call, and it works.
You should be able to get away with the --
comment lines, as- is, once you split on the delimiters... those should not have to be stripped.
Upvotes: 0
Reputation: 126722
The DBI connection doesn't work like the MySQL command-line tool. You can't execute more than one SQL statement at one, and you don't need the --
prefix.
(It's also notable that you have reversed the convention of upper case SQL language words and lower case identifiers.)
Code like this should work
$dbh->do("drop table if exists DEVICE");
$dbh->do(<<__ENDSQL__);
CREATE TABLE DEVICE(
NAME CHAR(60),
ID CHAR(36) NOT NULL,
SHORT_ID INT UNSIGNED,
MODEL CHAR(50),
SERIAL_NUMBER VARCHAR(50),
IP_ADDRESS CHAR(50),
LOCATION CHAR(50),
DV_VERSION CHAR(20),
OS_VERSION CHAR(20),
DEVICE_GROUP CHAR(50),
MANAGED TINYINT NOT NULL,
CONSTRAINT PK_DEVICE PRIMARY KEY (ID)
) Engine = InnoDB
__ENDSQL__
$dbh->do("drop table if exists SEGMENT");
$dbh->do(<<__ENDSQL__);
CREATE TABLE SEGMENT(
ID CHAR(100) NOT NULL,
DEVICE_ID CHAR(36) NOT NULL,
NAME CHAR(60),
IP_ADDRESS CHAR(50),
SLOT_INDEX INT NOT NULL,
SEGMENT_INDEX INT NOT NULL,
CONSTRAINT PK_SEGMENT PRIMARY KEY (ID),
KEY(DEVICE_ID),
CONSTRAINT FK_PARENT_DEV FOREIGN KEY (DEVICE_ID) REFERENCES DEVICE(ID)
) Engine = InnoDB
__ENDSQL__
Upvotes: 2