Πληροφορική & Τηλεπικοινωνίες K25 Ανάπτυξη Λογισμικού Εαρινό Εξάμηνο 2008 Καθηγητής Γιάννης Ιωαννίδης Μέρος 3ο: Επίπεδο Λειτουργικότητας Χρήστη (Ημερομηνία Παράδοσης: Τρίτη 17/6/2008, 11:59μμ) Εισαγωγή Για την ολοκλήρωση της όλης εργασίας απαιτείται η κατασκευή των παρακάτω τριών προγραμμάτων: 1 dbcreate 2 dbdestroy 3 minirel 1 - dbcreate Το πρόγραμμα dbcreate είναι ένα (μικρό) βοήθημα που χρησιμοποιείται για την δημιουργία νέων βάσεων δεδομένων Σύνταξη: Η λειτουργία του προγράμματος είναι εξαιρετικά απλή Αρκεί στην γραμμή εντολών ο χρήστης να πληκτρολογήσει dbcreate dbname και το πρόγραμμα δημιουργεί μία νέα βάση δεδομένων με όνομα dbname Η βάση αυτή μπορεί στην συνέχεια να χρησιμοποιηθεί από το πρόγραμμα minirel ή μπορεί να διαγραφεί με την βοήθεια του dbdestroy Πληροφορίες υλοποίησης: Κάθε βάση αποτελείται από ένα σύνολο από σχέσεις και ευρετήρια Τα αρχεία αυτά τοποθετούνται μέσα σε ένα φάκελο που έχει το ίδιο όνομα με την βάση Επομένως, η δημιουργία μία νέας βάσης με όνομα dbname, ισοδυναμεί με την δημιουργία ενός νέου φακέλου με όνομα dbname και την κατασκευή (μέσα σε αυτόν) των πινάκων που αφορούν τα μεταδεδομένα (σχέσεις relcat, attrcat, κλπ) της βάσης Οι σχέσεις θα δημιουργηθούν χρησιμοποιώντας κατάλληλα τις συναρτήσεις του επιπέδου HF Φυσικά, αν το επιθυμείτε, μπορείτε να δημιουργήσετε και ευρετήρια με σκοπό την επιτάχυνση της χρήσης των σχέσεων αυτών Για τη δημιουργία φακέλων, δεν έχει γίνει καμία πρόβλεψη από το πρότυπο της C Για αυτό τον λόγο θα πρέπει να χρησιμοποιήσετε την mkdir του κελύφους Για παράδειγμα, για να φτιάξετε τον φάκελο foo, θα πρέπει να χρησιμοποιήσετε την εντολή system("mkdir foo")
Σε γενικές γραμμές, η συνάρτηση main() του dbcreate θα έχει την ακόλουθη μορφή: int main(int argc, char* argv[]){ char command[80]; char* dbname; if(argc!=2){ printf("χρήση: %s dbname \n",argv[0]); return(1); dbname=argv[1]; sprintf(command,"mkdir %s",dbname); system(command); if(!chdir(dbname)){ printf("δεν είναι δυνατή η δημιουργία της βάσης"); return(2); /* Δημιουργία των πινάκων relcat και attrcat με χρήση των συναρτήσεων ΗF_CreateFile */ return 0; 2 - dbdestroy Το πρόγραμμα dbdestroy είναι ένα (μικρό) βοήθημα που χρησιμοποιείται για την διαγραφή βάσεων δεδομένων Σύνταξη: Η λειτουργία του προγράμματος είναι εξαιρετικά απλή Αρκεί στη γραμμή εντολών ο χρήστης να πληκτρολογήσει dbdestroy dbname και το πρόγραμμα διαγράφει τη βάση δεδομένων με όνομα dbname Πληροφορίες υλοποίησης: Κάθε βάση αποτελείται από ένα σύνολο από σχέσεις και ευρετήρια Τα αρχεία αυτά τοποθετούνται μέσα σε ένα φάκελο που έχει το ίδιο όνομα με την βάση Επομένως, η διαγραφή μιας βάσης με όνομα dbname, ισοδυναμεί με την διαγραφή του φακέλου αυτού Για την διαγραφή φακέλων, δεν έχει γίνει καμία πρόβλεψη από το πρότυπο της C Για αυτό τον λόγο θα πρέπει να χρησιμοποιήσετε την εντολή rm του κελύφους Για παράδειγμα, για να διαγράψετε τον φάκελο foo, θα πρέπει να χρησιμοποιήσετε την εντολή system("rm -r foo") Προσοχή: Αν προγραμματίζετε σε περιβάλλον Ms-Windows, θα πρέπει να χρησιμοποιήσετε τις αντίστοιχες εντολές system("del /q foo") και system("rmdir foo") Σε γενικές γραμμές, η συνάρτηση main() του dbdestroy θα έχει την ακόλουθη μορφή: int main(int argc, char* argv[]){ char command[80]; char* dbname; if(argc!=2){ printf("χρήση: %s dbdestroy \n",argv[0]); return(1); dbname=argv[1];
sprintf(command,"rm -r %s",dbname); system(command); return 0; 3 - minirel Το πρόγραμμα minirel χρησιμοποιείται για την διαχείριση των δεδομένων (πχ δημιουργία σχέσεων) που υπάρχουν σε μία βάση Σύνταξη: Η λειτουργία του προγράμματος είναι εξαιρετικά απλή Το πρόγραμμα αρχίζει την εκτέλεσή του όταν ο χρήστης πληκτρολογήσει minirel dbname στο κέλυφος του λειτουργικού συστήματος dbname είναι το όνομα της βάσης που θέλει ο χρήστης να χρησιμοποιήσει Στην συνέχεια το πρόγραμμα εκτελεί τις εντολές που ο χρήστης δίνει από το πληκτρολόγιο Η εκτέλεση του προγράμματος τερματίζεται όταν ο χρήστης πληκτρολογήσει quit Οι εντολές που μπορεί να δώσει ο χρήστης είναι οι εξής (τα τμήματα που είναι μέσα σε [] είναι προαιρετικά): Α) Βοηθητικές εντολές: 1) CREATE relname (attr1=format1,, attrn=formatn); Η εντολή αυτή δημιουργεί μία νέα σχέση με όνομα relname Η σχέση αυτή έχει N γνωρίσματα με ονόματα attr1, attr2, attrn Ο τύπος (format) του κάθε γνωρίσματος μπορεί να είναι 'i', 'f' ή 'cxxx' όπου ΧΧΧ είναι ένας αριθμός Εάν το format είναι 'i' ή 'f', τότε το αντίστοιχο γνώρισμα είναι ακέραιος ή πραγματικός αριθμός Αν το format είναι 'cxxx' τότε το αντίστοιχο γνώρισμα είναι κείμενο μήκους XXX χαρακτήρων 2) BUILDINDEX relname(attrname); Η εντολή αυτή δημιουργεί ένα ευρετήριο για τη σχέση relname, με βάση το γνώρισμα attrname 3) DESTROY relname; 4) QUIT; Η εντολή αυτή διαγράφει τη σχέση με όνομα relname Επιπλέον, διαγράφει και τα τυχόν ευρετήρια που σχετίζονται με αυτή τη σχέση Η εντολή αυτή προκαλεί τον τερματισμό του προγράμματος minirel Β) Εντολές χειρισμού δεδομένων: 1) SELECT [INTO relname] <TARGET_LIST> [WHERE <CONDITION>] Η εντολή αυτή τυπώνει το αποτέλεσμα ενός ερωτήματος, ή προαιρετικά εισάγει το αποτέλεσμα αυτό σε μία καινούργια σχέση με όνομα relname Εάν λείπει το τμήμα [WHERE <CONDITION>] της εντολής, τότε ανασύρονται όλες οι εγγραφές της σχέσης που δηλώνει το TARGET_LIST Το TARGET_LIST έχει τη μορφή (relname1attr1, relname2attr2,,relnamenattrn) To TARGET_LIST καθορίζει ποια γνωρίσματα πρέπει να περιληφθούν στην απάντηση της εντολής SELECT Όλα τα γνωρίσματα πρέπει να προέρχονται από την ίδια σχέση, εκτός και αν το
CONDITION υποδηλώνει ζεύξη σχέσεων, στην οποία περίπτωση, τα γνωρίσματα μπορούν να ανήκουν σε οποιαδήποτε από τις δύο σχέσεις της ζεύξης Επομένως, αν το ερώτημα δεν περιέχει ζεύξη, τότε όλα τα relnamex είναι ίδια, ενώ στην αντίθετη περίπτωση, καθένα από τα relnamex αντιστοιχεί σε ένα από τα δύο ονόματα των σχέσεων που συμμετέχουν στη ζεύξη Σε ό,τι αφορά το CONDITION, αυτό έχει τη γενική μορφή relnameattr <OP> value, αν το ερώτημα είναι επιλογής, ή τη μορφή relname1attr1 <OP> relname2attr2, αν το ερώτημα είναι ζεύξης, ή τη μορφή relname1attr <OP1> value AND relname1attr1 <OP2> relname2attr2, αν το ερώτημα είναι επιλεκτικής ζεύξης Σε όλες τις περιπτώσεις, οι τελεστές OP,OP1,OP2 μπορούν να είναι '=', '<', '<=', '>', '>=' ή '!=' Στην περίπτωση που το ερώτημα είναι ερώτημα επιλογής, τότε το value είναι μία σταθερή τιμή όπως για παράδειγμα -5, 67, "foo" κλπ Σε αυτήν την περίπτωση, η έννοια της ερώτησης είναι να επιλεχθούν όλες οι εγγραφές που ικανοποιούν τη συνθήκη Αντίστοιχα, αν το ερώτημα είναι τύπου ζεύξης, τότε πρέπει να ανασυρθούν όλες οι εγγραφές και από τις δύο σχέσεις που πληρούν τη συνθήκη της ζεύξης Τέλος, αν το ερώτημα είναι τύπου επιλεκτικής ζεύξης, οι εγγραφές στο αποτέλεσμα πρέπει να ικανοποιούν και τις δύο συνθήκες 2) DELETE relname [WHERE attr <OP> value] Η εντολή αυτή διαγράφει όλες τις εγγραφές της σχέσης με όνομα relname οι οποίες ικανοποιούν το δοθέν κριτήριο Αν η εντολή δεν έχει τμήμα WHERE, τότε όλες οι εγγραφές της σχέσης πρέπει να διαγραφούν 3) INSERT relname (attr1=value1, attr2=value2,, attrn=valuen) Η εντολή αυτή εισάγει μία εγγραφή στη σχέση με όνομα relname Η σειρά με την οποία δίνονται τα γνωρίσματα είναι τυχαία, όμως σε όλα τα γνωρίσματα της σχέσης πρέπει να ανατίθεται τιμή Πληροφορίες υλοποίησης: Μέσα στην παρεχόμενη βιβλιοθήκη, υπάρχει μια συνάρτηση με όνομα yyparse Η ρουτίνα αυτή αναλαμβάνει να χειριστεί την εισαγωγή των εντολών και την κλήση των κατάλληλων συναρτήσεων (τις οποίες θα υλοποιήσετε εσείς) που υλοποιούν τις εντολές αυτές Για παράδειγμα, αν ο χρήστης πληκτρολογήσει την εντολή DESTROY foo τότε η συνάρτηση yyparse αυτόματα θα καλέσει την συνάρτηση UT_destroy() η οποία θα πρέπει να διαγράψει τη σχέση foo Επομένως, η εργασία σας για την υλοποίηση του προγράμματος minirel περιορίζεται στην υλοποίηση αυτών των συναρτήσεων που καλούνται αυτόματα από την yyparse Σε γενικές γραμμές, η συνάρτηση main του minirel θα έχει την εξής μορφή: int main(int argc, char* argv[]){ char command[80]; char* dbname; if(argc!=2){ printf("χρήση: %s minirel \n",argv[0]); return(1); dbname=argv[1]; /* Αρχικοποίηση προγράμματος */ /* και άνοιγμα σχέσεων καταλόγου */
/* Κλήση της συνάρτησης yyparse ή οποία */ /* θα καλεί αυτόματα τις συναρτήσεις σας */ yyparse(); /* Κλείσιμο προγράμματος και */ /* κλείσιμο των σχέσεων καταλόγου αν */ /* ο χρήστης δεν πληκτρολόγησε την */ /* εντολή QUIT */ return(0); Συμβολομεταφραστής Μέσα στην παρεχόμενη βιβλιοθήκη, υπάρχει μια συνάρτηση με το όνομα yyparse η οποία δεν δέχεται και δεν επιστρέφει κανένα όρισμα (ορίζεται στο parsertabh): void yyparse(void) Η ρουτίνα αυτή αναλαμβάνει να χειριστεί την εισαγωγή των εντολών στο πρόγραμμα minirel και την κλήση των κατάλληλων συναρτήσεων (τις οποίες θα υλοποιήσετε εσείς) που υλοποιούν της εντολές αυτές Για παράδειγμα, αν ο χρήστης πληκτρολογήσει στο minirel την εντολή DESTROY foo τότε η συνάρτηση yyparse αυτόματα θα καλέσει την συνάρτηση UT_destroy η οποία θα πρέπει να διαγράψει τη σχέση foo Επομένως, η εργασία σας για την υλοποίηση του προγράμματος minirel περιορίζεται στην υλοποίηση αυτών των συναρτήσεων που καλούνται αυτόματα από την yyparse Οι περισσότερες συναρτήσεις που καλεί η yyparse (ορίζονται στην επόμενη ενότητα) δέχονται δύο ορίσματα, το argc και το argv To argv είναι ένας πίνακας με argc+1 στοιχεία Τα στοιχεία αυτά είναι δείκτες σε κείμενο (δηλ char*) Κατά σύμβαση, το πρώτο στοιχείο του argv είναι το όνομα της εντολής που πρέπει να εκτελεσθεί, ενώ το τελευταίο στοιχείο είναι NULL Ο πίνακας argv έχει όλα τα απαιτούμενα στοιχεία που χρειάζεται κάποιος για να καταλάβει τι πρέπει να κάνει η εντολή Η χρήση του πίνακα είναι υποχρεωτική καθώς ο αριθμός των παραμέτρων δεν είναι σταθερός Περιγραφή συναρτήσεων Για την υλοποίηση αυτών των προγραμμάτων θα πρέπει να χρησιμοποιήσετε ένα σύνολο από συναρτήσεις, οι οποίες και θα καλούν τις συναρτήσεις που έχετε ήδη φτιάξει στα κατώτερα επίπεδα για το χειρισμό εγγραφών ενός πίνακα και δευτερευόντων ευρετηρίων πίνακα Οι συναρτήσεις που απαιτούνται σε αυτό το επίπεδο υλοποιούν την ΕΚΤΕΛΕΣΗ των εντολών που υποστηρίζει το minirel Οι βοηθητικές συναρτήσεις δεδομένων υλοποιούν τις εντολές CREATE, BUILDINDEX, DESTROY, και QUIT Οι συναρτήσεις χειρισμού δεδομένων υλοποιούν τις εντολές SELECT, INSERT, και DELETE Οι συναρτήσεις του ενδιάμεσου αυτού επιπέδου δουλεύουν σε επίπεδο εγγραφών και
ευρετηρίων, παρέχοντας στον συμβολομεταφραστή ολοκληρωμένες λειτουργίες (πχ, δημιουργία μίας σχέσης) Μία σημαντική λειτουργία που υλοποιείται στο ενδιάμεσο επίπεδο είναι οι υπηρεσίες καταλόγου της βάσης Δεν εμφανίζονται στο παραπάνω σχήμα γιατί δεν παρέχεται αντίστοιχη διεπαφή προγραμματισμού προς το ανώτερο επίπεδο Αντίθετα, οι υπηρεσίες καταλόγου χρησιμοποιούνται εσωτερικά από τις δύο κατηγορίες συναρτήσεων δεδομένων Σε υψηλό επίπεδο, ο συμβολομεταφραστής αναλαμβάνει την μετατροπή των εντολών από απλό κείμενο σε μία εσωτερική δομημένη μορφή και την κλήση των κατάλληλων συναρτήσεων (του ενδιάμεσου επιπέδου) ώστε τελικά να εκτελεστούν οι εντολές που ο χρήστης δίνει στο σύστημα Ο συμβολομεταφραστής έχει και αυτός υλοποιηθεί και θα σας δοθεί Βοηθητικές συναρτήσεις δεδομένων Οι βοηθητικές συναρτήσεις δεδομένων υλοποιούν εντολές όπως η δημιουργία και καταστροφή σχέσεων, η παροχή πληροφοριών για τους καταλόγους της βάσης, κλπ Πρόκειται για συναρτήσεις οι οποίες καλούνται απευθείας από τον συμβολομεταφραστή, ανάλογα με τις εντολές που εισάγει ο χρήστης Ειδικότερα οι βοηθητικές συναρτήσεις δεδομένων είναι οι εξής: UT_create() int UT_create(int argc, char* argv[]) argv[0]="create" argv[1]=όνομα-σχέσης argv[2]=όνομα-πεδίου-1 argv[3]=φορμάτ-πεδίου-1 argv[argc-2]=όνομα-πεδίου-ν argv[argc-1]=φορμάτ-πεδίου-ν Η συνάρτηση αυτή καλείται από την yyparse όταν ο χρήστης πληκτρολογήσει μια εντολή CREATE Για τη δημιουργία της νέας σχέσης, η υλοποίησή σας θα πρέπει να χρησιμοποιεί την συνάρτηση HF_CreateFile Επιπλέον θα πρέπει να ενημερώνει τους καταλόγους (σχέσεις relcat και attrcat, κλπ) της βάσης Δηλαδή, θα πρέπει να εισάγει μία εγγραφή στη σχέση relcat, η οποία να περιγράφει τα χαρακτηριστικά της νέας σχέσης Επιπρόσθετα, θα πρέπει να εισάγει στη σχέση attrcat τις αντίστοιχες εγγραφές που θα περιγράφουν ένα-ένα τα γνωρίσματα της νέας σχέσης UT_buildindex() int UT_buildindex(int argc, char* argv[]) argv[0]="buildindex" argv[1]=όνομα-σχέσης argv[2]=όνομα-πεδίου argv[3]=null Η συνάρτηση αυτή καλείται από την yyparse όταν ο χρήστης πληκτρολογήσει μια εντολή BUILDINDEX Η υλοποίηση της εντολής αυτής θα πρέπει να δημιουργεί το ευρετήριο χρησιμοποιώντας την συνάρτηση AM_CreateIndex και να ενημερώνει κατάλληλα τους καταλόγους της βάσης
UT_destroy() int UT_destroy(int argc, char* argv[]) argv[0]="destroy" argv[1]=όνομα-σχέσης argv[2]=null Η συνάρτηση αυτή καλείται από την yyparse όταν ο χρήστης πληκτρολογήσει μια εντολή DESTROY Για την υλοποίηση αυτής της εντολής θα πρέπει να χρησιμοποιήσετε τις συναρτήσεις AM_DestroyIndex και HF_DestroyFile και να ενημερώσετε κατάλληλα τους καταλόγους της βάσης UT_quit() int UT_quit(void) Η συνάρτηση αυτή καλείται από την yyparse όταν ο χρήστης πληκτρολογήσει την εντολή QUIT Η συνάρτηση αυτή θα πρέπει να κλείνει όλες τις τυχόν ανοικτές σχέσεις και ευρετήρια (συμπεριλαμβανομένων και αυτών που αποτελούν τους καταλόγους της βάσης) και στην συνέχεια να καλεί την exit(1) Συναρτήσεις χειρισμού δεδομένων Οι Συναρτήσεις χειρισμού δεδομένων υλοποιούν τις εντολές του minirel που κάνουν εισαγωγή, διαγραφή και επιλογή εγγραφών από σχέσεις Πρόκειται για συναρτήσεις οι οποίες καλούνται απευθείας από τον συμβολομεταφραστή, ανάλογα με τις εντολές που εισάγει ο χρήστης Ειδικότερα οι συναρτήσεις χειρισμού δεδομένων είναι οι εξής: DM_select() int DM_select(int argc, char* argv[]) argv[0]="select" argv[1]=όνομα-σχέσης-αποτελέσματος /* Προαιρετικό στοιχείο */ argv[2]=πλήθος-γνωρισμάτων-τα-οποία-προβάλλονται /* ακέραιος */ argv[3]=όνομα-προβαλλόμενης-σχέσης-1 argv[4]=όνομα-προβαλλόμενου-πεδίου-1 argv[argc-6]=όνομα-προβαλλόμενης-σχέσης-ν argv[argc-5]=όνομα-προβαλλόμενου-πεδίου-ν argv[argc-4]=όνομα-σχέσης-επιλογής /* Τα στοιχεία */ argv[argc-3]=όνομα-πεδίου-επιλογής /* αυτά */ argv[argc-2]=τελεστής /* είναι */ argv[argc-1]=τιμή /* προαιρετικά */ Η συνάρτηση αυτή καλείται από την yyparse όταν ο χρήστης πληκτρολογήσει μια εντολή SELECT η οποία να μην είναι τύπου ζεύξης Σε ό,τι αφορά την υλοποίηση της εντολής αυτής, παρατηρήστε ότι όταν ζητείται να τυπωθούν τα αποτελέσματα στην οθόνη, μπορείτε να εισάγετε τα αποτελέσματα σε μία προσωρινή σχέση, στην συνέχεια να τυπώσετε τα περιεχόμενα της σχέσης αυτής και τέλος να την καταστρέψετε Επομένως, μπορείτε να χειρισθείτε την περίπτωση που τα αποτελέσματα πρέπει να εισαχθούν σε μία σχέση ως μια ειδίκευση της περίπτωσης που τα αποτελέσματα πρέπει απλά να τυπωθούν Καθώς θα σαρώνετε τις εγγραφές για να επιλέξετε αυτές που ικανοποιούν το κριτήριο της επιλογής (αν
βέβαια υπάρχει), θα πρέπει ταυτόχρονα να κάνετε και τον περιορισμό πάνω στα γνωρίσματα που τελικά θα υπάρχουν στην απάντηση της ερώτησης Δεν πρέπει να κάνετε πρώτα την επιλογή των εγγραφών και στην συνέχεια να κάνετε τον περιορισμό πάνω στα γνωρίσματα που υπάρχουν στην εντολή Αν υπάρχει ευρετήριο πάνω στο όνομα-πεδίου-επιλογής τότε θα πρέπει υποχρεωτικά να το χρησιμοποιήσετε εφόσον ο τελεστής υποστηρίζεται από τον τύπο του ευρετηρίου Τέλος, προσοχή θέλει το γεγονός ότι το στοιχείο τιμή (όπως επίσης και το στοιχείο τελεστής) είναι δείκτης σε κείμενο, δηλαδή θα πρέπει εσείς να κάνετε την μετατροπή στον σωστό τύπο DM_join() int DM_join(int argc, char* argv[]) argv[0]="join" argv[1]=όνομα-σχέσης-αποτελέσματος /* Προαιρετικό στοιχείο */ argv[2]=πλήθος-γνωρισμάτων-τα-οποία-προβάλλονται /* ακέραιος */ argv[3]=όνομα-προβαλλόμενης-σχέσης-1 argv[4]=όνομα-προβαλλόμενου-πεδίου-1 argv[argc-7]=όνομα-προβαλλόμενης-σχέσης-ν argv[argc-6]=όνομα-προβαλλόμενου-πεδίου-ν argv[argc-5]=όνομα-σχέσης-ζεύξης-1 argv[argc-4]=όνομα-πεδίου-ζεύξης-1 argv[argc-3]=τελεστής argv[argc-2]=όνομα-σχέσης-ζεύξης-2 argv[argc-1]=όνομα-πεδίου-ζεύξης-2 Η συνάρτηση αυτή καλείται από την yyparse όταν ο χρήστης πληκτρολογήσει μια εντολή SELECT η οποία είναι τύπου ζεύξης Για την υλοποίηση της συνάρτησης αυτής ισχύουν οι ίδιες παρατηρήσεις που δίνονται και για την συνάρτηση DM_select Επιπλέον, για τη ζεύξη θα πρέπει να χρησιμοποιηθεί ο αλγόριθμος εμφωλιασμένων βρόχων Αν υπάρχει ευρετήριο σε κάποιο από τα γνωρίσματα ζεύξης θα πρέπει επίσης υποχρεωτικά να χρησιμοποιηθεί εφόσον ο τελεστής υποστηρίζεται από τον τύπο του ευρετηρίου DM_selectivejoin() int DM_selectivejoin(int argc, char* argv[]) argv[0]="selectivejoin" argv[1]=όνομα-σχέσης-αποτελέσματος /* Προαιρετικό στοιχείο */ argv[2]=πλήθος-γνωρισμάτων-τα-οποία-προβάλλονται /* ακέραιος */ argv[3]=όνομα-προβαλλόμενης-σχέσης-1 argv[4]=όνομα-προβαλλόμενου-πεδίου-1 argv[argc-11]=όνομα-προβαλλόμενης-σχέσης-ν argv[argc-10]=όνομα-προβαλλόμενου-πεδίου-ν argv[argc-9]=όνομα-σχέσης-επιλογής argv[argc-8]=όνομα-πεδίου-επιλογής argv[argc-7]=τελεστής επιλογής argv[argc-6]=τιμή argv[argc-5]=όνομα-σχέσης-ζεύξης-1 /* ίδιο με argv[argc-9] */ argv[argc-4]=όνομα-πεδίου-ζεύξης-1 argv[argc-3]=τελεστής ζεύξης
argv[argc-2]=όνομα-σχέσης-ζεύξης-2 argv[argc-1]=όνομα-πεδίου-ζεύξης-2 Η συνάρτηση αυτή καλείται από την yyparse όταν ο χρήστης πληκτρολογήσει μια εντολή SELECT η οποία είναι τύπου επιλεκτικής ζεύξης Για την υλοποίηση της συνάρτησης αυτής ισχύουν όλες οι παρατηρήσεις που δίνονται και για τις συναρτήσεις DM_select και DM_join (πχ, εμφωλιασμένοι βρόχοι, υποχρεωτική χρήση ευρετηρίων αν υπάρχουν και είναι χρήσιμα, κτλ) Επιπλέον, με βάση μια εκτίμηση κόστους, θα πρέπει να επιλέξει το ποιά σχέση θα είναι εσωτερική και ποιά εξωτερική Αν επιλεγεί η σχέση επιλογής ως εξωτερική, τότε η συνάρτηση θα πρέπει να καλέσει την DM_pipeline για την εκτέλεση της επιλεκτικής ζεύξης Αντίθετα, αν επιλεγεί η άλλη σχέση ως εξωτερική, τότε η συνάρτηση θα πρέπει να καλέσει την DM_select για να δημιουργήσει μια προσωρινή σχέση με το αποτέλεσμα της επιλογής και μετά θα καλέσει την DM_join για την ζεύξη της εξωτερικής σχέσης με την προσωρινή αυτή σχέση DM_pipeline() int DM_pipeline(int argc, char* argv[]) argv[0]="pipeline" argv[1]=όνομα-σχέσης-αποτελέσματος /* Προαιρετικό στοιχείο */ argv[2]=πλήθος-γνωρισμάτων-τα-οποία-προβάλλονται /* ακέραιος */ argv[3]=όνομα-προβαλλόμενης-σχέσης-1 argv[4]=όνομα-προβαλλόμενου-πεδίου-1 argv[argc-11]=όνομα-προβαλλόμενης-σχέσης-ν argv[argc-10]=όνομα-προβαλλόμενου-πεδίου-ν argv[argc-9]=όνομα-σχέσης-επιλογής argv[argc-8]=όνομα-πεδίου-επιλογής argv[argc-7]=τελεστής επιλογής argv[argc-6]=τιμή argv[argc-5]=όνομα-σχέσης-ζεύξης-1 /* ίδιο με argv[argc-9] */ argv[argc-4]=όνομα-πεδίου-ζεύξης-1 argv[argc-3]=τελεστής ζεύξης argv[argc-2]=όνομα-σχέσης-ζεύξης-2 argv[argc-1]=όνομα-πεδίου-ζεύξης-2 Η συνάρτηση αυτή καλείται από την DM_selectivejoin όταν αποφασίζει ότι η σχέση της επιλογής θα χρησιμοποιηθεί ως εξωτερική για τους εμφωλιασμένους βρόχους που θα υλοποιήσουν τη ζεύξη Στην περίπτωση αυτή, η συνάρτηση θα σαρώνει την εξωτερική σχέση, θα εφαρμόζει την επιλογή, και μόνο για τις εγγραφές που την ικανοποιούν θα προχωρά στην σάρωση της εσωτερικής σχέσης και τον έλεγχο της συνθήκης της ζεύξης Επιπλέον, αν δε χρησιμοποιείται ευρετήριο για την εσωτερική σχέση, η εσωτερική σάρωση θα πραγματοποιείται μια φορά για κάθε ομάδα από εγγραφές της εξωτερικής σχέσης που ικανοποιούν την επιλογή και γεμίζουν ένα μπλοκ DM_delete() int DM_delete(int argc, char* argv[]) argv[0]="delete" argv[1]=όνομα-σχέσης argv[argc-3]=όνομα-πεδίου /* Τα στοιχεία */ argv[argc-2]=τελεστής /* αυτά είναι */ argv[argc-1]=τιμή /* προαιρετικά */
Η συνάρτηση αυτή καλείται από την yyparse όταν ο χρήστης πληκτρολογήσει μια εντολή DELETE Για την υλοποίησή της θα πρέπει να χρησιμοποιήσετε τις συναρτήσεις AM_DeleteEntry και ΗF_DeleteRec Αν υπάρχει ευρετήριο στο γνώρισμα που χρησιμοποιείται για την επιλογή των εγγραφών θα πρέπει υποχρεωτικά να χρησιμοποιηθεί εφόσον ο τελεστής υποστηρίζεται από τον τύπο του ευρετηρίου DM_insert() int DM_insert(int argc, char* argv[]) argv[0]="insert" argv[1]=όνομα-σχέσης argv[2]=όνομα-πεδίου-1 argv[3]=τιμή-1 argv[4]=όνομα-πεδίου-2 argv[5]=τιμή-2 Η συνάρτηση αυτή καλείται από την yyparse όταν ο χρήστης πληκτρολογήσει μια εντολή INSERT Για την υλοποίησή της θα πρέπει να χρησιμοποιήσετε τις συναρτήσεις AM_InsertEntry και ΗF_InsertRec Κατάλογοι Ένα από τα ωραία πράγματα των σχεσιακών συστημάτων βάσεων δεδομένων είναι ότι η εσωτερική πληροφορία που κρατούν για το σχήμα κάθε βάσης (τους πίνακες, τα πεδία τους, τα ευρετήριά τους, κτλ) διατηρείται και αυτή σε πίνακες Οι πίνακες αυτοί ανήκουν στο ίδιο το σύστημα και λέγονται κατάλογοι Υπάρχουν πάντα δύο βασικοί κατάλογοι: relcat και attrcat Και οι δύο αυτοί πίνακες δημιουργούνται όταν η εντολή dbcreate dbname εκτελείται Μπαίνουν στο φάκελο dbname που δημιουργείται, καθότι κάθε βάση έχει το δικό της σχήμα Πρέπει να χρησιμοποιήσετε την HF_CreateFile για να δημιουργήσετε τους δύο πίνακες ώστε να μπορείτε μετά να τους διαχειριστείτε με τις κανονικές ρουτίνες που έχετε φτιάξει για τους υπόλοιπους πίνακες Για παράδειγμα, μπορείτε αν θέλετε να ορίσετε και ευρετήρια πάνω σε κάποια πεδία των καταλόγων ώστε οι πρόσβαση σ' αυτούς να είναι γρήγορη Αυτό σημαίνει ότι το πρόγραμμα C που θα φτιάξετε για την dbcreate πρέπει να μεταφραστεί μαζί με τα επίπεδα HF και AM Ο πίνακας relcat θα χρησιμοποιηθεί για να κρατά στοιχεία για κάθε πίνακα μιας βάσης, συμπεριλαμβανομένων και όποιων στατιστικών κρίνετε ότι χρειάζεστε για την επιλογή πλάνου εκτέλεσης κάποιων τελεστών Κάθε εγγραφή του πίνακα relcat θα πρέπει να έχει περίπου την εξής δομή αν την εκφράζαμε σε μορφή C: typedef struct { char relname[maxname]; /* όνομα πίνακα */ int relwidth; /* εύρος εγγραφής πίνακα σε bytes */ int attrcnt; /* αριθμός πεδίων εγγραφής */ int indexcnt; /* αριθμός ευρετηρίων πίνακα */ int blockcnt; /* αριθμός μπλοκ πίνακα */ reldesc; Ο πίνακας attrcat θα χρησιμοποιηθεί για να κρατά στοιχεία για κάθε πεδίο κάθε πίνακα μιας βάσης, συμπεριλαμβανομένων και πάλι των απαραίτητων απλών στατιστικών Κάθε εγγραφή του πίνακα attrcat θα
πρέπει να έχει περίπου την εξής δομή αν την εκφράζαμε σε μορφή C: typedef struct { char relname[maxname]; /* όνομα πίνακα */ char attrname[maxname]; /* όνομα πεδίου του πίνακα */ int offset; /* απόσταση αρχής πεδίου από την αρχή της εγγραφής σε bytes */ int attrlength; /* μήκος πεδίου σε bytes */ char attrtype; /* τύπος πεδίου ('i', 'f', ή 'c' */ int valuecnt; /* αριθμός διαφορετικών τιμών πεδίου */ int indexed; /* TRUE αν το πεδίο έχει ευρετήριο */ int indexno; /* αύξων αριθμός του ευρετηρίου αν indexed=true */ attrdesc; Παρατήρηση: Όταν δημιουργείτε τους καταλόγους, να χρησιμοιήσετε την συνάρτηση sizeof στις παραπάνω δομές για να βρείτε το σωστό μήκος εγγραφής των καταλόγων Ο συμβολομετραφραστής της C προσθέτει μερικές φορές χώρο σε δομές όπως οι παραπάνω για να εγγυηθεί ότι τα δεδομένα θα ξεκινούν από όρια λέξεων στη μνήμη του, οπότε αν δεν χρησιμοποιήσετε την sizeof ίσως προκύψουν προβλήματα Τα παρακάτω παραδείγματα δείχνουν πώς πρέπει να χρησιμοποιούνται οι κατάλογοι Όταν καλείται το minirel για πρώτη φορά, η παρακάτω ακολουθία κλήσεων πρέπει να εκτελείται: #define RELCAT "relcat" #define ATTRCAT "attrcat" relcatfiledesc = HF_OpenFile(RELCAT); if (relcatfiledesc < 0) {printf ("Error in opening relcat Error = %d\n", relcatfiledesc); exit(); attrcatfiledesc = HF_OpenFile(ATTRCAT); if (attrcatfiledesc < 0) {printf ("Error in opening attrcat Error = %d\n", attrcatfiledesc); exit(); Αν όλα πάνε καλά, θα πρέπει να μείνουν αυτά τα αρχεία συνεχώς ανοιχτά όσο το σύστημα είναι ενεργό Βεβαιωθείτε βέβαια ότι όταν υλοιποιείτε την quit, ανάλογα καλείται και η HF_CloseFile σε κάθε ένα από τα δύο παραπάνω αναγνωριστικά πριν κλείσει το σύστημα Η προσπέλαση στους καταλόγους είναι αναπόσπαστο κομμάτι σχεδόν όλων των εντολών Κάθε φορά, θα πρέπει να επιλέγονται οι εγγραφές τους που αντιστοιχούν στα ζητούμενα πεδία και στους ζητούμενους πίνακες και να χρησιμοποιούντα τα στοιχεία τους στις κλήσεις των ρουτινών των διαφόρων επιπέδων του κώδικα Μάλλον θα σας βοηθήσει να γράψετε μια συνάρτηση που να δέχεται ένα όνομα πίνακα και ένα όνομα πεδίου του και επιστρέφει κάποια περιγραφή του πεδίου - κάτι τέτοιο θα χρειαστείτε να το κάνετε πολλές φορές Διαδικαστικές Λεπτομέρειες Όλα τα διαδικαστικά θέματα καθώς και θέματα ελέγχου σφαλμάτων παραμένουν όπως και στα προηγούμενα μέρη της εργασίας Το τί ακριβώς θα παραδόσετε, πού θα βρείτε τα αρχεία με τα οποία θα ελέγξετε τελικά το πρόγραμμά σας, κτλ, θα ανακοινωθεί πολύ σύντομα Τα προγράμματά σας θα πρέπει να δουλεύουν στα μηχανήματα του τμήματος (είτε στα sun (Unix) είτε στα pc (Windows)) ώστε να μπορούν να ελεγχθούν Ακόμη και αν δουλέψετε σε δικούς σας υπολογιστές θα πρέπει να βεβαιωθείτε ότι το τελικό αποτέλεσμα δουλεύει και εδώ τοπικά Η έκδοση της C που θα
χρησιμοποιήσετε πρέπει να έχει ακέραιους των 4 bytes και να επιτρέπει αλλαγή ερμηνείας τύπου δεδομένων (type casting) Όλα τα μέρη της εργασίας αυτής θα υλοποιηθούν από ομάδες των δύο ή τριών φοιτητών, οι οποίες εκτός σοβαρών περιπτώσεων πρέπει να είναι οι ίδιες που παρέδωσαν και τα δύο πρώτα μέρη της εργασίας Αν έχει υπάρξει η αλλαγή, ενημερώστε τον κo Πανταζάρα