working with databases in perl

50
13.06.22 - Page 1 Département Office Working with databases in Perl Tutorial for FPW::2011, Paris [email protected] Département Office

Upload: ldami

Post on 10-May-2015

6.865 views

Category:

Technology


1 download

DESCRIPTION

An overview of the main questions/design issues when starting to work with databases in Perl - choosing a database - matching DB datatypes to Perl datatypes - DBI architecture (handles, drivers, etc.) - steps of DBI interaction : prepare/execute/fetch - ORM principles and difficulties, ORMs on CPAN - a few examples with DBIx::DataModel - performance issues First given at YAPC::EU::2009 in Lisbon. Updated version given at FPW2011 in Paris and YAPC::EU::2011 in Riga

TRANSCRIPT

Page 1: Working with databases in Perl

11.04.23 - Page 1

DépartementOffice

Working with databasesin Perl

Tutorial for FPW::2011, Paris

[email protected]

DépartementOffice

Page 2: Working with databases in Perl

Overview

• intended audience : beginners– in Perl– in Databases

• main topics– Relational databases– Perl DBI basics– Advanced Perl DBI– Object-Relational Mappings

• disclaimer– didn't have personal exposure to everything mentioned in this

tutorial

Page 3: Working with databases in Perl

11.04.23 - Page 1

DépartementOffice

Relational databases

RDBMS = Relational Database Management System

Page 4: Working with databases in Perl

join on c3

Relational model

c1 c2 c3

1 foo 1

2 foo 2

3 bar 1

c3 c4

1 xx

2 yyfilter

Table (rows + columns)

projection

c1 c2 c3 c4

1 foo 1 xx

2 foo 2 yy

3 bar 1 xx

Page 5: Working with databases in Perl

Maybe you don't want a RDBMS

• Other solutions for persistency in Perl:• BerkeleyDB : persistent hashes / arrays• Judy : persistent dynamic arrays / hashes• Redis : persistent arrays / hashes / sets / sorted sets• CouchDB : OO/hierarchical database • MongoDB : document-oriented database • KiokuDB : persistent objects, front-end to BerkeleyDB / CouchDB

/ etc.• Plain Old File (using for example File::Tabular )• KinoSearch : bunch of fields with fulltext indexing• LDAP : directory• Net::Riak : buckets and keys

– See http://en.wikipedia.org/wiki/NoSQL

Page 6: Working with databases in Perl

Features of RDBMS

• Relational• Indexing• Concurrency• Distributed• Transactions (commit / rollback )• Authorization• Triggers and stored procedures• Internationalization• Fulltext• …

Page 7: Working with databases in Perl

Choosing a RDBMS

• Sometimes there is no choice (enforced by context) !

• Criteria– cost, proprietary / open source– volume– features– resources (CPU, RAM, etc.)– ease of installation / deployment / maintenance– stored procedures

• Common choices (open source)– SQLite (file-based)– mysql– Postgres

• Postgres can have server-side procedures in Perl !

Page 8: Working with databases in Perl

Talking to a RDBMS

• SQL : Standard Query Language.

Except that

– the standard is hard to find (not publicly available)

– vendors rarely implement the full standard

– most vendors have non-standard extensions

– it's not only about queries• DML : Data Manipulation Language• DDL : Data Definition Language

Page 9: Working with databases in Perl

Writing SQL

SQL is too low-level, I don't ever want to see it

SQL is the most important part of my application, I won't let

anybody write it for me

Page 10: Working with databases in Perl

Data Definition Language (DDL)

CREATE TABLE author (author_id INTEGER PRIMARY KEY,author_name VARCHAR(20),e_mail VARCHAR(20),…

);

CREATE/ALTER/DROP/RENAMEDATABASEINDEXVIEWTRIGGER

Page 11: Working with databases in Perl

Data Manipulation Language (DML)

SELECT author_name, distribution_nameFROM author INNER JOIN distribution ON author.author_id = distribution.author_id WHERE distribution_name like 'DBD::%';

INSERT INTO author ( author_id, author_name, e_mail ) VALUES ( 123, 'JFOOBAR', '[email protected]' );

UPDATE authorSET e_mail = '[email protected]'

WHERE author_id = 3456;

DELETE FROM author WHERE author_id = 3456;

Page 12: Working with databases in Perl

Best practice : placeholders

SELECT author_name, distribution_nameFROM author INNER JOIN distribution ON author.author_id = distribution.author_id WHERE distribution_name like ? ;

INSERT INTO author ( author_id, author_name, e_mail ) VALUES ( ?, ?, ? );

UPDATE authorSET e_mail = ?

WHERE author_id = ? ;

DELETE FROM author WHERE author_id = ?;

no type distinction (int/string) statements can be cached avoid SQL injection problems

SELECT * FROM foo WHERE val = $x;

$x eq '123; DROP TABLE foo'

• sometimes other syntax (for ex. $1, $2)

Page 13: Working with databases in Perl

11.04.23 - Page 1

DépartementOffice

Perl DBI Basics

Page 14: Working with databases in Perl

Architecture

Database

DBD driver

DBI

Object-Relational Mapper

Perl program

TIOOWTDI

There is onlyone way to do it

TAMMMWTDI

There are many,many manyways to do it

TIMTOWTDI

There is more thanone way to do it

Page 15: Working with databases in Perl

DBD Drivers

– Databases• Adabas DB2 DBMaker Empress Illustra Informix Ingres InterBase

MaxDB Mimer Oracle Ovrimos PO Pg PrimeBase QBase Redbase SQLAnywhere SQLite Solid Sqlflex Sybase Unify mSQL monetdb mysql

– Other kinds of data stores• CSV DBM Excel File iPod LDAP

– Proxy, relay, etc• ADO Gofer JDBC Multi Multiplex ODBC Proxy SQLRelay

– Fake, test• NullP Mock RAM Sponge

Page 16: Working with databases in Perl

When SomeExoticDB has no driver

• Quotes from DBI::DBD :" The first rule for creating a new database driver for the Perl DBI is very

simple: DON'T! "" The second rule for creating a new database driver for the Perl DBI is

also very simple: Don't -- get someone else to do it for you! "

• nevertheless there is good advice/examples– see DBI::DBD

• Other solution : forward to other drivers– ODBC (even on Unix)– JDBC– SQLRelay

Page 17: Working with databases in Perl

DBI API

• handles– the whole package (DBI)– driver handle ($dh)– database handle ($dbh)– statement handle ($sth)

• interacting with handles– objet-oriented

• ->connect(…), ->prepare(…), ->execute(...), …

– tied hash• ->{AutoCommit}, ->{NAME_lc}, ->{CursorName}, …

Page 18: Working with databases in Perl

Connecting

my $dbh = DBI->connect($connection_string);

my $dbh = DBI->connect($connection_string, $user,

$password, { %attributes } );

my $dbh = DBI->connect_cached( @args );

Page 19: Working with databases in Perl

Some dbh attributes

• AutoCommit – if true, every statement is immediately committed– if false, need to call

$dbh->begin_work();… # inserts, updates, deletes$dbh->commit();

• RaiseError– like autodie for standard Perl functions : errors raise exceptions

• see also– PrintError– HandleError– ShowErrorStatement

• and also– LongReadLen– LongTrunkOK– RowCacheSize– …

hash API : attributes can be set dynamically

[local] $dbh->{$attr_name} = $val

• peek at $dbh internals

DB<1> x $dbh {} DB<2> x tied %$dbh {…}

Page 20: Working with databases in Perl

Data retrieval

my $sth = $dbh->prepare($sql);$sth->execute( @bind_values );

my @columns = @{$sth->{NAME}};

while (my $row_aref = $sth->fetch) { …}

# or$dbh->do($sql);

• see also : prepare_cached

Page 21: Working with databases in Perl

Other ways of fetching

• single row• fetchrow_array• fetchrow_arrayref (a.k.a fetch)• fetchrow_hashref

• lists of rows (with optional slicing)• fetchall_arrayref• fetchall_hashref

• prepare, execute and fetch• selectall_arrayref• selectall_hashref

• vertical slice• selectcol_arrayref little DBI support for

cursors

Page 22: Working with databases in Perl

11.04.23 - Page 1

DépartementOffice

Advanced Perl DBI

Page 23: Working with databases in Perl

Transactions

$dbh->{RaiseError} = 1; # errors will raise exceptions

eval {$dbh->begin_work(); # will turn off AutoCommit… # inserts, updates, deletes$dbh->commit();

};if ($@) {

my $err = $@;eval {$dbh->rollback()};my $rollback_result = $@ || "SUCCESS";die "FAILED TRANSACTION : $err" . "; ROLLBACK: $rollback_result";

} • encapsulated in DBIx::Transaction or ORMs $schema->transaction( sub {…} );

• nested transactions : must keep track of transaction depth

• savepoint / release : only in DBIx::Class

Page 24: Working with databases in Perl

Efficiency

my $sth = $dbh->prepare(<<'');SELECT author_id, author_name, e_mail FROM author

my ($id, $name, $e_mail);$sth->execute;$sth->bind_columns(\ ($id, $name, $e_mail));

while ($sth->fetch) { print "author $id is $name at $e_mail\n";}

avoids cost of allocating / deallocating Perl variables don't store a reference and reuse it after another fetch

Page 25: Working with databases in Perl

Metadata

• datasourcesmy @sources = DBI->data_sources($driver);

• table_infomy $sth = $dbh->table_info(@search_criteria);while (my $row = $sth->fetchrow_hashref) { print "$row->{TABLE_NAME} : $row->{TABLE_TYPE}\n";}

• others– column_info()– primary_key_info()– foreign_key_info()

many drivers only have partial implementations

Page 26: Working with databases in Perl

Lost connection

• manual recoverif ($dbh->errstr =~ /broken connection/i) { … }

• DBIx::RetryOverDisconnects– intercepts requests (prepare, execute, …)– filters errors– attemps to reconnect and restart the transaction

• some ORMs have their own layer for recovering connections

• some drivers have their own mechanism$dbh->{mysql_auto_reconnect} = 1;

Page 27: Working with databases in Perl

Datatypes

• NULL undef

• INTEGER, VARCHAR, DATE perl scalar– usually DWIM works– if needed, can specify explicitly

$sth->bind_param($col_num, $value, SQL_DATETIME);

• BLOB perl scalar

• ARRAY (Postgres) arrayref

Page 28: Working with databases in Perl

Large objects

• usually : just scalars in memory

• when reading : control BLOB size$dbh->{LongReadLen} = $max_bytes;$dbh->{LongTrunkOK} = 1

• when writing : can inform the driver$sth->bind_param($ix, $blob, SQL_BLOB);

• driver-specific stream API. Ex :– Pg : pg_lo_open, pg_lo_write, pg_lo_lseek– Oracle : ora_lob_read(…), ora_lob_write(…),

ora_lob_append(…)

Page 29: Working with databases in Perl

Tracing / profiling

• $dbh->trace($trace_setting, $trace_where)– 0 - Trace disabled. – 1 - Trace top-level DBI method calls returning with results or

errors. – 2 - As above, adding tracing of top-level method entry with

parameters.– 3 - As above, adding some high-level information from the driver

and some internal information from the DBI.

• $dbh->{Profile} = 2; # profile at the statement level

– many powerful options– see L<DBI::Profile>

Page 30: Working with databases in Perl

Stored procedures

my $sth = $dbh->prepare($db_specific_sql);

# prepare params to be passed to the called procedure$sth->bind_param(1, $val1);$sth->bind_param(2, $val2);

# prepare memory locations to receive the results$sth->bind_param_inout(3, \$result1);$sth->bind_param_inout(4, \$result2);

# execute the whole thing$sth->execute;

Page 31: Working with databases in Perl

11.04.23 - Page 1

DépartementOffice

Object-Relational Mapping (ORM)

Page 32: Working with databases in Perl

ORM Principle

r1r2...

c1 c2 c3

...

c3 c4

+c1: String+c2: String+c3: class2

r1 : class1

RDBMS

r2 : class1

Application

table1

table2

Page 33: Working with databases in Perl

ORM: What for ?

[catalyst list] On Thu, 2006-06-08, Steve wrote:

Not intending to start any sort of rancorous discussion, but I was wondering whether someone could illuminate me a little?

I'm comfortable with SQL, and with DBI. I write basic SQL that runs just fine on all databases, or more complex SQL when I want to target a single database (ususally postgresql).

What value does an ORM add for a user like me?

Page 34: Working with databases in Perl

ORM useful for …

• dynamic SQL– navigation between tables– generate complex SQL queries from Perl datastructures– better than phrasebook or string concatenation

• automatic data conversions (inflation / deflation)• expansion of tree data structures coded in the relational model• transaction encapsulation • data validation• computed fields• caching• schema deployment• …

See Also : http://lists.scsys.co.uk/pipermail/catalyst/2006-June/008059.html

Page 35: Working with databases in Perl

Impedance mismatch

• SELECT c1, c2 FROM table1 missing c3, so cannot navigate to class2 is it a valid instance of class1 ?

• SELECT * FROM table1 LEFT JOIN table2 ON … what to do with the c4 column ? is it a valid instance of class1 ?

• SELECT c1, c2, length(c2) AS l_c2 FROM table1 no predeclared method in class1 for accessing l_c2

c1 c2 c3 c3 c4+c1: String+c2: String+c3: class2

r1 : class1 RDBMSRAMtable1 table2

Page 36: Working with databases in Perl

ORM Landscape

• Leader– DBIx::Class (a.k.a. DBIC)

• Also discussed here– DBIx::DataModel

• Many others– Rose::DB, Jifty::DBI, Fey::ORM, ORM,

DBIx::ORM::Declarative, Tangram, Coat::Persistent,DBR, DBIx::Sunny, DBIx::Skinny, DBI::Easy, …

Page 37: Working with databases in Perl

Model (UML)

Artist

CD Track

1

*

1 *

Page 38: Working with databases in Perl

DBIx::Class Schema

package MyDatabase::Main; use base qw/DBIx::Class::Schema/; __PACKAGE__->load_namespaces;

package MyDatabase::Main::Result::Artist; use base qw/DBIx::Class/; __PACKAGE__->load_components(qw/PK::Auto Core/); __PACKAGE__->table('artist'); __PACKAGE__->add_columns(qw/ artistid name /); __PACKAGE__->set_primary_key('artistid'); __PACKAGE__->has_many('cds' => 'MyDatabase::Main::Result::Cd');

package ... ...

Page 39: Working with databases in Perl

DBIx::Class usage

my $schema = MyDatabase::Main ->connect('dbi:SQLite:db/example.db');

my @artists = (['Michael Jackson'], ['Eminem']); $schema->populate('Artist', [ [qw/name/], @artists, ]);

my $rs = $schema->resultset('Track')->search( { 'cd.title' => $cdtitle }, { join => [qw/ cd /], } ); while (my $track = $rs->next) { print $track->title . "\n"; }

Page 40: Working with databases in Perl

DBIx::DataModel Schema

package MyDatabase;use DBIx::DataModel;

DBIx::DataModel->Schema(__PACKAGE__)

->Table(qw/Artist artist artistid/)->Table(qw/CD cd cdid /)->Table(qw/Track track trackid /)

->Association([qw/Artist artist 1 /], [qw/CD cds 0..* /])->Composition([qw/CD cd 1 /], [qw/Track tracks 1..* /]);

Page 41: Working with databases in Perl

DBIx::DataModel usage

my $dbh = DBI->connect('dbi:SQLite:db/example.db');

MyDatabase->dbh($dbh);

my @artists = (['Michael Jackson'], ['Eminem']);MyDatabase::Artist->insert(['name'], @artists);

my $statement = MyDatabase->join(qw/CD tracks/)->select( -columns => [qw/track.title|trtitle …/], -where => { 'cd.title' => $cdtitle }, -resultAs => 'statement', # default : arrayref of rows);

while (my $track = $statement->next) { print "$track->{trtitle}\n";}

Page 42: Working with databases in Perl

11.04.23 - Page 1

DépartementOffice

Conclusion

Page 43: Working with databases in Perl

Further info

• Database textbooks• DBI manual (L<DBI>, L<DBI:.FAQ>,

L<DBI::Profile>)• Book : "Programming the DBI"• Vendor's manuals• ORMs

– DBIx::Class::Manual– DBIx::DataModel

mastering databases requires a lot of reading !

Page 44: Working with databases in Perl

11.04.23 - Page 1

DépartementOffice

Bonus slides

Page 45: Working with databases in Perl

Names for primary / foreign keys

• primary : unique; foreign : same name

author.author_id distribution.author_id• RDBMS knows how to perform joins ( "NATURAL JOIN" )

• primary : constant; foreign : unique based on table + column name

author.id distribution.author_id• ORM knows how to perform joins (RoR ActiveRecord)• SELECT * FROM table1, table2 …. which id ?

• primary : constant; foreign : just table name

author.id distribution.author• $a_distrib->author() : foreign key or related record ?

columns for joins should always be indexed

Page 46: Working with databases in Perl

Locks and isolation levels

• Locks on rows– shared

• other clients can also get a shared lock• requests for exclusive lock must wait

– exclusive• all other requests for locks must wait

• Intention locks (on whole tables)– Intent shared– Intent exclusive

• Isolation levels– read-uncommitted– read-committed– repeatable-read– serializable

SELECT … FOR READ ONLYSELECT … FOR UPDATESELECT … LOCK IN SHARE MODE

LOCK TABLE(S) … READ/WRITE

SET TRANSACTION ISOLATION LEVEL …

Page 47: Working with databases in Perl

Cursors

my $sql = "SELECT * FROM SomeTable FOR UPDATE"; my $sth1 = $dbh->prepare($sql);$sth1->execute();my $curr = "WHERE CURRENT OF $sth1->{CursorName}";

while (my $row = $sth1->fetch) {if (…) { $dbh->do("DELETE FROM SomeTable WHERE $curr");

} else { my $sth2 = $dbh->prepare( "UPDATE SomeTable SET col = ? WHERE $curr");

$sth2->execute($new_val); …

Page 48: Working with databases in Perl

Modeling (UML)

Author

Distribution Module

1

*

1 *

► depends on* *

► contains

Page 49: Working with databases in Perl

Terminology

Author

Distribution Module

1

*

1 *

► depends on* *

► contains

multiplicity

associationname

class

association

composition

Page 50: Working with databases in Perl

Implementation

author_idauthor_namee_mail

1

*

1 *

* *

Author

distrib_idmodule_id

Dependency

distrib_iddistrib_named_releaseauthor_id

Distribution

module_idmodule_namedistrib_id

Module

1 1

link table forn-to-n association