How do I use the Class::DBI->sequence() method to fill 'id' field automatically in perl? - perl

I'm following the example Class::DBI.
I create the cd table like that in my MariaDB database:
CREATE TABLE cd (
cdid INTEGER PRIMARY KEY,
artist INTEGER, # references 'artist'
title VARCHAR(255),
year CHAR(4)
);
The primary key cdid is not set to auto-incremental. I want to use a sequence in MariaDB. So, I configured the sequence:
mysql> CREATE SEQUENCE cd_seq START WITH 100 INCREMENT BY 10;
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT NEXTVAL(cd_seq);
+-----------------+
| NEXTVAL(cd_seq) |
+-----------------+
| 100 |
+-----------------+
1 row in set (0.00 sec)
And set-up the Music::CD class to use it:
Music::CD->columns(Primary => qw/cdid/);
Music::CD->sequence('cd_seq');
Music::CD->columns(Others => qw/artist title year/);
After that, I try this inserts:
# NORMAL INSERT
my $cd = Music::CD->insert({
cdid => 4,
artist => 2,
title => 'October',
year => 1980,
});
# SEQUENCE INSERT
my $cd = Music::CD->insert({
artist => 2,
title => 'October',
year => 1980,
});
The "normal insert" succeed, but the "sequence insert" give me this error:
DBD::mysql::st execute failed: 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 ''cd_seq')' at line
1 [for Statement "SELECT NEXTVAL ('cd_seq')
"] at /usr/local/share/perl5/site_perl/DBIx/ContextualFetch.pm line 52.
I think the quotation marks ('') are provoking the error, because when I put the command "SELECT NEXTVAL (cd_seq)" (without quotations) in mysql client it works (see above). I proved all combinations (', ", `, no quotation), but still...
Any idea?
My versions: perl 5.30.3, 10.5.4-MariaDB

The documentation for sequence() says this:
If you are using a database with AUTO_INCREMENT (e.g. MySQL) then you do not need this, and any call to insert() without a primary key specified will fill this in automagically.
MariaDB is based on MySQL. Therefore you do not need the call to sequence(). Use the AUTO_INCREMENT keyword in your table definition instead.

Related

Syntax error in where clause in postgresql

fl=s.executeUpdate("
insert into demi(rno,subcode,subname,intm,extm,crd,resultdate)
values(
'13JG1A05A0',
'RT22058',
' FREE OPEN SOURCE SOFTWARE(FOSS) LAB ',
'20',
'70',
'2',
'MAY 2015'
)
end where not exists(SELECT * FROM demi WHERE rn0 ='13JG105A0' AND subcode='RT22058')
");
I'm working in jsp with postgresql as backend, my IDE shows error in this statement.
i want to insert a record into db after checking and making sure that no such record already exists
Is this statement correct, or am I trying a garbage code?
Please help, thanks in advance
The reason for your error message is that an INSERT statement does not allow a WHERE clause.
You can only add a where clause to a SELECT statement (or a DELETE or UPDATE statement)
So you would need to get rid of the VALUES clause and use the insert into .. select ... syntax:
insert into demi(rno,subcode,subname,intm,extm,crd,resultdate)
select '13JG1A05A0',
'RT22058',
' FREE OPEN SOURCE SOFTWARE(FOSS) LAB ',
'20',
'70',
'2',
'MAY 2015'
where not exists (SELECT *
FROM demi
WHERE rn0='13JG105A0'
AND subcode='RT22058');
However for the intended use case:
I'm trying to insert a particular record into db if and only if there exists no other record with same rno and subject code columns
there is a better alternative if you have a unique constraint on (rno, subcode) (which you should) - use the on conflict clause:
insert into demi (rno, subcode, subname, intm, extm, crd, resultdate)
values
(
'13JG1A05A0',
'RT22058',
' FREE OPEN SOURCE SOFTWARE(FOSS) LAB ',
'20',
'70',
'2',
'MAY 2015'
)
on conflict (rno, subcode) do nothing;
Again: the on conflict will only work if you have a unique constraint (or index) on those two columns.
Unrelated, but:
you should specify each constant value with a literal matching the underlying data type. '20' is a character constant, 20 would be a number. Ìf intm, extm and crd re numbers, don't provide character values. Also if resultdate is a date column 'MAY 2015' won't work either.
Not sure how
end where not exists(SELECT * FROM demi WHERE rn0 ='13JG105A0' AND subcode='RT22058')
got into your code, but the whole thing is suspicious what do you even want that to do?

How to quote column names on the commandline of fbexport

As the title says, how am I going to deal with a column name in FBExport that look like a keyword?
this is how my statement looks like:
-Q "SELECT a.ID, a.USERID, a.`WHEN`, a.INOUT FROM ATTENDANT a"
then I get this error:
Engine Code : 335544569
Engine Message :
Dynamic SQL Error
SQL error code = -104
Token unknown - line 1, column 26
WHEN
When I use
"WHEN"
Error: Switches must begin with -
tried 'When'
-Q "SELECT a.ID, a.USERID, a.'WHEN', a.INOUT FROM ATTENDANT a;"
SQL Message : -104
Invalid token
Engine Code : 335544569
Engine Message :
Dynamic SQL Error
SQL error code = -104
Token unknown - line 1, column 26
'WHEN'
Error: Switches must begin with -
What are the correct escape characters?
For dialect 3 database, Firebird allows quoting object names using double quotes ("<objectname>"). Be aware that quoting object names makes them case sensitive, so "WHEN" is not the same as "when". If your database is dialect 1 then this is not possible, and you should first convert your database to dialect 3.
However the problem this is a command line option, meaning that
-Q "SELECT a.ID, a.USERID, a."WHEN", a.INOUT FROM ATTENDANT a"
Is split by your shell to the arguments:
-Q
SELECT a.ID, a.USERID, a.
WHEN
, a.INOUT FROM ATTENDANT a
While you want:
-Q
SELECT a.ID, a.USERID, a."WHEN", a.INOUT FROM ATTENDANT a
To achieve that, you need to escape the double quote inside the second argument, so:
-Q "SELECT a.ID, a.USERID, a.\"WHEN\", a.INOUT FROM ATTENDANT a"
or - as indicated by a_horse_with_no_name in the comments - wrap the argument in single quotes:
-Q 'SELECT a.ID, a.USERID, a."WHEN", a.INOUT FROM ATTENDANT a'
This doesn't really have to do with Firebird or FBExport, but is a result of how your shell (eg bash) parses commandline arguments.
It looks like someone else is dealing with the external access to the Firebird database from Safescan TimeAttenedant
It was a little bit stupid from Safescan to name a column WHEN because this is a keyword in Firebird.
In the context of an insert statement, I have no success with a column list like:
insert into attendant (ID, USERID, DEVICEID, WHEN, INOUT, VERIFYMODE, WORKCODE) values (1092, 1, 1, '28.08.2017 08:00', 0, 4, 3);
"WHEN", \"WHEN\", \'WHEN\', ... no success
Remedy - Insert all data without column list, ex:
insert into attendant values (1034, 2, 1, '28.08.2017 08:00', 0, 4, null, null, null, null, null, 3);
Query is much easier: select * from attendant;

Error when querying PostgreSQL using range operators

I am trying to query a postgresql (v 9.3.6) table with a tstzrange to determine if a given timestamp exists within the table defined as
CREATE TABLE sensor(
id serial,
hostname varchar(64) NOT NULL,
ip varchar(15) NOT NULL,
period tstzrange NOT NULL,
PRIMARY KEY(id),
EXCLUDE USING gist (hostname WITH =, period with &&)
);
I am using psycopg2 and when I try the query:
sql = "SELECT id FROM sensor WHERE %s <# period;"
cursor.execute(sql,(isotimestamp,))
I get the error
psycopg2.DataError: malformed range literal:
...
DETAIL: Missing left parenthesis or bracket.
I've tried various type castings to no avail.
I've managed a workaround using the following query:
sql = "SELECT * FROM sensor WHERE %s BETWEEN lower(period) AND upper(period);"
but would like to know why I am having problem with the range operators. Is it my code or psycopg2 or what?
Any help is appreciated.
EDIT 1:
In response to the comments, I have attempted the same query on a simple 1-row table in postgresql like below
=> select * from sensor;
session_id | hostname | ip | period
------------+----------+-----------+-------------------------------------------------------------------
1 | bob | 127.0.0.1 | ["2015-02-08 19:26:42.032637+00","2015-02-08 19:27:28.562341+00")
(1 row)
Now by using the "#>" operator I get the following error:
=> select * from sensor where period #> '2015-02-08 19:26:43.04+00';
ERROR: malformed range literal: "2015-02-08 19:26:43.04+00"
LINE 1: select * from sensor where period #> '2015-02-08 19:26:42.03...
Which appears to be the same as the psycopg2 error, a malformed range literal, so I thought I would try typecasting to timestamp as below
=> select * from sensor where sensor.period #> '2015-02-08 19:26:42.032637+00'::timestamptz;
session_id | hostname | ip | period
------------+----------+-----------+-------------------------------------------------------------------
1 | feral | 127.0.0.1 | ["2015-02-08 19:26:42.032637+00","2015-02-08 19:27:28.562341+00")
So it appears that it is my mistake, the literal has to be typecast or it is assumed to be a range. Using psycopg2, the query can be executed with:
sql="select * from sensor where period #> %s::timestamptz"

Inserting values into multiple columns by splitting a string in PostgreSQL

I have the following heap of text:
"BundleSize,155648,DynamicSize,204800,Identifier,com.URLConnectionSample,Name,
URLConnectionSample,ShortVersion,1.0,Version,1.0,BundleSize,155648,DynamicSize,
16384,Identifier,com.IdentifierForVendor3,Name,IdentifierForVendor3,ShortVersion,
1.0,Version,1.0,".
What I'd like to do is extract data from this in the following manner:
BundleSize:155648
DynamicSize:204800
Identifier:com.URLConnectionSample
Name:URLConnectionSample
ShortVersion:1.0
Version:1.0
BundleSize:155648
DynamicSize:16384
Identifier:com.IdentifierForVendor3
Name:IdentifierForVendor3
ShortVersion:1.0
Version:1.0
All tips and suggestions are welcome.
It isn't quite clear what do you need to do with this data. If you really need to process it entirely in the database (looks like the task for your favorite scripting language instead), one option is to use hstore.
Converting records one by one is easy:
Assuming
%s =
BundleSize,155648,DynamicSize,204800,Identifier,com.URLConnectionSample,Name,URLConnectionSample,ShortVersion,1.0,Version,1.0
SELECT * FROM each(hstore(string_to_array(%s, ',')));
Output:
key | value
--------------+-------------------------
Name | URLConnectionSample
Version | 1.0
BundleSize | 155648
Identifier | com.URLConnectionSample
DynamicSize | 204800
ShortVersion | 1.0
If you have table with columns exactly matching field names (note the quotes, populate_record is case-sensitive to key names):
CREATE TABLE data (
"BundleSize" integer, "DynamicSize" integer, "Identifier" text,
"Name" text, "ShortVersion" text, "Version" text);
You can insert hstore records into it like this:
INSERT INTO data SELECT * FROM
populate_record(NULL::data, hstore(string_to_array(%s, ',')));
Things get more complicated if you have comma-separated values for more than one record.
%s = BundleSize,155648,DynamicSize,204800,Identifier,com.URLConnectionSample,Name,URLConnectionSample,ShortVersion,1.0,Version,1.0,BundleSize,155648,DynamicSize,16384,Identifier,com.IdentifierForVendor3,Name,IdentifierForVendor3,ShortVersion,1.0,Version,1.0,
You need to break up an array into chunks of number_of_fields * 2 = 12 elements first.
SELECT hstore(row) FROM (
SELECT array_agg(str) AS row FROM (
SELECT str, row_number() OVER () AS i FROM
unnest(string_to_array(%s, ',')) AS str
) AS str_sub
GROUP BY (i - 1) / 12) AS row_sub
WHERE array_length(row, 1) = 12;
Output:
"Name"=>"URLConnectionSample", "Version"=>"1.0", "BundleSize"=>"155648", "Identifier"=>"com.URLConnectionSample", "DynamicSize"=>"204800", "ShortVersion"=>"1.0"
"Name"=>"IdentifierForVendor3", "Version"=>"1.0", "BundleSize"=>"155648", "Identifier"=>"com.IdentifierForVendor3", "DynamicSize"=>"16384", "ShortVersion"=>"1.0"
And inserting this into the aforementioned table:
INSERT INTO data SELECT (populate_record(NULL::data, hstore(row))).* FROM ...
the rest of the query is the same.

String getting converted to number when inserting it in database through Perl's DBI $sth->execute() function

I'm using Perl's DBI and SQLite database (I have DBD::SQLite installed). I have the following code:
my $dbh = DBI->connect("dbi:SQLite:dbname=$db", "", "", { RaiseError => 1, AutoCommit => 1 });
...
my $q = "INSERT OR IGNORE INTO books (identica, book_title) VALUES (?, ?)";
my $sth = $dbh->prepare($q);
$sth->execute($book_info->{identica}, $book_info->{book_title});
The problem I have is when $book_info->{identica} begins with 0's they get dropped and I get a number inserted in the database.
For example, identica of 00123 will get converted to 123.
I know SQLite doesn't have types, so how do I make DBI to insert the identica as string rather than number?
I tried quoting it as "$book_info->{identica}" when passing to $sth->execute but that didn't help.
EDIT
Even if I insert value directly in query it doesn't work:
my $i = $book_info->{identica};
my $q = "INSERT OR IGNORE INTO books (identica, book_title) VALUES ('$i', ?)";
my $sth = $dbh->prepare($q);
$sth->execute($book_info->{book_title});
This still coverts 00123 to 123, and 0000000009 to 9...
EDIT
Holy sh*t, I did this on the command line, and I got this:
sqlite> INSERT INTO books (identica, book_title) VALUES ('0439023521', 'a');
sqlite> select * from books where id=28;
28|439023521|a|
It was dropped by SQLite!
Here is how the schema looks:
CREATE TABLE books (
id INTEGER PRIMARY KEY AUTOINCREMENT,
identica STRING NOT NULL,
);
CREATE UNIQUE INDEX IDX_identica on books(identica);
CREATE INDEX IDX_book_title on books(book_title);
Any ideas what is going on?
SOLUTION
It's sqlite problem, see answer by in the comments by Jim. The STRING has to be TEXT in sqlite. Otherwise it treats it as number!
Changing schema to the following solved it:
CREATE TABLE books (
id INTEGER PRIMARY KEY AUTOINCREMENT,
identica TEXT NOT NULL,
);
Use bind params
my $sth = $dbh->prepare($q);
$sth->bind_param(1, 00123, { TYPE => SQL_VARCHAR });
$sth->bind_param(2, $book_info->{book_title});
$sth->execute();
UPDATE:
Read about type affinity in SQLite. Because your column type is STRING (technically unsupported), it defaults to INTEGER affinity. You need to create your column as TEXT instead.
According to the docs, if the column type (affinity) is TEXT it should store it as a string; otherwise it will be a number.