From 31c05d869f83a064a835aa20416064df3a1f815a Mon Sep 17 00:00:00 2001 From: Michael Spang Date: Fri, 26 Oct 2007 00:05:05 -0400 Subject: [PATCH] Remove remnants of SQL interface --- debian/control | 2 +- debian/rules | 1 - etc/members.cf | 1 - etc/pgsql.cf | 11 - pylib/csc/adm/members.py | 4 +- pylib/csc/backends/db.py | 671 --------------------------------------- sql/initialize.sh | 13 - sql/structure.sql | 24 -- sql/verify_studentid.sql | 30 -- sql/verify_term.sql | 29 -- 10 files changed, 3 insertions(+), 783 deletions(-) delete mode 100644 etc/pgsql.cf delete mode 100644 pylib/csc/backends/db.py delete mode 100755 sql/initialize.sh delete mode 100644 sql/structure.sql delete mode 100644 sql/verify_studentid.sql delete mode 100644 sql/verify_term.sql diff --git a/debian/control b/debian/control index 650bc4a..7b143b5 100644 --- a/debian/control +++ b/debian/control @@ -7,7 +7,7 @@ Standards-Version: 3.7.2 Package: ceo Architecture: any -Depends: python2.4, python-ldap, python-pygresql, python-urwid, krb5-user, less, python-pam, ${shlibs:Depends} +Depends: python2.4, python-ldap, python-urwid, krb5-user, less, python-pam, ${shlibs:Depends} Recommends: quota Description: Computer Science Club Administrative Utilities This package contains the CSC Electronic Office diff --git a/debian/rules b/debian/rules index f4af3e4..4fc6180 100755 --- a/debian/rules +++ b/debian/rules @@ -30,7 +30,6 @@ install: build usr/lib/csc usr/bin etc/ldap/schema dh_install pylib/* usr/lib/$(PYTHON)/site-packages/ dh_install etc/* etc/csc/ - dh_install sql/* usr/share/csc/ dh_install bin/ceo bin/ceo-old bin/addhomedir bin/ceoquery bin/csc-chsh bin/csc-chfn usr/lib/csc/ dh_install build/ceo build/ceo-old build/addhomedir build/ceoquery build/csc-chsh build/csc-chfn usr/bin/ diff --git a/etc/members.cf b/etc/members.cf index a6ce533..59ac4a6 100644 --- a/etc/members.cf +++ b/etc/members.cf @@ -1,6 +1,5 @@ # /etc/csc/members.cf: CSC Members Configuration -include /etc/csc/pgsql.cf include /etc/csc/ldap.cf ### Validation Tuning ### diff --git a/etc/pgsql.cf b/etc/pgsql.cf deleted file mode 100644 index 021b962..0000000 --- a/etc/pgsql.cf +++ /dev/null @@ -1,11 +0,0 @@ -# /etc/csc/pgsql.cf: PostgreSQL database configuration - -### Database Configuration ### - -# server = "localhost" -server = "" -database = "ceo" - -# not used -user = "ceo" -password = "secret" diff --git a/pylib/csc/adm/members.py b/pylib/csc/adm/members.py index 2aef4c2..2dda57f 100644 --- a/pylib/csc/adm/members.py +++ b/pylib/csc/adm/members.py @@ -76,14 +76,14 @@ class NoSuchMember(MemberException): ldap_connection = ldapi.LDAPConnection() def connect(): - """Connect to PostgreSQL.""" + """Connect to LDAP.""" load_configuration() ldap_connection.connect(cfg['server_url'], cfg['admin_bind_dn'], cfg['admin_bind_pw'], cfg['users_base'], cfg['groups_base']) def disconnect(): - """Disconnect from PostgreSQL.""" + """Disconnect from LDAP.""" ldap_connection.disconnect() diff --git a/pylib/csc/backends/db.py b/pylib/csc/backends/db.py deleted file mode 100644 index 6d74a5e..0000000 --- a/pylib/csc/backends/db.py +++ /dev/null @@ -1,671 +0,0 @@ -""" -Database Backend Interface - -This module is intended to be a thin wrapper around CEO database operations. -Methods on the connection class correspond in a straightforward way to SQL -queries. These methods may restructure and clean up query output but may make -no other assumptions about its content or purpose. - -This module makes use of the PyGreSQL Python bindings to libpq, -PostgreSQL's native C client library. -""" -import pgdb - - -class DBException(Exception): - """Exception class for database-related errors.""" - pass - - -class DBConnection(object): - """ - A connection to CEO's backend database. All database queries - and updates are made via this class. - - Exceptions: (all methods) - DBException - on database query failure - - Note: Updates will never take place until commit() is called. - - Note: In the event that any method of this class raises a - DBException and that exception is caught, rollback() - must be called before further queries will be successful. - - Example: - connection = DBConnection() - connection.connect("localhost", "ceo") - - # make queries and updates, i.e. - connection.insert_member("Calum T. Dalek") - - connection.commit() - connection.disconnect() - """ - - def __init__(self): - self.cnx = None - self.cursor = None - - - def connect(self, hostname=None, database=None, username=None, password=None): - """ - Establishes the connection to CEO's PostgreSQL database. - - Parameters: - hostname - hostname:port to connect to - database - name of database - username - user to authenticate as - password - password of username - """ - - if self.cnx: raise DBException("unable to connect: already connected") - - try: - self.cnx = pgdb.connect(host=hostname, database=database, - user=username, password=password) - self.cursor = self.cnx.cursor() - except pgdb.Error, e: - raise DBException("unable to connect: %s" % e) - - - def disconnect(self): - """Closes the connection to CEO's PostgreSQL database.""" - - if self.cursor: - self.cursor.close() - self.cursor = None - - if self.cnx: - self.cnx.close() - self.cnx = None - - - def connected(self): - """Determine whether the connection has been established.""" - - return self.cnx is not None - - - def commit(self): - """Commits the current transaction and starts a new one.""" - - self.cnx.commit() - - - def rollback(self): - """Aborts the current transaction.""" - - self.cnx.rollback() - - - - ### Member-related methods ### - - def select_members(self, sql, params=None): - """ - Retrieves a list CSC members selected by given SQL statement. - - This is a helper function that should generally not be called directly. - - Parameters: - sql - the SELECT sql statement - params - parameters for the SQL statement - - The sql statement must select the six columns - (memberid, name, studentid, program, type, userid) - from the members table in that order. - - Returns: a memberid-keyed dictionary whose values are - column-keyed dictionaries with member attributes - """ - - # retrieve a list of all members - try: - self.cursor.execute(sql, params) - members_list = self.cursor.fetchall() - except pgdb.Error, e: - raise DBException("SELECT statement failed: %s" % e) - - # build a dictionary of dictionaries from the result (a list of lists) - members_dict = {} - for member in members_list: - members_dict[member[0]] = { - 'memberid': member[0], - 'name': member[1], - 'studentid': member[2], - 'program': member[3], - 'type': member[4], - 'userid': member[5], - } - - return members_dict - - - def select_single_member(self, sql, params=None): - """ - Retrieves a single member by memberid. - - This is a helper function that should generally not be called directly. - - See: self.select_members() - - Returns: a column-keyed dictionary with member attributes, or - None if no member matching member exists - """ - - # retrieve the member - results = self.select_members(sql, params) - - # too many members returned - if len(results) > 1: - raise DBException("multiple members selected: sql='%s' params=%s" % (sql, repr(params))) - - # no such member - elif len(results) < 1: - return None - - # return the single match - memberid = results.keys()[0] - return results[memberid] - - - def select_all_members(self): - """ - Retrieves a list of all CSC members (past and present). - - See: self.select_members() - - Example: connection.select_all_members() -> { - 0: { 'memberid': 0, 'name': 'Calum T. Dalek' ...} - 3349: { 'memberid': 3349, 'name': 'Michael Spang' ...} - ... - } - """ - sql = "SELECT memberid, name, studentid, program, type, userid FROM members" - return self.select_members(sql) - - - def select_members_by_name(self, name_re): - """ - Retrieves a list of all CSC members whose name matches name_re. - - See: self.select_members() - - Example: connection.select_members_by_name('Michael') -> { - 3349: { 'memberid': 3349, 'name': 'Michael Spang' ...} - ... - } - """ - sql = "SELECT memberid, name, studentid, program, type, userid FROM members WHERE name ~* %s" - params = [ str(name_re) ] - - return self.select_members(sql, params) - - - def select_members_by_term(self, term): - """ - Retrieves a list of all CSC members who were members in the specified term. - - See: self.select_members() - - Example: connection.select_members_by_term('f2006') -> { - 3349: { 'memberid': 3349, 'name': 'Michael Spang' ...} - ... - } - """ - sql = "SELECT members.memberid, name, studentid, program, type, userid FROM members JOIN terms ON members.memberid=terms.memberid WHERE term=%s" - params = [ str(term) ] - - return self.select_members(sql, params) - - - def select_member_by_id(self, memberid): - """ - Retrieves a single member by memberid. - - See: self.select_single_member() - - Example: connection.select_member_by_id(0) -> - { 'memberid': 0, 'name': 'Calum T. Dalek' ...} - """ - sql = "SELECT memberid, name, studentid, program, type, userid FROM members WHERE memberid=%d" - params = [ int(memberid) ] - - return self.select_single_member(sql, params) - - - def select_member_by_userid(self, username): - """ - Retrieves a single member by UNIX account username. - - See: self.select_single_member() - - Example: connection.select_member_by_userid('ctdalek') -> - { 'memberid': 0, 'name': 'Calum T. Dalek' ...} - """ - sql = "SELECT memberid, name, studentid, program, type, userid FROM members WHERE userid=%s" - params = [ username ] - - return self.select_single_member(sql, params) - - - def select_member_by_studentid(self, studentid): - """ - Retrieves a single member by student id number. - - See: self.select_single_member() - - Example: connection.select_member_by_studentid('nnnnnnnn') -> - { 'memberid': 3349, 'name': 'Michael Spang' ...} - """ - sql = "SELECT memberid, name, studentid, program, type, userid FROM members WHERE studentid=%s" - params = [ studentid ] - - return self.select_single_member(sql, params) - - - def insert_member(self, name, studentid=None, program=None, mtype='user', userid=None): - """ - Creates a member with the specified attributes. - - Parameters: - name - full name of member - studentid - student id number - program - program of study - mtype - member type - userid - account id - - Example: connection.insert_member('Michael Spang', '99999999', 'Math/CS') -> 3349 - - Returns: a memberid of created user - """ - try: - # retrieve the next memberid - sql = "SELECT nextval('memberid_seq')" - self.cursor.execute(sql) - result = self.cursor.fetchone() - memberid = result[0] - - # insert the member - sql = "INSERT INTO members (memberid, name, studentid, program, type, userid) VALUES (%d, %s, %s, %s, %s, %s)" - params = [ memberid, name, studentid, program, mtype, userid ] - self.cursor.execute(sql, params) - - return memberid - except pgdb.Error, e: - raise DBException("failed to create member: %s" % e) - - - def delete_member(self, memberid): - """ - Deletes a member. Note that a member cannot - be deleted until it has been unregistered from - all terms. - - Parameters: - memberid - the member id number to delete - - Example: connection.delete_member(3349) - """ - sql = "DELETE FROM members WHERE memberid=%d" - params = [ memberid ] - - try: - self.cursor.execute(sql, params) - except pgdb.Error, e: - raise DBException("DELETE statement failed: %s" %e) - - - def update_member(self, member): - """ - Modifies member attributes. - - Parameters: - member - a column-keyed dictionary with the new state of the member. - member['memberid'] must be present. ommitted columns - will not be changed. None is NULL. - - Returns: the full new state of the member as a column-keyed dictionary - - Example: connection.update_member({ - 'memberid': 3349, - 'name': 'Michael C. Spang', - 'program': 'CS!' - }) -> { - 'memberid': 3349, - 'name': 'Michael C. Spang', - 'program': CS!', - 'studentid': '99999999' # unchanged - } - - Equivalent Example: - member = connection.select_member_by_id(3349) - member['name'] = 'Michael C. Spang' - member['program'] = 'CS!' - connection.update_member(member) -> { see above } - """ - try: - - # memberid to update - memberid = member['memberid'] - - # retrieve current state of member - member_state = self.select_member_by_id(memberid) - - # build a list of changes to make - changes = [] - for column in member.keys(): - if member[column] != member_state[column]: - - # column's value must be updated - changes.append( (column, member[column]) ) - member_state[column] = member[column] - - # no changes? - if len(changes) < 1: - return member_state - - # make the necessary changes in an update statement - changes = zip(*changes) - sql = "UPDATE members SET " + ", ".join(["%s=%%s"] * len(changes[0])) % changes[0] + " WHERE memberid=%d" - params = changes[1] + ( memberid, ) - self.cursor.execute(sql, params) - - return member_state - except pgdb.Error, e: - raise DBException("member update failed: %s" % e) - - - - ### Term-related methods ### - - def select_term(self, memberid, term): - """ - Determines whether a member is registered for a term. - - Parameters: - memberid - the member id number - term - the term to check - - Returns: a matching term, or None - - Example: connection.select_term(3349, 'f2006') -> 'f2006' - """ - sql = "SELECT term FROM terms WHERE memberid=%d AND term=%s" - params = [ memberid, term ] - - # retrieve matches - try: - self.cursor.execute(sql, params) - result = self.cursor.fetchall() - except pgdb.Error, e: - raise DBException("SELECT statement failed: %s" % e) - - if len(result) > 1: - raise DBException("multiple rows in terms with memberid=%d term=%s" % (memberid, term)) - elif len(result) == 0: - return None - else: - return result[0][0] - - - def select_terms(self, memberid): - """ - Retrieves a list of terms a member is registered for. - - Parameters: - memberid - the member id number - - Returns: a sorted list of terms - - Example: connection.select_terms(3349) -> ['f2006'] - """ - sql = "SELECT term FROM terms WHERE memberid=%d" - params = [ memberid ] - - # retrieve the list of terms - try: - self.cursor.execute(sql, params) - result = self.cursor.fetchall() - except pgdb.Error, e: - raise DBException("SELECT statement failed: %s" % e) - - result = [ row[0] for row in result ] - - return result - - - def insert_term(self, memberid, term): - """ - Registers a member for a term. - - Parameters: - memberid - the member id number to register - term - string representation of the term - - Example: connection.insert_term(3349, 'f2006') - """ - sql = "INSERT INTO terms (memberid, term) VALUES (%d, %s)" - params = [ memberid, term ] - - try: - self.cursor.execute(sql, params) - except pgdb.Error, e: - raise DBException("INSERT statement failed: %s" % e) - - - def delete_term(self, memberid, term): - """ - Unregisters a member for a term. - - Parameters: - memberid - the member id number to register - term - string representation of the term - - Example: connection.delete_term(3349, 'f2006') - """ - sql = "DELETE FROM terms WHERE memberid=%d and term=%s" - params = [ memberid, term ] - - try: - self.cursor.execute(sql, params) - except pgdb.Error, e: - raise DBException("DELETE statement failed: %s" % e) - - - def delete_term_all(self, memberid): - """ - Unregisters a member for all registered terms. - - Parameters: - memberid - the member id number to unregister - - Example: connection.delete_term_all(3349) - """ - sql = "DELETE FROM terms WHERE memberid=%d" - params = [ memberid ] - - # retrieve a list of terms - try: - self.cursor.execute(sql, params) - except pgdb.Error, e: - raise DBException("DELETE statement failed: %s" % e) - - - ### Miscellaneous methods ### - - def trim_memberid_sequence(self): - """ - Sets the value of the member id sequence to the id of the newest - member. For use after testing to prevent large intervals of unused - memberids from developing. - - Note: this does nothing unless the most recently added member(s) have been deleted - """ - self.cursor.execute("SELECT setval('memberid_seq', (SELECT max(memberid) FROM members))") - - - -### Tests ### - -if __name__ == '__main__': - - from csc.common.test import * - - conffile = "/etc/csc/pgsql.cf" - - cfg = dict([map(str.strip, a.split("=", 1)) for a in map(str.strip, open(conffile).read().split("\n")) if "=" in a ]) - hostnm = cfg['server'][1:-1] - dbase = cfg['database'][1:-1] - - # t=test m=member s=student d=default e=expected u=updated - tmname = 'Test Member' - tmuname = 'Member Test' - tmsid = '00000004' - tmusid = '00000008' - tmprogram = 'Undecidable' - tmuprogram = 'Nondetermined' - tmtype = 'Untyped' - tmutype = 'Poly' - tmuserid = 'tmem' - tmuuserid = 'identifier' - tm2name = 'Test Member 2' - tm2sid = '00000005' - tm2program = 'Undeclared' - tm3name = 'T. M. 3' - dtype = 'user' - tmterm = 'w0000' - tm3term = 'f1112' - tm3term2 = 's1010' - - emdict = { 'name': tmname, 'program': tmprogram, 'studentid': tmsid, 'type': tmtype, 'userid': tmuserid } - emudict = { 'name': tmuname, 'program': tmuprogram, 'studentid': tmusid, 'type': tmutype, 'userid': tmuuserid } - em2dict = { 'name': tm2name, 'program': tm2program, 'studentid': tm2sid, 'type': dtype, 'userid': None } - em3dict = { 'name': tm3name, 'program': None, 'studentid': None, 'type': dtype, 'userid': None } - - test(DBConnection) - connection = DBConnection() - success() - - test(connection.connect) - connection.connect(hostnm, dbase) - success() - - test(connection.connected) - assert_equal(True, connection.connected()) - success() - - test(connection.insert_member) - tmid = connection.insert_member(tmname, tmsid, tmprogram, tmtype, tmuserid) - tm2id = connection.insert_member(tm2name, tm2sid, tm2program) - tm3id = connection.insert_member(tm3name) - assert_equal(True, int(tmid) >= 0) - assert_equal(True, int(tmid) >= 0) - success() - - emdict['memberid'] = tmid - emudict['memberid'] = tmid - em2dict['memberid'] = tm2id - em3dict['memberid'] = tm3id - - test(connection.select_member_by_id) - m1 = connection.select_member_by_id(tmid) - m2 = connection.select_member_by_id(tm2id) - m3 = connection.select_member_by_id(tm3id) - assert_equal(emdict, m1) - assert_equal(em2dict, m2) - assert_equal(em3dict, m3) - success() - - test(connection.select_all_members) - members = connection.select_all_members() - assert_equal(True, tmid in members) - assert_equal(True, tm2id in members) - assert_equal(True, tm3id in members) - assert_equal(emdict, members[tmid]) - success() - - test(connection.select_members_by_name) - members = connection.select_members_by_name(tmname) - assert_equal(True, tmid in members) - assert_equal(False, tm3id in members) - assert_equal(emdict, members[tmid]) - success() - - test(connection.select_member_by_userid) - assert_equal(emdict, connection.select_member_by_userid(tmuserid)) - success() - - test(connection.insert_term) - connection.insert_term(tmid, tmterm) - connection.insert_term(tm3id, tm3term) - connection.insert_term(tm3id, tm3term2) - success() - - test(connection.select_members_by_term) - members = connection.select_members_by_term(tmterm) - assert_equal(True, tmid in members) - assert_equal(False, tm2id in members) - assert_equal(False, tm3id in members) - success() - - test(connection.select_term) - assert_equal(tmterm, connection.select_term(tmid, tmterm)) - assert_equal(None, connection.select_term(tm2id, tmterm)) - assert_equal(tm3term, connection.select_term(tm3id, tm3term)) - assert_equal(tm3term2, connection.select_term(tm3id, tm3term2)) - success() - - test(connection.select_terms) - trms = connection.select_terms(tmid) - trms2 = connection.select_terms(tm2id) - assert_equal([tmterm], trms) - assert_equal([], trms2) - success() - - test(connection.delete_term) - assert_equal(tm3term, connection.select_term(tm3id, tm3term)) - connection.delete_term(tm3id, tm3term) - assert_equal(None, connection.select_term(tm3id, tm3term)) - success() - - test(connection.update_member) - connection.update_member({'memberid': tmid, 'name': tmuname}) - connection.update_member({'memberid': tmid, 'program': tmuprogram, 'studentid': tmusid }) - connection.update_member({'memberid': tmid, 'userid': tmuuserid, 'type': tmutype }) - assert_equal(emudict, connection.select_member_by_id(tmid)) - connection.update_member(emdict) - assert_equal(emdict, connection.select_member_by_id(tmid)) - success() - - test(connection.delete_term_all) - connection.delete_term_all(tm2id) - connection.delete_term_all(tm3id) - assert_equal([], connection.select_terms(tm2id)) - assert_equal([], connection.select_terms(tm3id)) - success() - - test(connection.delete_member) - connection.delete_member(tm3id) - assert_equal(None, connection.select_member_by_id(tm3id)) - negative(connection.delete_member, (tmid,), DBException, "delete of term-registered member") - success() - - test(connection.rollback) - connection.rollback() - assert_equal(None, connection.select_member_by_id(tm2id)) - success() - - test(connection.commit) - connection.commit() - success() - - test(connection.trim_memberid_sequence) - connection.trim_memberid_sequence() - success() - - test(connection.disconnect) - connection.disconnect() - assert_equal(False, connection.connected()) - connection.disconnect() - success() diff --git a/sql/initialize.sh b/sql/initialize.sh deleted file mode 100755 index 477a214..0000000 --- a/sql/initialize.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -# Initializes a database for CEO. - -# initialize the database -createdb $1 -createlang plpythonu $1 - -# initialize the tables -psql $1 < structure.sql - -# initialize check triggers -psql $1 < verify_studentid.sql -psql $1 < verify_term.sql diff --git a/sql/structure.sql b/sql/structure.sql deleted file mode 100644 index 14c6f76..0000000 --- a/sql/structure.sql +++ /dev/null @@ -1,24 +0,0 @@ --- Table structure for CEO's SQL database. - --- Usage: --- su postgres --- createdb ceo --- psql ceo < structure.sql - -CREATE SEQUENCE memberid_seq; - -CREATE TABLE members ( - memberid integer PRIMARY KEY DEFAULT nextval('memberid_seq') NOT NULL, - name character varying(50) NOT NULL, - studentid character varying(10) UNIQUE, - program character varying(50), - "type" character varying(10), - userid character varying(32) UNIQUE -); - - -CREATE TABLE terms ( - memberid integer NOT NULL, - term character(5) NOT NULL, - UNIQUE(memberid, term) -); diff --git a/sql/verify_studentid.sql b/sql/verify_studentid.sql deleted file mode 100644 index da2a849..0000000 --- a/sql/verify_studentid.sql +++ /dev/null @@ -1,30 +0,0 @@ --- PL/Python trigger to verify student ids for validity --- Dedicated to office staff who can't type student ids. - --- To (re)install: --- su postgres --- psql ceo < verify_studentid.sql - --- To uninstall: --- su postgres --- echo 'DROP FUNCTION verify_studentid() CASCADE' | psql ceo - -DROP FUNCTION verify_studentid() CASCADE; - -CREATE FUNCTION verify_studentid() RETURNS trigger AS ' - import re - - # update this line if the student id format changes - STUDENTID_REGEX = "^[0-9]{8}$" - - studentid = TD["new"]["studentid"] - if studentid and not re.match(STUDENTID_REGEX, studentid): - plpy.error("student id is invalid (%s)" % studentid) - -' LANGUAGE plpythonu; - -CREATE TRIGGER verify_studentid_insert BEFORE INSERT on members - FOR ROW EXECUTE PROCEDURE verify_studentid(); - -CREATE TRIGGER verify_studentid_update BEFORE UPDATE ON members - FOR ROW EXECUTE PROCEDURE verify_studentid(); diff --git a/sql/verify_term.sql b/sql/verify_term.sql deleted file mode 100644 index be1f3e4..0000000 --- a/sql/verify_term.sql +++ /dev/null @@ -1,29 +0,0 @@ --- PL/Python trigger to verify terms for validity - --- To (re)install: --- su postgres --- psql ceo < verify_term.sql - --- To uninstall: --- su postgres --- echo 'DROP FUNCTION verify_term() CASCADE' | psql ceo - -DROP FUNCTION verify_term() CASCADE; - -CREATE FUNCTION verify_term() RETURNS trigger AS ' - import re - - # update this line if the term format changes - TERM_REGEX = "^[wsf][0-9]{4}$" - - term = TD["new"]["term"] - if term and not re.match(TERM_REGEX, term): - plpy.error("term is invalid (%s)" % term) - -' LANGUAGE plpythonu; - -CREATE TRIGGER verify_term_insert BEFORE INSERT on terms - FOR ROW EXECUTE PROCEDURE verify_term(); - -CREATE TRIGGER verify_term_update BEFORE UPDATE ON terms - FOR ROW EXECUTE PROCEDURE verify_term();