💾 Archived View for magaz.hellug.gr › 23 › 04_perldbi › index.gmi captured on 2024-08-25 at 00:34:21. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2024-02-05)
-=-=-=-=-=-=-
Μιχάλης Καμπριάνης(mailto:kabrianis@hellug.gr) Απρ 2000
Σε αυτό το μικρό αρθράκι θα κάνουμε μία πρώτη και επιφανειακή προσέγγιση σε προγραματισμό Perl για Databases μέσω του DBI interface
Η Perl είναι μία interpreted γλώσσα όπως όλοι ξέρουν. Και ως interpreted θεωρείται από αρκετούς "κακή" γλώσσα. Από την άλλη, είναι εξαιρετικά απλή σε σύνταξη. Και επειδή είναι εξαιρετικά απλή, υπάρχουν εκατοντάδες προγραμματιστές που την επεκτείνουν, σε τομείς για τους οποίους δεν είχε σχεδιαστεί. Ένας από αυτούς τους τομείς είναι οι DataBases.
1. Τι παρέχει το DBI
2. Τι ΔΕΝ παρέχει το DBI
3. Συνηθισμένες συναρτήσεις
4. `Αλλα καλούδια
5. Βιβλιογραφία
Το module DBI (Database Interface) παρέχει τη δυνατότητα στην perl να μιλάει με μία database. Το module αυτό παρέχει κάποιες functions οι οποίες μιλάνε με κάποιο underlying module, το οποίο λέγεται DBD. Υπάρχει ξεχωριστό DBD για κάθε βάση δεδομένων (π.χ. MySQL, Postgres, Oracle κλπ), και δεν παρέχουν όλα τις ίδιες δυνατότητες, αλλά με την χρήση του DBI είσαστε λίγο-πολύ σίγουροι ότι ο κώδικας σας μεταφέρεται από την μία Database στην άλλη, χωρίς να ξαναγραφτούν όλες οι functions από την αρχή. Ειδικά αν η SQL που γράφετε είναι "κανονική" SQL και όχι specific-database-enhanced SQL, είναι πολύ εύκολο (από πλευράς προγράμματος και μόνο) να αλλάξετε βάση, ή να διανείμετε τον κώδικά σας χωρίς να σας ενδιαφέρει το τί βάση χρησιμοποιεί ο άλλος (αρκεί βέβαια να του ορίσετε το schema που θα δημιουργήσει στην βάση του).
Βέβαια, αν χρησιμοποιήσετε το DBI για να εκτελέσετε triggers ή stored procedures σε μία βάση (κάτι που γίνεται), δεν μπορείτε να περιμένετε μεγάλη μεταφερσιμότητα, κυρίως λόγω του διαφορετικού τρόπου με τον οποίο καλούν τα διάφορα triggers οι βάσεις. Συνεπώς, αν θέλετε να έχετε την ευελιξία να αλλάξετε βάση αύριο - μεθαύριο χωρίς να σας βγουν τα μάτια να διαβάζετε καλά όλο τον κώδικα για να βρείτε τα databse-specific κομμάτια, σημειώστε καλά τα σημεία που τα χρησιμοποιείτε και στο υπόλοιπο πρόγραμμα.... stic k to SQL.
Το DBI δεν παρέχει έναν τρόπο να μιλήσετε σε μία βάση χωρίς να ξέρετε SQL. To DBI είναι ένα απλό API, άρα αν δεν ξέρετε πως να σχεδιάσετε / υλοποιήσετε ένα RDBMS και πως να αποθηκεύσετε, αλλάξετε, σβήσετε αρχεία με απλή SQL, μην συνεχίσετε να διαβάζετε, το άρθρο αυτό θα σας είναι εντελώς άχρηστο. Αντ' αυτού διαβάστε ένα βιβλίο για SQL πρώτα, και μετά ξαναελάτε.
Για να ξεκινήσουμε να μιλάμε σε μία βάση, πρέπει πρώτα να συνδεθούμε σε αυτή τη βάση. Αυτό γίνεται απλά καλώντας την function "connect" του DBI. Όταν την καλέσουμε, θα μας επιστρέψει ένα handler (αν επιτύχει βέβαια), και με αυτό το handler θα μιλάμε.
Παράδειγμα 1 $dbh=DBI->connect('DBI:Pg:dbname=dbname user=dbuser password=dbpass');
Η παραπάνω γραμμή, απλά συνδέεται σε μία postgres database, στην βάση με όνομα dbname, με username dbuser και password dbpass. Η database στο συγκεκριμένο παράδειγμα πρέπει να είναι στο ίδιο μηχάνημα, και να "ακούει" στο standard port της Postgres, αλλιώς πρέπει να χρησιμοποιηθούν και οι παράμετροι host= και port=. Σωστή προγραμματιστική τακτική λέει ότι πρέπει η μεταβλητή $dbh πρέπει να έχει οριστεί νωρίτερα, και να ελεγχθεί αν πράγματι έχει τιμή αυτό που επιστρέφεται (αν δηλαδή πέτυχε το connect).
Αυτή η συγκεκριμένη γραμμή είναι αρκετά διαφορετική από βάση σε βάση. Για παράδειγμα για Oracle, θα έπρεπε να γραφτεί ως εξής
Παράδειγμα 2 $dbh=DBI->connect('DBI:Oracle:dbname', 'dbuser', 'dbpass');
και αυτό οφείλεται στο ότι ο κάθε προγραμματιστής ενός DBD δεν ακολουθεί κάποιους κανόνες για το πως περνάει της παραμέτρους στην βάση του.
Αφού δημιουργήσαμε τη σύνδεση, πλέον στέλνουμε εντολές, στο $dbh. Αυτό γίνεται σε δύο στάδια (εκτός από κάποιες περιπτώσεις που θα δούμε παρακάτω). Το πρώτο στάδιο είναι να "προετοιμάσουμε" την εντολή, και το δεύτερο είναι να την εκτελέσουμε. Το να λάβουμε τα δεδομένα είναι άλλη υπόθεση (και τρίτο στάδιο). Για παράδειγμα, για να εκτελέσουμε μία τυπική select σε ένα πίνακα, έστω clients, οι εντολές είναι :
Παράδειγμα 3 $entoli = $dbh->prepare('SELECT * FROM clients'); $entoli->execute();
Όσα είπαμε προηγουμένως για σωστή προγραμματιστική τακτική ισχύουν και εδώ (και σε όλο το άρθρο, οπότε θα σταματήσω να τα λέω), και απλά παραλείπονται μια που ο καθένας προτιμάει τον δικό του τρόπο δήλωσης μεταβλητών και ελέγχου επιτυχίας της function.
Όπως βλέπουμε, η prepare() δίνει την εντολή, η οποία εκτελείται από την execute(). Αν τώρα θέλουμε να επιλέξουμε συγκεκριμένα στοιχεία από τον πίνακα, απλά αντικαθιστούμε το * με τα στοιχεία που θέλουμε. Έστω δηλαδή ότι θέλουμε τα στοιχεία client_name και client_tel από τον πίνακα, οι εντολές γίνονται:
Παράδειγμα 4 $entoli = $dbh->prepare('SELECT client_sname, client_tel FROM clients'); $entoli->execute();
Φυσικά, αν θέλουμε να περιορίσουμε την select μπορούμε μέσω του DBI, θέτοντας WHERE clauses. Ο σωστός τρόπος να γίνει αυτός είναι ο εξής:
Παράδειγμα 5 $entoli = $dbh->prepare('SELECT client_tel FROM clients WHERE client_sname = ?'); $entoli->execute('kabrianis');
Η παράμετρος η οποία θα αντικαταστήσει το ερωτηματικό (?) όταν εκτελεστεί η εντολή, είναι η παράμετρος η οποία μπαίνει ως όρισμα στην function execute(). Τελικά δηλαδή θα εκτελεστεί στην Database η εντολή:
Παράδειγμα 6 SELECT client_tel FROM clients WHERE client_sname = 'kabrianis'
Προφανώς, μπορούμε να έχουμε περισσότερες από μία παραμέτρους στα WHERE clauses, αντικαθιστώντας τα αντίστοιχα ερωτηματικά με τις παραμέτρους, χωρισμένες με κόμμα, στην αντίστοιχη execute(). Μπορούμε επίσης να έχουμε και subqueries, όπως:
Παράδειγμα 7 $entoli = $dbh->prepare('SELECT client_tel FROM clients WHERE client_addr = ? AND client_sname in (SELECT client_sname FROM clients WHERE client_fname = ?)'); $entoli->execute('Athina','Michalis');
Αν μπερδευτήκατε, ξαναδιαβάστε SQL. Η εντολή είναι απλούστατη, και θα μας βρεί από την βάση μας (πίνακα clients) όσους έχουν διεύθυνση Athina και λέγονται Michalis.
Όλα αυτά τα αποτελέσματα της SELECT μπορούν να διαβαστούν από εμάς, γραμμή - γραμμή, με την fetchrow_array() μέθοδο της $entoli, και βέβαια μας έρχονται σε μορφή array. Για παράδειγμα, ο κώδικας του παραδείγματος 4 θα μας επιστρέφει arrays που θα περιέχουν τα client_sname και client_tel ως πρώτο και δεύτερο στοιχείο του array. Το να τα κρατήσουμε σε μεταβλητές και να τα κάνουμε κάτι, είναι φυσικά εύκολο.
Παράδειγμα 8 while (@apotelesmata = $entoli->fetchrow_array()) { $client_sname = $apotelesmata[0]; $client_tel = $apotelesmata[1]; ## ## Εδώ κάνουμε κάτι με τα αποτελέσματα που πήραμε ## }
Η χρήση της INSERT είναι πολύ απλή, και μοιάζει με αυτή της SELECT. Για παράδειγμα για να βάλουμε άλλη μία γραμμή στον παραπάνω πίνακα (ο οποίος έστω ότι έχει πεδία id, client_sname, client_fname και client_addr) θα γράψουμε τα εξής:
Παράδειγμα 9 $entoli = $dbh->prepare('INSERT INTO clients VALUES (?,?,?,?)'); $entoli->execute($id,$client_sname,$client_fname,$client_addr);
Φυσικά εδώ η fetchrow_array() δεν θα μας επιστρέψει τίποτα. Η ανωτέρω συνάρτηση μπορεί να επεκταθεί όσο θέλετε, με βάση πάντα τα όρια της SQL, προσθέτοντας συγκεκριμένες τιμές, σε συγκεκριμένα πεδία (αν το επιτρέπει ο πίνακας που έχετε φτιάξει).
Πραγματικά δεν θα περιμένετε κάτι διαφορετικό, έτσι; Ας πάμε κατυεθείαν στον κώδικα:
Παράδειγμα 10 $entoli = $dbh->prepare('DELETE FROM clients WHERE client_sname = ?'); $entoli->execute($client_sname);
και φυσικά, πάλι η fetchrow_array() δεν θα επιστρέψει τίποτα. Subqueries μπορούν να χρησιμοποιηθούν, για να σβήσουμε συγκεκριμένες γραμμές ενός πίνακα, ακόμα και χωρίς να δώσουμε καθόλου εξωτερικά στοιχεία.
`Αλλη μία απλή εντολή, η update, έχει ακριβώς την ίδια σύνταξη με την insert και την delete.
Παράδειγμα 11 $entoli = $dbh->prepare('UPDATE clients SET client_tel = ? WHERE client_sname = ?'); $entoli->execute($client_tel,$client_sname);
Όπως είπαμε και πιο πάνω, οι παράμετροι της execute() διαβάζονται σειριακά, και αντικαθιστούν τα ερωτηματικά που βρίσκουν στην prepare(). Αυτό το ξανατονίζω γιατί η update είναι ίσως η πιο "επικίνδυνη" εντολή, από την άποψη ότι μπορεί εύκολα να κάνεις λάθος, και να μην το καταλάβεις, και να κάνεις update όλες τις εγγραφές ενός πίνακα αντί για μία - δύο που ήθελες.
Για τις εντολές insert, update και delete, οι οποίες ΔΕΝ επιστρέφουν τίποτα, το DBI παρέχει έναν τρόπο να αποφεύγουμε τις πολλές γραμμές κώδικα. Ο τρόπος είναι να αντικαταστήσουμε τα prepare(), execute() και finish (που θα δούμε παρακάτω) με την απλή εντολή do.
Παράδειγμα 12 $entoli = $dbh->do('DELETE FROM clients WHERE client_id = 5');
Έτσι η εντολή DELETE προετοιμάζεται (prepare()), εκτελείται (execute()) και ο handler $entoli τερματίζεται (finish) αυτόματα.
Πρέπει πάντα, να τερματίζουμε τον handler της εντολής μετά την εκτέλεσή της, και το fetching των γραμμών που μας επιστρέφει (αν μας επιστρέφει τίποτα φυσικά). Συνεπώς, σε όλα τα παραπάνω παραδείγματα, πρέπει να προσθέσετε στο τέλος την γραμμή:
$entoli->finish;
ενώ πρέπει, όταν τελειώνουμε με την βάση μας, να κλείνουμε τη σύνδεση:
$dbh->disconnect;
Εκτός από τα connect, disconnect, prepare, execute, do, finish και fetchrow_arrays που είδαμε, υπάρχουν και άλλα καλούδια που παρέχονται από το DBI, τα οποία μπορούν να χρησιμοποιηθούν για να κάνουν τη ζωή σας ευκολότερη. Ας κάνουμε μία σύντομη αναφορά σε κάποια (τα πιο χρήσιμα ίσως) από αυτά:
Το DBI μας παρέχει στην err τον κωδικό λάθους (αν υπάρχει κάποιο) κατά την εκτέλεση της εντολής, στην errstring το μήνυμα λάθους που επιστρέφει η database, και στην state το SQLSTATE κωδικό λάθους. Χρήσιμες functions είναι οι trace, trace_msg και η func.
Καλό βιβλίο, και προτεινόμενο για DBI programming (και με κάποια στοιχεία SQL μέσα) είναι το Programming the Perl DBI από τις εκδόσεις O'Reilly. Μην το ψάξετε στον Παπασωτηρίου, δεν το έχει (και το ζητούσα 2 μήνες).
Πολύ καλές πληροφορίες δίνει το manual page του DBI του ίδιου.
Πρέπει επίσης να διαβάσετε το manual του DBD module της βάσης σας (αν φυσικά έχει ένα τέτοιο). Βιβλία για Perl και SQL ΔΕΝ θα προτείνω εδώ.