简体   繁体   中英

Perl DBI create table error

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.

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__

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM