16
Using Perl Stored Procedures with MariaDB Antony T Curtis <[email protected] > Special thanks to LinkedIn for permitting personal projects

Using Perl Stored Procedures for MariaDB

Embed Size (px)

DESCRIPTION

https://www.percona.com/live/mysql-conference-2013/sessions/perl-stored-procedures-mariadb

Citation preview

Page 1: Using Perl Stored Procedures for MariaDB

Using Perl Stored Procedures

with MariaDB

Antony T Curtis <[email protected]>Special thanks to LinkedIn for permitting personal projects

Page 2: Using Perl Stored Procedures for MariaDB

Why Perl?

• CPAN• "Multiplicity" / Thread-friendly.• Lots of Perl code already written.• MySQL UDFs have no access control.• MySQL UDFs cannot execute dynamic SQL.• Faster than “native” Stored Procedures.

Page 3: Using Perl Stored Procedures for MariaDB

Perl Stored Procedures

• Implemented as Perl modules.• Use DBD::mysql to access database.• Runs in-process with MariaDB.• Easier to debug than SQL stored routines.

Page 4: Using Perl Stored Procedures for MariaDB

Hello World

MariaDB [Demo]> CREATE PROCEDURE PerlHello() -> DYNAMIC RESULT SETS 1 -> NO SQL -> LANGUAGE Perl -> EXTERNAL NAME "HelloWorld::test";Query OK, 0 rows affected (0.02 sec)

MariaDB [Demo]> call PerlHello();+-----------------------+| message |+-----------------------+| Hello World from Perl |+-----------------------+1 row in set (0.03 sec)

Query OK, 0 rows affected (0.03 sec)

package HelloWorld;# put this file in <prefix>/lib/mysql/perl use 5.008008;use strict;use warnings;use Symbol qw(delete_package);require Exporter;

our @ISA = qw(Exporter);our @EXPORT_OK = qw( );our @EXPORT = qw( test );our $VERSION = '0.01';

sub test(){ return { 'message' => 'Hello World from Perl', };}1;

Page 5: Using Perl Stored Procedures for MariaDB

Hello World

MariaDB [Demo]> CREATE PROCEDURE PerlHello() -> DYNAMIC RESULT SETS 1 -> NO SQL -> LANGUAGE Perl -> EXTERNAL NAME "HelloWorld::test";Query OK, 0 rows affected (0.02 sec)

MariaDB [Demo]> call PerlHello();+-----------------------+| message |+-----------------------+| Hello World from Perl |+-----------------------+1 row in set (0.03 sec)

Query OK, 0 rows affected (0.03 sec)

package HelloWorld;# put this file in <prefix>/lib/mysql/perl use 5.008008;use strict;use warnings;use Symbol qw(delete_package);require Exporter;

our @ISA = qw(Exporter);our @EXPORT_OK = qw( );our @EXPORT = qw( test );our $VERSION = '0.01';

sub test(){ return { 'message' => 'Hello World from Perl', };}1;

Page 6: Using Perl Stored Procedures for MariaDB

Error handling or die

MariaDB [Demo]> CREATE PROCEDURE PerlError() -> DYNAMIC RESULT SETS 1 -> NO SQL -> LANGUAGE Perl -> EXTERNAL NAME "GoodbyeWorld::test";Query OK, 0 rows affected (0.00 sec)

MariaDB [Demo]> call PerlError();ERROR 1220 (HY000): Cannot open file: No such file or directory at /usr/local/mysql/lib/plugin/perl/GoodbyeWorld.pm line 16.

package GoodbyeWorld;# put this file in <prefix>/lib/mysql/perluse 5.008008;use strict;use warnings;use Symbol qw(delete_package);require Exporter;

our @ISA = qw(Exporter);our @EXPORT_OK = qw( );our @EXPORT = qw( test );our $VERSION = '0.01';

sub test({ open FILE, "</something/nonexist" or die "Cannot open file: $!"; return { 'message' => 'Goodbye World from Perl', };}1;

Page 7: Using Perl Stored Procedures for MariaDB

Reading DataMariaDB [employees]> CREATE PROCEDURE employee( -> empno INT) -> DYNAMIC RESULT SETS 1 -> READS SQL DATA -> LANGUAGE Perl -> EXTERNAL NAME "ReadData::test";Query OK, 0 rows affected (0.02 sec)

MariaDB [employees]> call employee(10034)\G*************************** 1. row ***************************birth_date: 1962-12-29 emp_no: 10034first_name: Bader gender: M hire_date: 1988-09-21 last_name: Swan1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

package ReadData;# put this file in <prefix>/lib/mysql/perl use 5.008008;use strict;use warnings;use Symbol qw(delete_package);require Exporter;

our @ISA = qw(Exporter);our @EXPORT_OK = qw( );our @EXPORT = qw( test );our $VERSION = '0.01';our $DSN = ‘dbi:mysql:employees’;

use DBI;use DBD::mysql;

sub test($){ my($emp_no) = @_; my $dbh = DBI->connect($DSN, undef, undef) || die "Failed in call to DBI->connect, $!";

my $s = $dbh->prepare( “SELECT * FROM employees WHERE emp_no=?”);

$s->execute(int $emp_no);

return $s->fetchrow_hashref;}1;

Page 8: Using Perl Stored Procedures for MariaDB

Reading DataMariaDB [employees]> CREATE PROCEDURE employee( -> empno INT) -> DYNAMIC RESULT SETS 1 -> READS SQL DATA -> LANGUAGE Perl -> EXTERNAL NAME "ReadData::test";Query OK, 0 rows affected (0.02 sec)

MariaDB [employees]> call employee(10034)\G*************************** 1. row ***************************birth_date: 1962-12-29 emp_no: 10034first_name: Bader gender: M hire_date: 1988-09-21 last_name: Swan1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

package ReadData;# put this file in <prefix>/lib/mysql/perl use 5.008008;use strict;use warnings;use Symbol qw(delete_package);require Exporter;

our @ISA = qw(Exporter);our @EXPORT_OK = qw( );our @EXPORT = qw( test );our $VERSION = '0.01';our $DSN = ‘dbi:mysql:employees’;

use DBI;use DBD::mysql;

sub test($){ my($emp_no) = @_; my $dbh = DBI->connect($DSN, undef, undef) || die "Failed in call to DBI->connect, $!";

my $s = $dbh->prepare( “SELECT * FROM employees WHERE emp_no=?”);

$s->execute(int $emp_no);

return $s->fetchrow_hashref;}1;

Page 9: Using Perl Stored Procedures for MariaDB

Modifying DataMariaDB [employees]> CREATE PROCEDURE -> makesummary( -> empno INT) -> MODIFIES SQL DATA -> LANGUAGE Perl -> EXTERNAL NAME "ModifyData::test";Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> call makesummary;

sub test(){ my $dbh = DBI->connect($DSN, undef, undef) || die "Failed in call to DBI->connect, $!"; $dbh->begin_work or die $!; my $ins = $dbh->prepare("INSERT INTO summary VALUES (?,?)"); my $emps = $dbh->selectall_arrayref( "select emp_no, max(salary) salary from employees ". "left join salaries using (emp_no) group by emp_no", { Slice => {} }); foreach my $emp (@$emps) { $ins->execute($emp->{emp_no}, $emp->{salary}) or die $!; } $dbh->commit or die $!; return undef;}

Page 10: Using Perl Stored Procedures for MariaDB

Modifying DataMariaDB [employees]> CREATE PROCEDURE -> makesummary( -> empno INT) -> MODIFIES SQL DATA -> LANGUAGE Perl -> EXTERNAL NAME "ModifyData::test";Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> call makesummary;Query OK, 0 rows affected (18.02 sec)

sub test(){ my $dbh = DBI->connect($DSN, undef, undef) || die "Failed in call to DBI->connect, $!"; $dbh->begin_work or die $!; my $ins = $dbh->prepare("INSERT INTO summary VALUES (?,?)"); my $emps = $dbh->selectall_arrayref( "select emp_no, max(salary) salary from employees ". "left join salaries using (emp_no) group by emp_no", { Slice => {} }); foreach my $emp (@$emps) { $ins->execute($emp->{emp_no}, $emp->{salary}) or die $!; } $dbh->commit or die $!; return undef;}

Page 11: Using Perl Stored Procedures for MariaDB

Modifying DataMariaDB [employees]> CREATE PROCEDURE -> makesummary( -> empno INT) -> MODIFIES SQL DATA -> LANGUAGE Perl -> EXTERNAL NAME "ModifyData::test";Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> call makesummary;Query OK, 0 rows affected (18.02 sec)

MariaDB [employees]> select count(*) -> from summary;+----------+| count(*) |+----------+| 300024 |+----------+1 row in set (0.15 sec)

sub test(){ my $dbh = DBI->connect($DSN, undef, undef) || die "Failed in call to DBI->connect, $!"; $dbh->begin_work or die $!; my $ins = $dbh->prepare("INSERT INTO summary VALUES (?,?)"); my $emps = $dbh->selectall_arrayref( "select emp_no, max(salary) salary from employees ". "left join salaries using (emp_no) group by emp_no", { Slice => {} }); foreach my $emp (@$emps) { $ins->execute($emp->{emp_no}, $emp->{salary}) or die $!; } $dbh->commit or die $!; return undef;}

Page 12: Using Perl Stored Procedures for MariaDB

Modifying DataMariaDB [employees]> CREATE PROCEDURE -> makesummary( -> empno INT) -> MODIFIES SQL DATA -> LANGUAGE Perl -> EXTERNAL NAME "ModifyData::test";Query OK, 0 rows affected (0.00 sec)

MariaDB [employees]> call makesummary;Query OK, 0 rows affected (18.02 sec)

MariaDB [employees]> select count(*) -> from summary;+----------+| count(*) |+----------+| 300024 |+----------+1 row in set (0.15 sec)

sub test(){ my $dbh = DBI->connect($DSN, undef, undef) || die "Failed in call to DBI->connect, $!"; $dbh->begin_work or die $!; my $ins = $dbh->prepare("INSERT INTO summary VALUES (?,?)"); my $emps = $dbh->selectall_arrayref( "select emp_no, max(salary) salary from employees ". "left join salaries using (emp_no) group by emp_no", { Slice => {} }); foreach my $emp (@$emps) { $ins->execute($emp->{emp_no}, $emp->{salary}) or die $!; } $dbh->commit or die $!; return undef;}

select statement - 2 seconds300k inserts in 16 seconds== 18k inserts per second

Page 13: Using Perl Stored Procedures for MariaDB

More than just procs

• CPAN is a large library...

• Can extend MariaDB’s functionality.

• How about using HTTP::Daemon?

Page 14: Using Perl Stored Procedures for MariaDB

Server Monitoringsub daemon(){ my $d = HTTP::Daemon->new( LocalAddr => '127.0.0.1', LocalPort => 8080, ) || die "Failed in call to HTTP::Daemon->new, $!";

my $dbh = DBI->connect("dbi:mysql:mysql", undef, undef) || die "Failed in call to DBI->connect, $!";

while (my $c = $d->accept) { while (my $r = $c->get_request) { if (exists $pages{$r->url->path}) { $pages{$r->url->path}->($dbh, $c, $r); } else { $c->send_error(RC_NOT_FOUND); } } $c->close; undef $c; }}

What if we can use HTTP::Daemon?

Page 15: Using Perl Stored Procedures for MariaDB

Server Monitoring '/statusz' => sub { my ($dbh,$c,$r) = @_; return $c->send_error(RC_FORBIDDEN) if $r->method ne 'GET';

my $sth = $dbh->prepare_cached(q{ SELECT VARIABLE_NAME name, VARIABLE_VALUE value FROM INFORMATION_SCHEMA.GLOBAL_STATUS }, { Slice => {} }); $sth->execute() || die $sth->errstr;

my $response = HTTP::Response->new(200, undef, HTTP::Headers->new( Content_Type => "application/json", )); my $result = {}; while (my $row = $sth->fetchrow_arrayref) { $result->{$row->[0]} = $row->[1]; } $response->add_content(to_json($result, { ascii => 1, pretty => 1 })); return $c->send_response($response); },

Fetching current server status could be as simple as fetching

http://127.0.0.1/statusz

Page 16: Using Perl Stored Procedures for MariaDB

Status

• Code hosted at LaunchPad.

• Contributing to MariaDB 10.0 soon.

• Stuff needs to be tidied up.

• Future steps include better Table Functions.