From e55213a5a8ff7ecad60e81ae476f31f5d0ab8e51 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Mon, 17 May 1999 06:15:31 +0000 Subject: [PATCH] Re-add python. --- src/interfaces/python/ChangeLog | 65 + src/interfaces/python/README | 986 ++++++++++++++ src/interfaces/python/README.linux | 33 + src/interfaces/python/mkdefines | 11 + src/interfaces/python/pg.py | 237 ++++ src/interfaces/python/pgmodule.c | 2036 ++++++++++++++++++++++++++++ src/interfaces/python/pgsqldb.py | 46 + 7 files changed, 3414 insertions(+) create mode 100644 src/interfaces/python/ChangeLog create mode 100644 src/interfaces/python/README create mode 100644 src/interfaces/python/README.linux create mode 100755 src/interfaces/python/mkdefines create mode 100644 src/interfaces/python/pg.py create mode 100644 src/interfaces/python/pgmodule.c create mode 100644 src/interfaces/python/pgsqldb.py diff --git a/src/interfaces/python/ChangeLog b/src/interfaces/python/ChangeLog new file mode 100644 index 0000000000..4d988e6002 --- /dev/null +++ b/src/interfaces/python/ChangeLog @@ -0,0 +1,65 @@ +PyGreSQL changelog. +=================== + +This software is copyright (c) 1995, Pascal Andre (andre@via.ecp.fr) +Further copyright 1997, 1998 and 1999 by D'Arcy J.M. Cain (darcy@druid.net) +See file README for copyright information. + +Version 2.3 + - connect.host returns "localhost" when connected to Unix socket + (torppa@tuhnu.cutery.fi) + - Use PyArg_ParseTupleAndKeywords in connect() (torppa@tuhnu.cutery.fi) + - fixes and cleanups (torppa@tuhnu.cutery.fi) + - Fixed memory leak in dictresult() (terekhov@emc.com) + - Deprecated pgext.py - functionality now in pg.py + - More cleanups to the tutorial + - Added fileno() method - terekhov@emc.com (Mikhail Terekhov) + - added money type to quoting function + - Compiles cleanly with more warnings turned on + - Returns PostgreSQL error message on error + - Init accepts keywords (Jarkko Torppa) + - Convenience functions can be overridden (Jarkko Torppa) + - added close() method + +Version 2.2 + - Added user and password support thanks to Ng Pheng Siong + - Insert queries return the inserted oid + - Add new pg wrapper (C modile renamed to _pg) + - Wrapped database connection in a class. + - Cleaned up some of the tutorial. (More work needed.) + - Added version and __version__. Thanks to thilo@eevolute.com for + the suggestion. + +Version 2.1 + - return fields as proper Python objects for field type + - Cleaned up pgext.py + - Added dictresult method + +Version 2.0 (23/12/1997): + - updated code for PostgreSQL 6.2.1 and Python 1.5 + - reformatted code and converted to ANSI + - Changed name to PyGreSQL (from PyGres95) + - changed order of arguments to connect function + - Created new type pgqueryobject and moved certain methods to it. + - Added a print function for pgqueryobject + +Version 1.0b (4/11/1995): + - keyword support for connect function moved from library file to C code + and taken away from library. + - rewrote documentation + - bug fix in connect function + - enhancements in large objects interface methods + +Version 1.0a (30/10/1995) (limited release): + - module adapted to standard Python syntax + - keyword support for connect function in library file + - rewrote default parameters interface (internal use of strings) + - fixed minor bugs in module interface + - redefinition of error messages + +Version 0.9b (10/10/1995) (first public release): + - large objects implementation + - many bug fixes, enhancements, ... + +Version 0.1a (7/10/1995): + - basic libpq functions (SQL access) diff --git a/src/interfaces/python/README b/src/interfaces/python/README new file mode 100644 index 0000000000..35c0e3136c --- /dev/null +++ b/src/interfaces/python/README @@ -0,0 +1,986 @@ + +PyGreSQL - v2.3: PostgreSQL module for Python +============================================== + +0. Copyright notice +=================== + + PyGreSQL, version 2.3 + A Python interface for PostgreSQL database. + Written by D'Arcy J.M. Cain, darcy@druid.net
+ Based heavily on code written by Pascal Andre, andre@chimay.via.ecp.fr. + Copyright (c) 1995, Pascal ANDRE (andre@via.ecp.fr) + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose, without fee, and without a written agreement + is hereby granted, provided that the above copyright notice and this + paragraph and the following two paragraphs appear in all copies or in any + new file that contains a substantial portion of this file. + + IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, + SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, + ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE + AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE + AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, + ENHANCEMENTS, OR MODIFICATIONS. + + Further modifications copyright 1997, 1998 and 1999 by D'Arcy J.M. Cain + (darcy@druid.net) subject to the same terms and conditions as above. + +1. Presentation +=============== + +1.1. Introduction +----------------- + +PostgreSQL is a database system derived from Postgres4.2. It conforms to +(most of) ANSI SQL and offers many interesting capabilities (C dynamic linking +for functions or type definition, etc.). This package is copyright by the +Regents of the University of California, and is freely distributable. + +Python is an interpreted programming language. It is object oriented, simple +to use (light syntax, simple and straightforward statements), and has many +extensions for building GUIs, interfacing with WWW, etc. An intelligent web +browser (HotJava like) is currently under development (November 1995), and +this should open programmers many doors. Python is copyrighted by Stichting S +Mathematisch Centrum, Amsterdam, The Netherlands, and is freely distributable. + +PyGreSQL is a python module that interfaces to a PostgreSQL database. It +embeds the PostgreSQL query library to allow easy use of the powerful +PostgreSQL features from a Python script. + +PyGreSQL 2.0 was developed and tested on a NetBSD 1.3_BETA system. It is +based on the PyGres95 code written by Pascal Andre, andre@chimay.via.ecp.fr. +I changed the version to 2.0 and updated the code for Python 1.5 and +PostgreSQL 6.2.1. While I was at it I upgraded the code to use full ANSI +style prototypes and changed the order of arguments to connect. + + +1.2. Distribution files +----------------------- + + README - this file + Announce - announcement of this release + ChangeLog - changes that affected this package during its history + pgmodule.c - the C python module + pg.py - PyGreSQL DB class. + tutorial/ - demos directory + Content: basics.py, syscat.py, advanced.py, func.py and + pgtools.py. The samples here have been taken from the + PostgreSQL manual and were used for module testing. They + demonstrate some PostgreSQL features. Pgtools.py is an + add-in used for demonstration. + +1.3. Installation +----------------- + +* You first have to get and build Python and PostgreSQL. + +* PyGreSQL is implemented as two parts, a C module labeled _pg and a + Python wrapper called pg.py. This changed between 2.1 and 2.2. This + should not affect any existing programs but the installation is slightly + different. + +* Find the directory where your 'Setup' file lives (usually ??/Modules) and + copy or symlink the 'pgmodule.c' file there. + +* Add the following line to your Setup file + _pg pgmodule.c -I[pgInc] -L[pgLib] -lpq # -lcrypt # needed on some systems + where: + [pgInc] = path of the PostgreSQL include + [pgLib] = path of the PostgreSQL libraries + Some options may be added to this line: + -DNO_DEF_VAR - no default variables support + -DNO_DIRECT - no direct access methods + -DNO_LARGE - no large object support + -DNO_PQSOCKET - if running an older PostgreSQL + + Define NO_PQSOCKET if you are using a version of PostgreSQL before 6.4 + that does not have the PQsocket function. The other options will be + described in the next sections. + +* If you want a shared module, make sure that the "*shared*" keyword is + uncommented and add the above line below it. You used to need to install + your shared modules with "make sharedinstall but this no longer seems + to be true." + +* Copy pg.py to the lib directory where the rest of your modules are. For + example, that's /usr/local/lib/Python on my system. + +* Do 'make -f Makefile.pre.in boot' and do 'make && make install' + +* For more details read the documentation at the top of Makefile.pre.in + +* If you are on NetBSD, look in the packages directory under databases. If + it isn't there yet, it should be there shortly. You can also pick up the + package files from ftp://ftp.druid.net/pub/distrib/pygresql.pkg.tgz. + There is also a package in the FreeBSD ports collection but as I write + this it is at version 2.1. I will try to get that updated as well. + +* For Linux installation look at README.linux + + +1.4. Where to get ... ? +----------------------- + +The home sites of the different packages are: + + - Python: ftp://ftp.python.org:/pub/python + - PosgreSQL: ftp://ftp.PostgreSQL.org/pub/postgresql-6.4.tar.gz + - PyGreSQL: ftp://ftp.druid.net/pub/distrib/pygresql-2.2.tgz + +A Linux RPM can be picked up from ftp://www.eevolute.com/pub/python/. + + +1.5. Information and support +---------------------------- + +If you need information about these packages please check their web sites: + + - Python: http://www.python.org/ + - PostgreSQL: http://www.postgresql.org/ + - PyGres95: http://www.via.ecp.fr/via/products/pygres.html + - PyGreSQL: http://www.druid.net/pygresql/ + +For support: + + - Python: newgroup comp.lang.python + - PostgreSQL: mailing list (see package documentation for information) + - PyGres95: contact me (andre@via.ecp.fr) for bug reports, ideas, remarks + I will try to answer as long as my free time allow me to do + that. + - PyGreSQL: contact me (darcy@druid.net) concerning the changes to 2.x. + + +2. Programming information +========================== + +This module defines three objects: the pgobject that handles the connection +and all the requests to the database, the pglargeobject that handles +all the accesses to Postgres large objects and pgqueryobject that handles +query results. + +2.1. pg module description +---------------------------- + +The module defines only a few methods that allow to connect to a database and +to allow to define "default variables" that override the environment variables +used by PostgreSQL. + +These "default variables" were designed to allow you to handle general +connection parameters without heavy code in your programs. You can prompt the +user for a value, put it in the default variable, and forget it, without +having to modify your environment. The support for default variables can be +disabled by setting the -DNO_DEF_VAR option in the Python Setup file. Methods +relative to this are specified by te tag [DV]. + +All variables are set to None at module initialization, specifying that +standard environment variables should be used. + + 2.1.1. connect - opens a pg connection + ---------------------------------------- + + Syntax: + connect(dbname, host, port, opt, tty, user, passwd) + Parameters: + dbname - name of connected database (string/None) + host - name of the server host (string/None) + port - port used by the database server (integer/-1) + opt - connection options (string/None) + tty - debug terminal (string/None) + user - PostgreSQL user (string/None) + passwd - password for user (string/None) + Return type: + pgobject - the object handling the connection + Exceptions raised: + TypeError - bad argument type, or too many arguments + SyntaxError - duplicate argument definition + pg.error - some error occurred during pg connection definition + (+ all exceptions relative to object allocation) + Description: + This method opens a connection to a specified database on a given + PostgreSQL server. You can use keywords here, as described in the + Python tutorial; + the names of the keywords are the name of the parameters given in the + syntax line. For a precise description of the parameters, please refer to + the PostgreSQL user manual. + + 2.1.2. get_defhost, set_defhost - default server host name handling [DV] + ------------------------------------------------------------------------ + + Syntax: get_defhost() + Parameters: + none + Return type: + string, None - default host specification + Exceptions raised: + SyntaxError - too many arguments + Description: + This method returns the current default host specification, or None if the + environment variables should be used. Environment variables won't be looked + up. + + Syntax: set_defhost(host) + Parameters: + host - new default host (string/None) + Return type: + string, None - previous default host specification + Exceptions raised: + TypeError - bad argument type, or too many arguments + Description: + This methods sets the default host value for new connections. If None is + supplied as parameter, environment variables will be used in future + connections. It returns the previous setting for default host. + + 2.1.3. get_defport, set_defport - default server port handling [DV] + ------------------------------------------------------------------- + + Syntax: get_defport() + Parameters: none + Return type: + integer, None - default port specification + Exceptions raised: + SyntaxError - too many arguments + Description: + This method returns the current default port specification, or None if + the environment variables should be used. Environment variables won't + be looked up. + + Syntax: set_defport(port) + Parameters: + port - new default port (integer/-1) + Return type: + integer, None - previous default port specification + Description: + This methods sets the default port value for new connections. If -1 is + supplied as parameter, environment variables will be used in future + connections. It returns the previous setting for default port. + + 2.1.4. get_defopt, set_defopt - default connection options handling [DV] + ------------------------------------------------------------------------ + + Syntax: get_defopt() + Parameters: none + Return type: + string, None - default options specification + Exceptions raised: + SyntaxError - too many arguments + Description: + This method returns the current default connection options specification, + or None if the environment variables should be used. Environment variables + won't be looked up. + + Syntax: set_defopt(options) + Parameters: + options - new default connection options (string/None) + Return type: + string, None - previous default options specification + Exceptions raised: + TypeError - bad argument type, or too many arguments + Description: + This methods sets the default connection options value for new connections. + If None is supplied as parameter, environment variables will be used in + future connections. It returns the previous setting for default options. + + 2.1.5. get_deftty, set_deftty - default connection debug tty handling [DV] + -------------------------------------------------------------------------- + + Syntax: get_deftty() + Parameters: none + Return type: + string, None - default debug terminal specification + Exceptions raised: + SyntaxError - too many arguments + Description: + This method returns the current default debug terminal specification, or + None if the environment variables should be used. Environment variables + won't be looked up. + + Syntax: set_deftty(terminal) + Parameters: + terminal - new default debug terminal (string/None) + Return type: + string, None - previous default debug terminal specification + Exceptions raised: + TypeError - bad argument type, or too many arguments + Description: + This methods sets the default debug terminal value for new connections. If + None is supplied as parameter, environment variables will be used in future + connections. It returns the previous setting for default terminal. + + 2.1.6. get_defbase, set_defbase - default database name handling [DV] + --------------------------------------------------------------------- + + Syntax: get_defbase() + Parameters: none + Return type: + string, None - default database name specification + Exceptions raised: + SyntaxError - too many arguments + Description: + This method returns the current default database name specification, or + None if the environment variables should be used. Environment variables + won't be looked up. + + Syntax: set_defbase(base) + Parameters: + base - new default base name (string/None) + Return type: + string, None - previous default database name specification + Exceptions raised: + TypeError - bad argument type, or too many arguments + Description: + This method sets the default database name value for new connections. If + None is supplied as parameter, environment variables will be used in + future connections. It returns the previous setting for default host. + + 2.1.7. Module constants + ----------------------- + + Some constants are defined in the module dictionary. They are intended to be +used as parameters for methods calls. You should refer to PostgreSQL user +manual for more information about them. These constants are: + + - large objects access modes, used by (pgobject.)locreate and + (pglarge.)open: (pg.)INV_READ, (pg.)INV_WRITE, (pg.)INV_ARCHIVE + - positional flags, used by (pglarge.)seek: (pg.)SEEK_SET, + (pg.)SEEK_CUR, (pg.)SEEK_END. + - version and __version__ constants that give the current version. + + 2.1.9. + 2.1.10. Miscellaneous attributes + + The following methods return information about the current connection. + + - +2.2. pgobject description +--------------------------- + + This object handle a connection to a PostgreSQL database. It embeds and +hides all the parameters that define this connection, thus just leaving really +significant parameters in function calls. + Some methods give direct access to the connection socket. They are specified +by the tag [DA]. DO NOT USE THEM UNLESS YOU REALLY KNOW WHAT YOU ARE DOING. If +you prefer disabling them, set the -DNO_DIRECT option in the Python Setup file. + Some other methods give access to large objects (refer to PostgreSQL user +manual for more information about these). if you want to forbid access to these +from the module, set the -DNO_LARGE option in the Python Setup file. These +methods are specified by the tag [LO]. + + 2.2.1. query - executes a SQL command string + -------------------------------------------- + + Syntax: query(command) + Parameters: + command - SQL command (string) + Return type: + pgqueryobject, None - result values + Exceptions raised: + TypeError - bad argument type, or too many arguments. + ValueError - empty SQL query + pg.error - error during query processing, or invalid connection + Description: + This method simply sends a SQL query to the database. If the query is + an insert statement, the return value is the OID of the newly + inserted row. If it is otherwise a query that does not return a result + (ie. is not a some kind of SELECT statement), it returns None. + Otherwise, it returns a pgqueryobject that can be accessed via the + getresult method or printed. + + pgqueryobject methods + --------------------- + + 2.2.1.1. getresult - gets the values returned by the query + ------------------------------------------------------------- + + Syntax: getresult() + Parameters: none + Return type: + list - result values + Exceptions raised: + SyntaxError - too many parameters + pg.error - invalid previous result + Description: + This method returns the list of the values returned by the query. + More information about this result may be get using listfields, + fieldname and fiednum methods. + + 2.2.1.2. dictresult - like getresult but returns list of dictionaries + --------------------------------------------------------------------- + + Syntax: dictresult() + Parameters: none + Return type: + list - result values as a dictionary + Exceptions raised: + SyntaxError - too many parameters + pg.error - invalid previous result + Description: + This method returns the list of the values returned by the query + with each tuple returned as a dictionary with the field names + used as the dictionary index. + + + 2.2.1.3. listfields - lists the fields names of the previous query result + ----------------------------------------------------------------------- + + Syntax: listfields() + Parameters: none + Return type: + list - fields names + Exceptions raised: + SyntaxError - too many parameters + pg.error - invalid previous result, or invalid connection + Description: + This method returns the list of names of the fields defined for the + query result. The fields are in the same order as the result values. + + 2.2.1.4. fieldname, fieldnum - field name-number conversion + --------------------------------------------------------- + + Syntax: fieldname(i) + Parameters: + i - field number (integer) + Return type: + string - field name + Exceptions raised: + TypeError - bad parameter type, or too many parameters + ValueError - invalid field number + pg.error - invalid previous result, or invalid connection + Description: + This method allows to find a field name from its rank number. It can be + useful for displaying a result. The fields are in the same order than the + result values. + + Syntax: fieldnum(name) + Parameters: + name - field name (string) + Return type: + integer - field number + Exceptions raised: + TypeError - bad parameter type, or too many parameters + ValueError - unknown field name + pg.error - invalid previous result, or invalid connection + Description: + This method returns a field number from its name. It can be used to + build a function that converts result list strings to their correct + type, using a hardcoded table definition. The number returned is the + field rank in the result values list. + + 2.2.2. reset - resets the connection + ------------------------------------ + + Syntax: reset() + Parameters: None + Return type: None + Exceptions raised: + TypeError - too many (any) arguments + Description: + This method resets the current database. + + + 2.2.3. close - close the database connection + -------------------------------------------- + + Syntax: close() + Parameters: none + Return type: None + Exceptions raised: + TypeError - too many (any) arguments + Description: + This method closes the database connection. The connection will + be closed in any case when the connection is deleted but this + allows you to explicitly close it. It is mainly here to allow + the DB-SIG API wrapper to implement a close function. + + + 2.2.4. fileno - returns the socket used to connect to the database + ------------------------------------------------------------------ + + Syntax: fileno() + Parameters: none + Exceptions raised: + TypeError - too many (any) arguments + Description: + This method returns the underlying socket id used to connect + to the database. This is useful for use in select calls, etc. + Note: This function depends on having a recent version of the + database. See "-DNO_PQSOCKET" described above. + + + 2.2.5. getnotify - gets the last notify from the server + ------------------------------------------------------- + + Syntax: getnotify() + Parameters: none + Return type: + tuple, None - last notify from server + Exceptions raised: + SyntaxError - too many parameters + pg.error - invalid connection + Description: + This methods try to get a notify from the server (from the SQL statement + NOTIFY). If the server returns no notify, the methods returns None. + Otherwise, it returns a tuple (couple) (relname, pid), where relname is the + name of the notify and pid the process id of the connection that triggered + the notify. Remember to do a listen query first otherwise getnotify + will always return None. + + 2.2.6. inserttable - insert a list into a table + ----------------------------------------------- + + Syntax: inserttable(table, values) + Parameters: + table - the table name (string) + values - list of rows values (list) + Return type: + None + Exception raised: + pg.error - invalid connection + TypeError - bad argument type, or too many arguments + Description: + This method allow to quickly insert large blocks of data in a table: it + inserts the whole values list into the given table. The list is a list of + tuples/lists that define the values for each inserted row. The rows values + may contain string, integer, long or double (real) values. + BE VERY CAREFUL: this method doesn't typecheck the fields according to the + table definition; it just look whether or not it knows how to handle such + types. + + 2.2.7. putline - writes a line to the server socket [DA] + -------------------------------------------------------- + + Syntax: putline(line) + Parameters: + line - line to be written (string) + Return type: + None + Exceptions raised: + pg.error - invalid connection + TypeError - bad parameter type, or too many parameters + Description: + This method allows to directly write a string to the server socket. + + 2.2.8. getline - gets a line from server socket [DA] + ---------------------------------------------------- + + Syntax: getline() + Parameters: none + Return type: + string - the line read + Exceptions raised: + pg.error - invalid connection + SyntaxError - too many parameters + Description: + This method allows to directly read a string from the server socket. + + 2.2.9. endcopy - synchronizes client and server [DA] + ---------------------------------------------------- + + Syntax: endcopy() + Parameters: none + Return type: + None + Exceptions raised: + pg.error - invalid connection + SyntaxError - too many parameters + Description: + The use of direct access methods may desynchonize client and server. This + method ensure that client and server will be synchronized. + + 2.2.10. locreate - creates of large object in the database [LO] + --------------------------------------------------------------- + + Syntax: locreate(mode) + Parameters: + mode - large object create mode + Return type: + pglarge - object handling the postgres large object + Exceptions raised: + pg.error - invalid connection, or creation error + TypeError - bad parameter type, or too many parameters + Description: + This method creates a large object in the database. The mode can be defined + by OR-ing the constants defined in the pg module (INV_READ, INV_WRITE and + INV_ARCHIVE). Please refer to PostgreSQL user manual for a description of + the mode values. + + 2.2.11. getlo - builds a large object from given oid [LO] + --------------------------------------------------------- + + Syntax: getlo(oid) + Parameters: + oid - oid of the existing large object (integer) + Return type: + pglarge - object handling the postgres large object + Exceptions raised: + pg.error - invalid connection + TypeError - bad parameter type, or too many parameters + ValueError - bad oid value (0 is invalid_oid) + Description: + This method allows to reuse a formerly created large object through the + pglarge interface, providing the user have its oid. + + 2.2.12. loimport - import a file to a postgres large object [LO] + ---------------------------------------------------------------- + + Syntax: loimport(name) + Parameters: + name - the name of the file to be imported (string) + Return type: + pglarge - object handling the postgres large object + Exceptions raised: + pg.error - invalid connection, or error during file import + TypeError - bad argument type, or too many arguments + Description: + This methods allows to create large objects in a very simple way. You just + give the name of a file containing the data to be use. + + 2.2.13. pgobject attributes + ----------------------------- + + Every pgobject defines a set of read-only attributes that describe the +connection and its status. These attributes are: + host - the hostname of the server (string) + port - the port of the server (integer) + db - the selected database (string) + options - the connection options (string) + tty - the connection debug terminal (string) + user - the username on the database system (string) + status - the status of the connection (integer: 1 - OK, 0 - BAD) + error - the last warning/error message from the server (string) + +2.3. pglarge description +-------------------------- + + This object handles all the request concerning a postgres large object. It +embeds and hides all the 'recurrent' variables (object oid and connection), +exactly in the same way pgobjects do, thus only keeping significant +parameters in function calls. It keeps a reference to the pgobject used for +its creation, sending requests though with its parameters. Any modification but +dereferencing the pgobject will thus affect the pglarge object. +Dereferencing the initial pgobject is not a problem since Python won't +deallocate it before the large object dereference it. + All functions return a generic error message on call error, whatever the +exact error was. The 'error' attribute of the object allow to get the exact +error message. + + 2.3.1. open - opens a large object + ---------------------------------- + + Syntax: open(mode) + Parameters: + mode - open mode definition (integer) + Return type: + None + Exceptions raised: + pg.error - invalid connection + TypeError - bad parameter type, or too many parameters + IOError - already opened object, or open error + Description: + This method opens a large object for reading/writing, in the same way than + the UNIX open() function. The mode value can be obtained by OR-ing the + constants defined in the pgmodule (INV_READ, INV_WRITE). + + 2.3.2. close - closes a large object + ------------------------------------ + + Syntax: close() + Parameters: none + Return type: + None + Exceptions raised: + pg.error - invalid connection + SyntaxError - too many parameters + IOError - object is not opened, or close error + Description: + This method closes a previously opened large object, in the same way than + the UNIX close() function. + + 2.3.4. read, write, tell, seek, unlink - file like large object handling + ------------------------------------------------------------------------ + + Syntax: read(size) + Parameters: + size - maximal size of the buffer to be read + Return type: + sized string - the read buffer + Exceptions raised: + pg.error - invalid connection or invalid object + TypeError - bad parameter type, or too many parameters + IOError - object is not opened, or read error + Description: + This function allows to read data from a large object, starting at current + position. + + Syntax: write(string) + Parameters: + (sized) string - buffer to be written + Return type: + None + Exceptions raised: + pg.error - invalid connection or invalid object + TypeError - bad parameter type, or too many parameters + IOError - object is not opened, or write error + Description: + This function allows to write data to a large object, starting at current + position. + + Syntax: seek(offset, whence) + Parameters: + offset - position offset + whence - positional parameter + Return type: + integer - new position in object + Exception raised: + pg.error - invalid connection or invalid object + TypeError - bad parameter type, or too many parameters + IOError - object is not opened, or seek error + Description: + This method allows to move the position cursor in the large object. The + whence parameter can be obtained by OR-ing the constants defined in the + pg module (SEEK_SET, SEEK_CUR, SEEK_END). + + Syntax: tell() + Parameters: none + Return type: + integer - current position in large object + Exception raised: + pg.error - invalid connection or invalid object + SyntaxError - too many parameters + IOError - object is not opened, or seek error + Description: + This method allows to get the current position in the large object. + + Syntax: unlink() + Parameter: none + Return type: + None + Exception raised: + pg.error - invalid connection or invalid object + SyntaxError - too many parameters + IOError - object is not closed, or unlink error + Description: + This methods unlinks (deletes) the postgres large object. + + 2.3.5. size - gives the large object size + ----------------------------------------- + + Syntax: size() + Parameters: none + Return type: + integer - large object size + Exceptions raised: + pg.error - invalid connection or invalid object + SyntaxError - too many parameters + IOError - object is not opened, or seek/tell error + Description: + This (composite) method allows to get the size of a large object. Currently + the large object needs to be opened. It was implemented because this + function is very useful for a WWW interfaced database. + + 2.3.6. export - saves a large object to a file + ---------------------------------------------- + + Syntax: export(name) + Parameters: + name - file to be created + Return type: + None + Exception raised: + pg.error - invalid connection or invalid object + TypeError - bad parameter type, or too many parameters + IOError - object is not closed, or export error + Description: + This methods allows to dump the content of a large object in a very simple + way. The exported file is created on the host of the program, not the + server host. + + 2.3.7. Object attributes + ------------------------ + + pglarge objects define a read-only set of attributes that allow to get some +information about it. These attributes are: + oid - the oid associated with the object + pgcnx - the pgobject associated with the object + error - the last warning/error message of the connection +BE CAREFUL: in multithreaded environments, 'error' may be modified by another +thread using the same pgobject. Remember these object are shared, not +duplicated. You should provide some locking to be able if you want to check +this. + The oid attribute is very interesting because it allow you reuse the oid +later, creating the pglarge object with a pgobject getlo() method call. + + +3. The pg wrapper +================ + +The previous functions are wrapped in a module called pg. The module +has a class called DB. The above functions are also included in the +name space so it isn't necessary to import both modules. The preferred +way to use this module is as follows. + +from pg import DB +db = DB(...) # See description of the initialization method below. + +The following describes the methods and variables of this class. + + + 3.1. Initialization + ------------------- + The DB class is initialized with the same arguments as the connect + method described in section 2. It also initializes a few internal + variables. The statement 'db = DB()' will open the local database + with the name of the user just like connect() does. + + 3.2. pkey + --------- + Syntax: + pkey(table) + Parameters: + table - name of table + Returns: + Name of field which is the primary key of the table. + Description: + This method returns the primary key of a table. Note that this raises + an exception if the table doesn't have a primary key. Further, in the + currently released implementation of PostgreSQL the 'PRIMARY KEY' syntax + doesn't actually fill in the necessary tables to determine primary keys. + You can do this yourself with the following query. + + # Set up table and primary_field variables... + + """UPDATE pg_index SET indisprimary = 't' + WHERE pg_index.oid in (SELECT pg_index.oid + FROM pg_class, pg_attribute, pg_index + WHERE pg_class.oid = pg_attribute.attrelid AND + pg_class.oid = pg_index.indrelid AND + pg_index.indkey[0] = pg_attribute.attnum AND + pg_class.relname = '%(table)s' AND + pg_attribute.attname = '%(primary_field)');""" % locals() + + This will be fixed in the upcoming 6.5 release of PostgreSQL or + you can download the current sources now. Downloading current + is, as usual, at your own risk. + + 3.3. get_databases - get list of databases in the system + -------------------------------------------------------- + Syntax: get_databases() + Parameters: none + Returns: list of databases in the system + Description: + Although you can do this with a simple select, it is added here for + convenience + + 3.4. get_tables - get list of tables in connected database + ---------------------------------------------------------- + Syntax: get_tables() + Parameters: none + Returns: list of tables in connected database + + 3.5. get_attnames + ----------------- + Syntax: + get_attnames(table) + Parameters: + table - name of table + Returns: + List of attribute names + Description: + Given the name of a table, digs out the list of attribute names. + + 3.6. get - get a tuple from a database table + -------------------------------------------- + Syntax: + get(table, arg, [keyname]) + Parameters: + table - name of table + arg - either a dictionary or the value to be looked up + keyname - name of field to use as key (optional) + Returns: + A dictionary mapping attribute names to row values. + Description: + This method is the basic mechanism to get a single row. It assumes + that the key specifies a unique row. If keyname is not specified + then the primary key for the table is used. If arg is a dictionary + then the value for the key is taken from it and it is modified to + include the new values, replacing existing values where necessary. + The oid is also put into the dictionary but in order to allow the + caller to work with multiple tables, the attribute name is munged + to make it unique. It consists of the string "oid_" followed by + the name of the table. + + + 3.7. insert - insert a tuple into a database table + -------------------------------------------------- + Syntax: + insert(table, a) + Parameters: + table - name of table + a - a dictionary of values + Returns: + The OID of the newly inserted row. + Description: + This method inserts values into the table specified filling in the + values from the dictionary. It then reloads the dictionary with the + values from the database. This causes the dictionary to be updated + with values that are modified by rules, triggers, etc. + + + 3.8. update + ----------- + Syntax: + update(table, a) + Parameters: + table - name of table + a - a dictionary of values + Returns: + A dictionary with the new row + Description: + Similar to insert but updates an existing row. The update is based + on the OID value as munged by get. The array returned is the + one sent modified to reflect any changes caused by the update due + to triggers, rules, defaults, etc. + + 3.9. clear + ---------- + Syntax: + clear(table, [a]) + Parameters: + table - name of table + a - a dictionary of values + Returns: + A dictionary with an empty row + Description: + This method clears all the attributes to values determined by the types. + Numeric types are set to 0, dates are set to 'TODAY' and everything + else is set to the empty string. If the array argument is present, + it is used as the array and any entries matching attribute names + are cleared with everything else left unchanged. + + 3.8. delete + ----------- + Syntax: + delete(table, a) + Parameters: + table - name of table + a - a dictionary of values + Returns: + None + Description: + This method deletes the row from a table. It deletes based on the OID + as munged as described above. + + +4. Future directions +==================== + +The large object and direct access functions need much more attention. + +I want to add a DB-SIG API wrapper around the underlying module. This +will be in 3.0. + diff --git a/src/interfaces/python/README.linux b/src/interfaces/python/README.linux new file mode 100644 index 0000000000..8fd75f252f --- /dev/null +++ b/src/interfaces/python/README.linux @@ -0,0 +1,33 @@ +Thanks to thilo@eevolute.com for this README and the RPM + + +INSTALLING PyGreSQL on Redhat Linux 5.1 or 5.2 +============================================== + +Things are pretty easy on Redhat Linux. You can either get a precompiled +RPM package from + + ftp://www.eevolute.com/pub/python/ + +or try in compile and install it yourself: + +bash$ make redhat # this just compiles the module as a shared object +cc -fpic -shared -o _pg.so -I/usr/include/python1.5 pgmodule.c -lpq +bash$ python # you can test it from your local directory +Python 1.5.1 (#1, May 6 1998, 01:48:27) [GCC 2.7.2.3] on linux-i386 +Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam +>>> import _pg +>>> db = _pg.connect('thilo', 'localhost') +>>> db.query("INSERT INTO test VALUES ('ping', 'pong')") +18304 +>>> db.query("SELECT * FROM test") +eins|zwei +----+---- +ping|pong +(1 row) + +bash$ su # Yow! Seems to work - now install it properly +bash# cp _pg.so /usr/lib/python1.5/lib-dynload + +done! + diff --git a/src/interfaces/python/mkdefines b/src/interfaces/python/mkdefines new file mode 100755 index 0000000000..fd5eb6f4cd --- /dev/null +++ b/src/interfaces/python/mkdefines @@ -0,0 +1,11 @@ +#! /usr/local/bin/python + +import string + +# change this if you have it somewhere else +for l in open("/usr/local/pgsql/src/include/catalog/pg_type.h").readlines(): + tokens = string.split(l) + if len(tokens) == 0 or tokens[0] != "#define": continue + + if tokens[1] in ('CASHOID', 'INT2OID', 'INT4OID', 'OIDOID', 'FLOAT4OID', 'FLOAT8OID'): + print l, diff --git a/src/interfaces/python/pg.py b/src/interfaces/python/pg.py new file mode 100644 index 0000000000..64022d2349 --- /dev/null +++ b/src/interfaces/python/pg.py @@ -0,0 +1,237 @@ +# pgutil.py +# Written by D'Arcy J.M. Cain + +# This library implements some basic database management stuff +# It includes the pg module and builds on it + +from _pg import * +import string, re, sys + +# utility function +# We expect int, seq, decimal, text or date (more later) +def _quote(d, t): + if t in ['int', 'decimal', 'seq']: + if d == "": return 0 + return "%s" % d + + if t == 'money': + if d == "": return '0.00' + return "'%.2f'" % d + + if t == 'bool': + if string.upper(d) in ['T', 'TRUE', 'Y', 'YES', 1, '1', 'ON']: + return "'t'" + else: + return "'f'" + + if d == "": return "null" + return "'%s'" % string.strip(re.sub('\'', '\'\'', "%s" % d)) + +class DB: + """This class wraps the pg connection type""" + + def __init__(self, *args, **kw): + self.db = apply(connect, args, kw) + + # Create convience methods, in a way that is still overridable. + for e in ( 'query', 'reset', 'close', 'getnotify', 'inserttable', + 'putline', 'getline', 'endcopy', + 'host', 'port', 'db', 'options', + 'tty', 'error', 'status', 'user', + 'locreate', 'getlo', 'loimport' ): + if not hasattr(self,e) and hasattr(self.db,e): + exec 'self.%s = self.db.%s' % ( e, e ) + + self.attnames = {} + self.pkeys = {} + self.debug = None # For debugging scripts, set to output format + # that takes a single string arg. For example + # in a CGI set to "%s
" + + # Get all the primary keys at once + for rel, att in self.db.query("""SELECT + pg_class.relname, pg_attribute.attname + FROM pg_class, pg_attribute, pg_index + WHERE pg_class.oid = pg_attribute.attrelid AND + pg_class.oid = pg_index.indrelid AND + pg_index.indkey[0] = pg_attribute.attnum AND + pg_index.indisprimary = 't'""").getresult(): + self.pkeys[rel] = att + + def pkey(self, cl): + # will raise an exception if primary key doesn't exist + return self.pkeys[cl] + + def get_databases(self): + l = [] + for n in self.db.query("SELECT datname FROM pg_database").getresult(): + l.append(n[0]) + return l + + def get_tables(self): + l = [] + for n in self.db.query("""SELECT relname FROM pg_class + WHERE relkind = 'r' AND + relname !~ '^Inv' AND + relname !~ '^pg_'""").getresult(): + l.append(n[0]) + return l + + def get_attnames(self, cl): + # May as well cache them + if self.attnames.has_key(cl): + return self.attnames[cl] + + query = """SELECT pg_attribute.attname, pg_type.typname + FROM pg_class, pg_attribute, pg_type + WHERE pg_class.relname = '%s' AND + pg_attribute.attnum > 0 AND + pg_attribute.attrelid = pg_class.oid AND + pg_attribute.atttypid = pg_type.oid""" + + l = {} + for attname, typname in self.db.query(query % cl).getresult(): + if re.match("^int", typname): + l[attname] = 'int' + elif re.match("^oid", typname): + l[attname] = 'int' + elif re.match("^text", typname): + l[attname] = 'text' + elif re.match("^char", typname): + l[attname] = 'text' + elif re.match("^name", typname): + l[attname] = 'text' + elif re.match("^abstime", typname): + l[attname] = 'date' + elif re.match("^date", typname): + l[attname] = 'date' + elif re.match("^bool", typname): + l[attname] = 'bool' + elif re.match("^float", typname): + l[attname] = 'decimal' + elif re.match("^money", typname): + l[attname] = 'money' + else: + l[attname] = 'text' + + self.attnames[cl] = l + return self.attnames[cl] + + # return a tuple from a database + def get(self, cl, arg, keyname = None): + if keyname == None: # use the primary key by default + keyname = self.pkeys[cl] + + fnames = self.get_attnames(cl) + + if type(arg) == type({}): + # To allow users to work with multiple tables we munge the + # name when the key is "oid" + if keyname == 'oid': k = arg['oid_%s' % cl] + else: k = arg[keyname] + else: + k = arg + arg = {} + + # We want the oid for later updates if that isn't the key + if keyname == 'oid': + q = "SELECT * FROM %s WHERE oid = %s" % (cl, k) + else: + q = "SELECT oid AS oid_%s, %s FROM %s WHERE %s = %s" % \ + (cl, string.join(fnames.keys(), ','),\ + cl, keyname, _quote(k, fnames[keyname])) + + if self.debug != None: print self.debug % q + res = self.db.query(q).dictresult() + if res == []: + raise error, \ + "No such record in %s where %s is %s" % \ + (cl, keyname, _quote(k, fnames[keyname])) + return None + + for k in res[0].keys(): + arg[k] = res[0][k] + + return arg + + # Inserts a new tuple into a table + def insert(self, cl, a): + fnames = self.get_attnames(cl) + l = [] + n = [] + for f in fnames.keys(): + if a.has_key(f): + if a[f] == "": l.append("null") + else: l.append(_quote(a[f], fnames[f])) + n.append(f) + + try: + q = "INSERT INTO %s (%s) VALUES (%s)" % \ + (cl, string.join(n, ','), string.join(l, ',')) + if self.debug != None: print self.debug % q + a['oid_%s' % cl] = self.db.query(q) + except: + raise error, "Error inserting into %s: %s" % (cl, sys.exc_value) + + # reload the dictionary to catch things modified by engine + # note that get() changes 'oid' below to oid_table + return self.get(cl, a, 'oid') + + # update always works on the oid which get returns + def update(self, cl, a): + q = "SELECT oid FROM %s WHERE oid = %s" % (cl, a['oid_%s' % cl]) + if self.debug != None: print self.debug % q + res = self.db.query(q).getresult() + if len(res) < 1: + raise error, "No record in %s where oid = %s (%s)" % \ + (cl, a['oid_%s' % cl], sys.exc_value) + + v = [] + k = 0 + fnames = self.get_attnames(cl) + + for ff in fnames.keys(): + if a.has_key(ff) and a[ff] != res[0][k]: + v.append("%s = %s" % (ff, _quote(a[ff], fnames[ff]))) + + if v == []: + return None + + try: + q = "UPDATE %s SET %s WHERE oid = %s" % \ + (cl, string.join(v, ','), a['oid_%s' % cl]) + if self.debug != None: print self.debug % q + self.db.query(q) + except: + raise error, "Can't update %s: %s" % (cl, sys.exc_value) + + # reload the dictionary to catch things modified by engine + return self.get(cl, a, 'oid') + + # At some point we will need a way to get defaults from a table + def clear(self, cl, a = {}): + fnames = self.get_attnames(cl) + for ff in fnames.keys(): + if fnames[ff] in ['int', 'decimal', 'seq', 'money']: + a[ff] = 0 + elif fnames[ff] == 'date': + a[ff] = 'TODAY' + else: + a[ff] = "" + + a['oid'] = 0 + return a + + # Like update, delete works on the oid + # one day we will be testing that the record to be deleted + # isn't referenced somewhere (or else PostgreSQL will) + def delete(self, cl, a): + try: + q = "DELETE FROM %s WHERE oid = %s" % (cl, a['oid_%s' % cl]) + if self.debug != None: print self.debug % q + self.db.query(q) + except: + raise error, "Can't delete %s: %s" % (cl, sys.exc_value) + + return None + diff --git a/src/interfaces/python/pgmodule.c b/src/interfaces/python/pgmodule.c new file mode 100644 index 0000000000..7b5ced54eb --- /dev/null +++ b/src/interfaces/python/pgmodule.c @@ -0,0 +1,2036 @@ +/* + * PyGres, version 2.2 A Python interface for PostgreSQL database. Written by + * D'Arcy J.M. Cain, (darcy@druid.net). Based heavily on code written by + * Pascal Andre, andre@chimay.via.ecp.fr. Copyright (c) 1995, Pascal Andre + * (andre@via.ecp.fr). + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without a written + * agreement is hereby granted, provided that the above copyright notice and + * this paragraph and the following two paragraphs appear in all copies or in + * any new file that contains a substantial portion of this file. + * + * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, + * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE + * AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE + * AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, + * ENHANCEMENTS, OR MODIFICATIONS. + * + * Further modifications copyright 1997 by D'Arcy J.M. Cain (darcy@druid.net) + * subject to the same terms and conditions as above. + * + */ + +#include +#include +#include +#include +#include +#include + +/* really bad stuff here - I'm so naughty */ +/* If you need to you can run mkdefines to get */ +/* current defines but it should not have changed */ +#define INT2OID 21 +#define INT4OID 23 +#define OIDOID 26 +#define FLOAT4OID 700 +#define FLOAT8OID 701 +#define CASHOID 790 + +static PyObject *PGError; +static const char *PyPgVersion = "2.3"; + +/* taken from fileobject.c */ +#define BUF(v) PyString_AS_STRING((PyStringObject *)(v)) + +#define CHECK_OPEN 1 +#define CHECK_CLOSE 2 + +#define MAX_BUFFER_SIZE 8192 /* maximum transaction size */ + +#ifndef NO_DIRECT +#define DIRECT_ACCESS 1 /* enables direct access functions */ +#endif /* NO_DIRECT */ + +#ifndef NO_LARGE +#define LARGE_OBJECTS 1 /* enables large objects support */ +#endif /* NO_LARGE */ + +#ifndef NO_DEF_VAR +#define DEFAULT_VARS 1 /* enables default variables use */ +#endif /* NO_DEF_VAR */ + +/* --------------------------------------------------------------------- */ + +/* MODULE GLOBAL VARIABLES */ + +#ifdef DEFAULT_VARS + +static PyObject *pg_default_host; /* default database host */ +static PyObject *pg_default_base; /* default database name */ +static PyObject *pg_default_opt; /* default connection options */ +static PyObject *pg_default_tty; /* default debug tty */ +static PyObject *pg_default_port; /* default connection port */ +static PyObject *pg_default_user; /* default username */ +static PyObject *pg_default_passwd; /* default password */ + +#endif /* DEFAULT_VARS */ + +/* --------------------------------------------------------------------- */ + +/* OBJECTS DECLARATION */ + +/* pg connection object */ + +typedef struct +{ + PyObject_HEAD + int valid; /* validity flag */ + PGconn *cnx; /* PostGres connection handle */ +} pgobject; + +staticforward PyTypeObject PgType; + +#define is_pgobject(v) ((v)->ob_type == &PgType) + +/* pg query object */ + +typedef struct +{ + PyObject_HEAD + PGresult *last_result; /* last result content */ +} pgqueryobject; + +staticforward PyTypeObject PgQueryType; + +#define is_pgqueryobject(v) ((v)->ob_type == &PgQueryType) + +#ifdef LARGE_OBJECTS +/* pg large object */ + +typedef struct +{ + PyObject_HEAD + pgobject * pgcnx; + Oid lo_oid; + int lo_fd; +} pglargeobject; + +staticforward PyTypeObject PglargeType; + +#define is_pglargeobject(v) ((v)->ob_type == &PglargeType) +#endif /* LARGE_OBJECTS */ + +/* --------------------------------------------------------------------- */ + +/* INTERNAL FUNCTIONS */ + +#ifdef LARGE_OBJECTS +/* validity check (large object) */ +static int +check_lo(pglargeobject * self, int level) +{ + if (!self->lo_oid) + { + PyErr_SetString(PGError, "object is not valid (null oid)."); + return 0; + } + + if (level & CHECK_OPEN) + { + if (self->lo_fd < 0) + { + PyErr_SetString(PyExc_IOError, "object is not opened."); + return 0; + } + } + + if (level & CHECK_CLOSE) + { + if (self->lo_fd >= 0) + { + PyErr_SetString(PyExc_IOError, "object is already opened."); + return 0; + } + } + + return 1; +} + +#endif /* LARGE_OBJECTS */ + +/* --------------------------------------------------------------------- */ + +#ifdef LARGE_OBJECTS +/* PG CONNECTION OBJECT IMPLEMENTATION */ + +/* pglargeobject initialisation (from pgobject) */ + +/* creates large object */ +static PyObject * +pg_locreate(pgobject * self, PyObject * args) +{ + int mode; + pglargeobject *npglo; + + if (!self->cnx) + { + PyErr_SetString(PyExc_TypeError, "Connection is not valid"); + return NULL; + } + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "i", &mode)) + { + PyErr_SetString(PyExc_TypeError, + "locreate(mode), with mode (integer)."); + return NULL; + } + + if ((npglo = PyObject_NEW(pglargeobject, &PglargeType)) == NULL) + return NULL; + + npglo->pgcnx = self; + Py_XINCREF(self); + npglo->lo_fd = -1; + npglo->lo_oid = lo_creat(self->cnx, mode); + + /* checks result validity */ + if (npglo->lo_oid == 0) + { + PyErr_SetString(PGError, "can't create large object."); + Py_XDECREF(npglo); + return NULL; + } + + return (PyObject *) npglo; +} + +/* init from already known oid */ +static PyObject * +pg_getlo(pgobject * self, PyObject * args) +{ + int lo_oid; + pglargeobject *npglo; + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "i", &lo_oid)) + { + PyErr_SetString(PyExc_TypeError, "loopen(oid), with oid (integer)."); + return NULL; + } + + if (!lo_oid) + { + PyErr_SetString(PyExc_ValueError, "the object oid can't be null."); + return NULL; + } + + /* creates object */ + if ((npglo = PyObject_NEW(pglargeobject, &PglargeType)) == NULL) + return NULL; + + npglo->pgcnx = self; + Py_XINCREF(self); + npglo->lo_fd = -1; + npglo->lo_oid = lo_oid; + + return (PyObject *) npglo; +} + +/* import unix file */ +static PyObject * +pg_loimport(pgobject * self, PyObject * args) +{ + char *name; + pglargeobject *npglo; + + if (!self->cnx) + { + PyErr_SetString(PyExc_TypeError, "Connection is not valid"); + return NULL; + } + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "loimport(name), with name (string)."); + return NULL; + } + + if ((npglo = PyObject_NEW(pglargeobject, &PglargeType)) == NULL) + return NULL; + + npglo->pgcnx = self; + Py_XINCREF(self); + npglo->lo_fd = -1; + npglo->lo_oid = lo_import(self->cnx, name); + + /* checks result validity */ + if (npglo->lo_oid == 0) + { + PyErr_SetString(PGError, "can't create large object."); + Py_XDECREF(npglo); + return NULL; + } + + return (PyObject *) npglo; +} + +/* pglargeobject methods */ + +/* destructor */ +static void +pglarge_dealloc(pglargeobject * self) +{ + if (self->lo_fd >= 0 && self->pgcnx->valid == 1) + lo_close(self->pgcnx->cnx, self->lo_fd); + + Py_XDECREF(self->pgcnx); + PyMem_DEL(self); +} + +/* opens large object */ +static PyObject * +pglarge_open(pglargeobject * self, PyObject * args) +{ + int mode, fd; + + /* check validity */ + if (!check_lo(self, CHECK_CLOSE)) + return NULL; + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "i", &mode)) + { + PyErr_SetString(PyExc_TypeError, "open(mode), with mode(integer)."); + return NULL; + } + + /* opens large object */ + if ((fd = lo_open(self->pgcnx->cnx, self->lo_oid, mode)) < 0) + { + PyErr_SetString(PyExc_IOError, "can't open large object."); + return NULL; + } + self->lo_fd = fd; + + /* no error : returns Py_None */ + Py_INCREF(Py_None); + return Py_None; +} + +/* close large object */ +static PyObject * +pglarge_close(pglargeobject * self, PyObject * args) +{ + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method close() takes no parameters."); + return NULL; + } + + /* checks validity */ + if (!check_lo(self, CHECK_OPEN)) + return NULL; + + /* closes large object */ + if (lo_close(self->pgcnx->cnx, self->lo_fd)) + { + PyErr_SetString(PyExc_IOError, "error while closing large object fd."); + return NULL; + } + self->lo_fd = -1; + + /* no error : returns Py_None */ + Py_INCREF(Py_None); + return Py_None; +} + +/* reads from large object */ +static PyObject * +pglarge_read(pglargeobject * self, PyObject * args) +{ + int size; + PyObject *buffer; + + /* checks validity */ + if (!check_lo(self, CHECK_OPEN)) + return NULL; + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "i", &size)) + { + PyErr_SetString(PyExc_TypeError, "read(size), wih size (integer)."); + return NULL; + } + + if (size <= 0) + { + PyErr_SetString(PyExc_ValueError, "size must be positive."); + return NULL; + } + + /* allocate buffer and runs read */ + buffer = PyString_FromStringAndSize((char *) NULL, size); + + if ((size = lo_read(self->pgcnx->cnx, self->lo_fd, BUF(buffer), size)) < 0) + { + PyErr_SetString(PyExc_IOError, "error while reading."); + Py_XDECREF(buffer); + return NULL; + } + + /* resize buffer and returns it */ + _PyString_Resize(&buffer, size); + return buffer; +} + +/* write to large object */ +static PyObject * +pglarge_write(pglargeobject * self, PyObject * args) +{ + char *buffer; + int size; + + /* checks validity */ + if (!check_lo(self, CHECK_OPEN)) + return NULL; + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "s", &buffer)) + { + PyErr_SetString(PyExc_TypeError, + "write(buffer), with buffer (sized string)."); + return NULL; + } + + /* sends query */ + if ((size = lo_write(self->pgcnx->cnx, self->lo_fd, buffer, + strlen(buffer))) < strlen(buffer)) + { + PyErr_SetString(PyExc_IOError, "buffer truncated during write."); + return NULL; + } + + /* no error : returns Py_None */ + Py_INCREF(Py_None); + return Py_None; +} + +/* go to position in large object */ +static PyObject * +pglarge_lseek(pglargeobject * self, PyObject * args) +{ + int ret, offset, whence; + + /* checks validity */ + if (!check_lo(self, CHECK_OPEN)) + return NULL; + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "ii", &offset, &whence)) + { + PyErr_SetString(PyExc_TypeError, + "lseek(offset, whence), with offset and whence (integers)."); + return NULL; + } + + /* sends query */ + if ((ret = lo_lseek(self->pgcnx->cnx, self->lo_fd, offset, whence)) == -1) + { + PyErr_SetString(PyExc_IOError, "error while moving cursor."); + return NULL; + } + + /* returns position */ + return PyInt_FromLong(ret); +} + +/* gets large object size */ +static PyObject * +pglarge_size(pglargeobject * self, PyObject * args) +{ + int start, end; + + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method size() takes no parameters."); + return NULL; + } + + /* checks validity */ + if (!check_lo(self, CHECK_OPEN)) + return NULL; + + /* gets current position */ + if ((start = lo_tell(self->pgcnx->cnx, self->lo_fd)) == -1) + { + PyErr_SetString(PyExc_IOError, "error while getting current position."); + return NULL; + } + + /* gets end position */ + if ((end = lo_lseek(self->pgcnx->cnx, self->lo_fd, 0, SEEK_END)) == -1) + { + PyErr_SetString(PyExc_IOError, "error while getting end position."); + return NULL; + } + + /* move back to start position */ + if ((start = lo_lseek(self->pgcnx->cnx,self->lo_fd,start,SEEK_SET)) == -1) + { + PyErr_SetString(PyExc_IOError, + "error while moving back to first position."); + return NULL; + } + + /* returns size */ + return PyInt_FromLong(end); +} + +/* gets large object cursor position */ +static PyObject * +pglarge_tell(pglargeobject * self, PyObject * args) +{ + int start; + + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method tell() takes no parameters."); + return NULL; + } + + /* checks validity */ + if (!check_lo(self, CHECK_OPEN)) + return NULL; + + /* gets current position */ + if ((start = lo_tell(self->pgcnx->cnx, self->lo_fd)) == -1) + { + PyErr_SetString(PyExc_IOError, "error while getting position."); + return NULL; + } + + /* returns size */ + return PyInt_FromLong(start); +} + +/* exports large object as unix file */ +static PyObject * +pglarge_export(pglargeobject * self, PyObject * args) +{ + char *name; + + /* checks validity */ + if (!check_lo(self, CHECK_CLOSE)) + return NULL; + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, + "export(filename), with filename (string)."); + return NULL; + } + + /* runs command */ + if (!lo_export(self->pgcnx->cnx, self->lo_oid, name)) + { + PyErr_SetString(PyExc_IOError, "error while exporting large object."); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +/* deletes a large object */ +static PyObject * +pglarge_unlink(pglargeobject * self, PyObject * args) +{ + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method unlink() takes no parameters."); + return NULL; + } + + /* checks validity */ + if (!check_lo(self, CHECK_CLOSE)) + return NULL; + + /* deletes the object, invalidate it on success */ + if (!lo_unlink(self->pgcnx->cnx, self->lo_oid)) + { + PyErr_SetString(PyExc_IOError, "error while unlinking large object"); + return NULL; + } + self->lo_oid = 0; + + Py_INCREF(Py_None); + return Py_None; +} + +/* large object methods */ +static struct PyMethodDef pglarge_methods[] = { + {"open", (PyCFunction) pglarge_open, 1}, /* opens large object */ + {"close", (PyCFunction) pglarge_close, 1},/* closes large object */ + {"read", (PyCFunction) pglarge_read, 1}, /* reads from large object */ + {"write", (PyCFunction) pglarge_write, 1},/* writes to large object */ + {"seek", (PyCFunction) pglarge_lseek, 1},/* seeks position */ + {"size", (PyCFunction) pglarge_size, 1}, /* gives object size */ + {"tell", (PyCFunction) pglarge_tell, 1}, /* gives position in lobj */ + {"export", (PyCFunction) pglarge_export, 1},/* exports to unix file */ + {"unlink", (PyCFunction) pglarge_unlink, 1},/* deletes a large object */ + {NULL, NULL} /* sentinel */ +}; + +/* get attribute */ +static PyObject * +pglarge_getattr(pglargeobject * self, char *name) +{ + /* list postgreSQL large object fields */ + + /* associated pg connection object */ + if (!strcmp(name, "pgcnx")) + { + if (check_lo(self, 0)) + { + Py_INCREF(self->pgcnx); + return (PyObject *) (self->pgcnx); + } + + Py_INCREF(Py_None); + return Py_None; + } + + /* large object oid */ + if (!strcmp(name, "oid")) + { + if (check_lo(self, 0)) + return PyInt_FromLong(self->lo_oid); + + Py_INCREF(Py_None); + return Py_None; + } + + /* error (status) message */ + if (!strcmp(name, "error")) + return PyString_FromString(PQerrorMessage(self->pgcnx->cnx)); + + /* attributes list */ + if (!strcmp(name, "__members__")) + { + PyObject *list = PyList_New(3); + + if (list) + { + PyList_SetItem(list, 0, PyString_FromString("oid")); + PyList_SetItem(list, 1, PyString_FromString("pgcnx")); + PyList_SetItem(list, 2, PyString_FromString("error")); + } + + return list; + } + + return Py_FindMethod(pglarge_methods, (PyObject *) self, name); +} + +/* object type definition */ +staticforward PyTypeObject PglargeType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "pglarge", /* tp_name */ + sizeof(pglargeobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + + /* methods */ + (destructor) pglarge_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) pglarge_getattr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ +}; + +#endif /* LARGE_OBJECTS */ + + +/* --------------------------------------------------------------------- */ + +/* PG CONNECTION OBJECT IMPLEMENTATION */ + +/* pgobject initialisation (from module) */ + +static PyObject * +pgconnect(pgobject *self, PyObject *args, PyObject *dict) +{ + static const char *kwlist[] = { "dbname", "host", "port", "opt", + "tty", "user", "passwd" , NULL }; + char *pghost, *pgopt, *pgtty, *pgdbname, *pguser, *pgpasswd; + int pgport; + char port_buffer[20]; + pgobject *npgobj; + + pghost = pgopt = pgtty = pgdbname = pguser = pgpasswd = NULL; + pgport = -1; + + /* parses standard arguments */ + if (!PyArg_ParseTupleAndKeywords(args, dict, "|zzlzzzz", kwlist, + &pgdbname, &pghost, &pgport, &pgopt, &pgtty, &pguser, &pgpasswd)) + return NULL; + +#ifdef DEFAULT_VARS + /* handles defaults variables (for unintialised vars) */ + if ((!pghost) && (pg_default_host != Py_None)) + pghost = PyString_AsString(pg_default_host); + + if ((pgport == -1) && (pg_default_port != Py_None)) + pgport = PyInt_AsLong(pg_default_port); + + if ((!pgopt) && (pg_default_opt != Py_None)) + pgopt = PyString_AsString(pg_default_opt); + + if ((!pgtty) && (pg_default_tty != Py_None)) + pgtty = PyString_AsString(pg_default_tty); + + if ((!pgdbname) && (pg_default_base != Py_None)) + pgdbname = PyString_AsString(pg_default_base); + + if ((!pguser) && (pg_default_user != Py_None)) + pguser = PyString_AsString(pg_default_user); + + if ((!pgpasswd) && (pg_default_passwd != Py_None)) + pgpasswd = PyString_AsString(pg_default_passwd); +#endif /* DEFAULT_VARS */ + + if ((npgobj = PyObject_NEW(pgobject, &PgType)) == NULL) + return NULL; + + if (pgport != -1) + { + bzero(port_buffer, sizeof(port_buffer)); + sprintf(port_buffer, "%d", pgport); + npgobj->cnx = PQsetdbLogin(pghost, port_buffer, pgopt, pgtty, pgdbname, + pguser, pgpasswd); + } + else + npgobj->cnx = PQsetdbLogin(pghost, NULL, pgopt, pgtty, pgdbname, + pguser, pgpasswd); + + if (PQstatus(npgobj->cnx) == CONNECTION_BAD) + { + PyErr_SetString(PGError, PQerrorMessage(npgobj->cnx)); + Py_XDECREF(npgobj); + return NULL; + } + + return (PyObject *) npgobj; +} + +/* pgobject methods */ + +/* destructor */ +static void +pg_dealloc(pgobject * self) +{ + if (self->cnx) + PQfinish(self->cnx); + + PyMem_DEL(self); +} + +/* close without deleting */ +static PyObject * +pg_close(pgobject *self, PyObject *args) +{ + /* gets args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_TypeError, "close()."); + return NULL; + } + + if (self->cnx) + PQfinish(self->cnx); + + self->cnx = NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static void +pg_querydealloc(pgqueryobject * self) +{ + if (self->last_result) + PQclear(self->last_result); + + PyMem_DEL(self); +} + +/* resets connection */ +static PyObject * +pg_reset(pgobject * self, PyObject * args) +{ + if (!self->cnx) + { + PyErr_SetString(PyExc_TypeError, "Connection is not valid"); + return NULL; + } + + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method reset() takes no parameters."); + return NULL; + } + + /* resets the connection */ + PQreset(self->cnx); + Py_INCREF(Py_None); + return Py_None; +} + +/* get connection socket */ +static PyObject * +pg_fileno(pgobject * self, PyObject * args) +{ + if (!self->cnx) + { + PyErr_SetString(PyExc_TypeError, "Connection is not valid"); + return NULL; + } + + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method fileno() takes no parameters."); + return NULL; + } + +#ifdef NO_PQSOCKET + return PyInt_FromLong((long) self->cnx->sock); +#else + return PyInt_FromLong((long) PQsocket(self->cnx)); +#endif +} + +/* list fields names from query result */ +static PyObject * +pg_listfields(pgqueryobject * self, PyObject * args) +{ + int i, n; + char *name; + PyObject *fieldstuple, *str; + + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method listfields() takes no parameters."); + return NULL; + } + + /* builds tuple */ + n = PQnfields(self->last_result); + fieldstuple = PyTuple_New(n); + + for (i = 0; i < n; i++) + { + name = PQfname(self->last_result, i); + str = PyString_FromString(name); + PyTuple_SetItem(fieldstuple, i, str); + } + + return fieldstuple; +} + +/* get field name from last result */ +static PyObject * +pg_fieldname(pgqueryobject * self, PyObject * args) +{ + int i; + char *name; + + /* gets args */ + if (!PyArg_ParseTuple(args, "i", &i)) + { + PyErr_SetString(PyExc_TypeError, + "fieldname(number), with number(integer)."); + return NULL; + } + + /* checks number validity */ + if (i >= PQnfields(self->last_result)) + { + PyErr_SetString(PyExc_ValueError, "invalid field number."); + return NULL; + } + + /* gets fields name and builds object */ + name = PQfname(self->last_result, i); + return PyString_FromString(name); +} + +/* gets fields number from name in last result */ +static PyObject * +pg_fieldnum(pgqueryobject * self, PyObject * args) +{ + char *name; + int num; + + /* gets args */ + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "fieldnum(name), with name (string)."); + return NULL; + } + + /* gets field number */ + if ((num = PQfnumber(self->last_result, name)) == -1) + { + PyErr_SetString(PyExc_ValueError, "Unknown field."); + return NULL; + } + + return PyInt_FromLong(num); +} + +/* retrieves last result */ +static PyObject * +pg_getresult(pgqueryobject * self, PyObject * args) +{ + PyObject *rowtuple, *reslist, *val; + int i, j, m, n, *typ; + + /* checks args (args == NULL for an internal call) */ + if ((args != NULL) && (!PyArg_ParseTuple(args, ""))) + { + PyErr_SetString(PyExc_SyntaxError, + "method getresult() takes no parameters."); + return NULL; + } + + /* stores result in tuple */ + reslist = PyList_New(0); + m = PQntuples(self->last_result); + n = PQnfields(self->last_result); + + if ((typ = malloc(sizeof(int) * n)) == NULL) + { + PyErr_SetString(PyExc_SyntaxError, "memory error in getresult()."); + return NULL; + } + + for (j = 0; j < n; j++) + { + switch (PQftype(self->last_result, j)) + { + case INT2OID: + case INT4OID: + case OIDOID: + typ[j] = 1; + break; + + case FLOAT4OID: + case FLOAT8OID: + typ[j] = 2; + break; + + case CASHOID: + typ[j] = 3; + break; + + default: + typ[j] = 4; + break; + } + } + + for (i = 0; i < m; i++) + { + rowtuple = PyTuple_New(n); + for (j = 0; j < n; j++) + { + char *s = PQgetvalue(self->last_result, i, j); + char cashbuf[64]; + + switch (typ[j]) + { + case 1: + val = PyInt_FromLong(strtol(s, NULL, 10)); + break; + + case 2: + val = PyFloat_FromDouble(strtod(s, NULL)); + break; + + case 3: /* get rid of the '$' and commas */ + if (*s == '$') /* there's talk of getting rid of it */ + s++; + + for (i = 0; *s; s++) + if (*s != ',') + cashbuf[i++] = *s; + + cashbuf[i] = 0; + val = PyFloat_FromDouble(strtod(cashbuf, NULL)); + break; + + default: + val = PyString_FromString(s); + break; + } + + PyTuple_SetItem(rowtuple, j, val); + } + + PyList_Append(reslist, rowtuple); + Py_XDECREF(rowtuple); + } + + free(typ); + + /* returns list */ + return reslist; +} + +/* retrieves last result as a list of dictionaries*/ +static PyObject * +pg_dictresult(pgqueryobject * self, PyObject * args) +{ + PyObject *dict, *reslist, *val; + int i, j, m, n, *typ; + + /* checks args (args == NULL for an internal call) */ + if ((args != NULL) && (!PyArg_ParseTuple(args, ""))) + { + PyErr_SetString(PyExc_SyntaxError, + "method getresult() takes no parameters."); + return NULL; + } + + /* stores result in list */ + reslist = PyList_New(0); + m = PQntuples(self->last_result); + n = PQnfields(self->last_result); + + if ((typ = malloc(sizeof(int) * n)) == NULL) + { + PyErr_SetString(PyExc_SyntaxError, "memory error in getresult()."); + return NULL; + } + + for (j = 0; j < n; j++) + { + switch (PQftype(self->last_result, j)) + { + case INT2OID: + case INT4OID: + case OIDOID: + typ[j] = 1; + break; + + case FLOAT4OID: + case FLOAT8OID: + typ[j] = 2; + break; + + case CASHOID: + typ[j] = 3; + break; + + default: + typ[j] = 4; + break; + } + } + + for (i = 0; i < m; i++) + { + dict = PyDict_New(); + for (j = 0; j < n; j++) + { + int k; + char *s = PQgetvalue(self->last_result, i, j); + char cashbuf[64]; + + switch (typ[j]) + { + case 1: + val = PyInt_FromLong(strtol(s, NULL, 10)); + break; + + case 2: + val = PyFloat_FromDouble(strtod(s, NULL)); + break; + + case 3: /* get rid of the '$' and commas */ + if (*s == '$') /* there's talk of getting rid of it */ + s++; + + for (k = 0; *s; s++) + if (*s != ',') + cashbuf[k++] = *s; + + cashbuf[k] = 0; + val = PyFloat_FromDouble(strtod(cashbuf, NULL)); + break; + + default: + val = PyString_FromString(s); + break; + } + + PyDict_SetItemString(dict, PQfname(self->last_result, j), val); + Py_XDECREF(val); + } + + PyList_Append(reslist, dict); + Py_XDECREF(dict); + } + + free(typ); + + /* returns list */ + return reslist; +} + +/* getq asynchronous notify */ +static PyObject * +pg_getnotify(pgobject * self, PyObject * args) +{ + PGnotify *notify; + PGresult *result; + PyObject *notify_result, *temp; + + if (!self->cnx) + { + PyErr_SetString(PyExc_TypeError, "Connection is not valid"); + return NULL; + } + + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method getnotify() takes no parameters."); + return NULL; + } + + /* gets notify and builds result */ + /* notifies only come back as result of a query, so I send an empty query */ + result = PQexec(self->cnx, " "); + + if ((notify = PQnotifies(self->cnx)) != NULL) + { + notify_result = PyTuple_New(2); + temp = PyString_FromString(notify->relname); + PyTuple_SetItem(notify_result, 0, temp); + temp = PyInt_FromLong(notify->be_pid); + PyTuple_SetItem(notify_result, 1, temp); + free(notify); + } + else + { + Py_INCREF(Py_None); + notify_result = Py_None; + } + + PQclear(result); + + /* returns result */ + return notify_result; +} + +/* database query */ +static PyObject * +pg_query(pgobject * self, PyObject * args) +{ + char *query; + PGresult *result; + pgqueryobject *npgobj; + int status; + + if (!self->cnx) + { + PyErr_SetString(PyExc_TypeError, "Connection is not valid"); + return NULL; + } + + /* get query args */ + if (!PyArg_ParseTuple(args, "s", &query)) + { + PyErr_SetString(PyExc_TypeError, "query(sql), with sql (string)."); + return NULL; + } + + /* gets result */ + result = PQexec(self->cnx, query); + + /* checks result validity */ + if (!result) + { + PyErr_SetString(PyExc_ValueError, PQerrorMessage(self->cnx)); + return NULL; + } + + /* checks result status */ + if ((status = PQresultStatus(result)) != PGRES_TUPLES_OK) + { + const char *str; + + PQclear(result); + + switch (status) + { + case PGRES_EMPTY_QUERY: + PyErr_SetString(PyExc_ValueError, "empty query."); + break; + case PGRES_BAD_RESPONSE: + case PGRES_FATAL_ERROR: + case PGRES_NONFATAL_ERROR: + PyErr_SetString(PGError, PQerrorMessage(self->cnx)); + break; + case PGRES_COMMAND_OK: /* could be an INSERT */ + if (*(str = PQoidStatus(result)) == 0) /* nope */ + { + Py_INCREF(Py_None); + return Py_None; + } + + /* otherwise, return the oid */ + return PyInt_FromLong(strtol(str, NULL, 10)); + + case PGRES_COPY_OUT: /* no data will be received */ + case PGRES_COPY_IN: + Py_INCREF(Py_None); + return Py_None; + default: + PyErr_SetString(PGError, "internal error: " + "unknown result status."); + break; + } + + return NULL; /* error detected on query */ + } + + if ((npgobj = PyObject_NEW(pgqueryobject, &PgQueryType)) == NULL) + return NULL; + + /* stores result and returns object */ + npgobj->last_result = result; + return (PyObject *) npgobj; +} + +#ifdef DIRECT_ACCESS +/* direct acces function : putline */ +static PyObject * +pg_putline(pgobject * self, PyObject * args) +{ + char *line; + + if (!self->cnx) + { + PyErr_SetString(PyExc_TypeError, "Connection is not valid"); + return NULL; + } + + /* reads args */ + if (!PyArg_ParseTuple(args, "s", &line)) + { + PyErr_SetString(PyExc_TypeError, "putline(line), with line (string)."); + return NULL; + } + + /* sends line to backend */ + PQputline(self->cnx, line); + Py_INCREF(Py_None); + return Py_None; +} + +/* direct access function : getline */ +static PyObject * +pg_getline(pgobject * self, PyObject * args) +{ + char line[MAX_BUFFER_SIZE]; + PyObject *str = NULL; /* GCC */ + int ret; + + if (!self->cnx) + { + PyErr_SetString(PyExc_TypeError, "Connection is not valid"); + return NULL; + } + + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method getline() takes no parameters."); + return NULL; + } + + /* gets line */ + switch (PQgetline(self->cnx, line, MAX_BUFFER_SIZE)) + { + case 0: + str = PyString_FromString(line); + break; + case 1: + PyErr_SetString(PyExc_MemoryError, "buffer overflow"); + str = NULL; + break; + case EOF: + Py_INCREF(Py_None); + str = Py_None; + break; + } + + return str; +} + +/* direct access function : end copy */ +static PyObject * +pg_endcopy(pgobject * self, PyObject * args) +{ + if (!self->cnx) + { + PyErr_SetString(PyExc_TypeError, "Connection is not valid"); + return NULL; + } + + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method endcopy() takes no parameters."); + return NULL; + } + + /* ends direct copy */ + PQendcopy(self->cnx); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* DIRECT_ACCESS */ + + +static PyObject * +pg_print(pgqueryobject *self, FILE *fp, int flags) +{ + PQprintOpt op; + + memset(&op, 0, sizeof(op)); + op.align = 1; + op.header = 1; + op.fieldSep = "|"; + op.pager = 1; + PQprint(fp, self->last_result, &op); + return 0; +} + +/* insert table */ +static PyObject * +pg_inserttable(pgobject * self, PyObject * args) +{ + PGresult *result; + char *table, *buffer, *temp; + char temp_buffer[256]; + PyObject *list, *sublist, *item; + PyObject *(*getitem) (PyObject *, int); + PyObject *(*getsubitem) (PyObject *, int); + int i, j; + + if (!self->cnx) + { + PyErr_SetString(PyExc_TypeError, "Connection is not valid"); + return NULL; + } + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "sO:filter", &table, &list)) + { + PyErr_SetString(PyExc_TypeError, + "tableinsert(table, content), with table (string) " + "and content (list)."); + return NULL; + } + + /* checks list type */ + if (PyTuple_Check(list)) + getitem = PyTuple_GetItem; + else if (PyList_Check(list)) + getitem = PyList_GetItem; + else + { + PyErr_SetString(PyExc_TypeError, + "second arg must be some kind of array."); + return NULL; + } + + /* checks sublists type */ + for (i = 0; (sublist = getitem(list, i)) != NULL; i++) + { + if (!PyTuple_Check(sublist) && !PyList_Check(sublist)) + { + PyErr_SetString(PyExc_TypeError, + "second arg must contain some kind of arrays."); + return NULL; + } + } + + /* allocate buffer */ + if (!(buffer = malloc(MAX_BUFFER_SIZE))) + { + PyErr_SetString(PyExc_MemoryError, "can't allocate insert buffer."); + return NULL; + } + + /* starts query */ + sprintf(buffer, "copy %s from stdin", table); + + if (!(result = PQexec(self->cnx, buffer))) + { + free(buffer); + PyErr_SetString(PyExc_ValueError, PQerrorMessage(self->cnx)); + return NULL; + } + + PQclear(result); + + /* feeds table */ + for (i = 0; (sublist = getitem(list, i)) != NULL; i++) + { + if (PyTuple_Check(sublist)) + getsubitem = PyTuple_GetItem; + else + getsubitem = PyList_GetItem; + + /* builds insert line */ + buffer[0] = 0; + + for (j = 0; (item = getsubitem(sublist, j)) != NULL; j++) + { + /* converts item to string */ + if (PyString_Check(item)) + PyArg_ParseTuple(item, "s", &temp); + else if (PyInt_Check(item)) + { + int k; + + PyArg_ParseTuple(item, "i", &k); + sprintf(temp_buffer, "%d", k); + temp = temp_buffer; + } + else if (PyLong_Check(item)) + { + long k; + + PyArg_ParseTuple(item, "l", &k); + sprintf(temp_buffer, "%ld", k); + temp = temp_buffer; + } + else if (PyFloat_Check(item)) + { + double k; + + PyArg_ParseTuple(item, "d", &k); + sprintf(temp_buffer, "%g", k); + temp = temp_buffer; + } + else + { + free(buffer); + PyErr_SetString(PyExc_ValueError, + "items must be strings, integers, " + "longs or double (real)."); + return NULL; + } + + /* concats buffer */ + if (strlen(buffer)) + strncat(buffer, "\t", MAX_BUFFER_SIZE - strlen(buffer)); + + fprintf(stderr, "Buffer: '%s', Temp: '%s'\n", buffer, temp); + strncat(buffer, temp, MAX_BUFFER_SIZE - strlen(buffer)); + } + + strncat(buffer, "\n", MAX_BUFFER_SIZE - strlen(buffer)); + + /* sends data */ + PQputline(self->cnx, buffer); + } + + /* ends query */ + PQputline(self->cnx, ".\n"); + PQendcopy(self->cnx); + free(buffer); + + /* no error : returns nothing */ + Py_INCREF(Py_None); + return Py_None; +} + +/* connection object methods */ +static struct PyMethodDef pgobj_methods[] = { + {"query", (PyCFunction) pg_query, 1}, /* query method */ + {"reset", (PyCFunction) pg_reset, 1}, /* connection reset */ + {"close", (PyCFunction) pg_close, 1}, /* connection close */ + {"fileno", (PyCFunction) pg_fileno,1}, /* connection socket */ + {"getnotify", (PyCFunction) pg_getnotify, 1}, /* checks for notify */ + {"inserttable", (PyCFunction) pg_inserttable, 1}, /* table insert */ + +#ifdef DIRECT_ACCESS + {"putline", (PyCFunction) pg_putline, 1}, /* direct access: putline */ + {"getline", (PyCFunction) pg_getline, 1}, /* direct access: getline */ + {"endcopy", (PyCFunction) pg_endcopy, 1}, /* direct access: endcopy */ +#endif /* DIRECT_ACCESS */ + +#ifdef LARGE_OBJECTS + {"locreate", (PyCFunction) pg_locreate, 1}, /* creates large object */ + {"getlo", (PyCFunction) pg_getlo, 1}, /* get lo from oid */ + {"loimport", (PyCFunction) pg_loimport, 1}, /* imports lo from file */ +#endif /* LARGE_OBJECTS */ + + {NULL, NULL} /* sentinel */ +}; + +/* get attribute */ +static PyObject * +pg_getattr(pgobject * self, char *name) +{ + /* Although we could check individually, there are only a few + attributes that don't require a live connection and unless + someone has an urgent need, this will have to do */ + if (!self->cnx) + { + PyErr_SetString(PyExc_TypeError, "Connection is not valid"); + return NULL; + } + + /* list postgreSQL connection fields */ + + /* postmaster host */ + if (!strcmp(name, "host")) + { + char *r = PQhost(self->cnx); + return r ? PyString_FromString(r) : PyString_FromString("localhost"); + } + + /* postmaster port */ + if (!strcmp(name, "port")) + return PyInt_FromLong(atol(PQport(self->cnx))); + + /* selected database */ + if (!strcmp(name, "db")) + return PyString_FromString(PQdb(self->cnx)); + + /* selected options */ + if (!strcmp(name, "options")) + return PyString_FromString(PQoptions(self->cnx)); + + /* selected postgres tty */ + if (!strcmp(name, "tty")) + return PyString_FromString(PQtty(self->cnx)); + + /* error (status) message */ + if (!strcmp(name, "error")) + return PyString_FromString(PQerrorMessage(self->cnx)); + + /* connection status : 1 - OK, 0 - BAD */ + if (!strcmp(name, "status")) + return PyInt_FromLong(PQstatus(self->cnx) == CONNECTION_OK ? 1 : 0); + + /* provided user name */ + if (!strcmp(name, "user")) + return PyString_FromString("Deprecated facility"); + /* return PyString_FromString(fe_getauthname("")); */ + + /* attributes list */ + if (!strcmp(name, "__members__")) + { + PyObject *list = PyList_New(8); + + if (list) + { + PyList_SetItem(list, 0, PyString_FromString("host")); + PyList_SetItem(list, 1, PyString_FromString("port")); + PyList_SetItem(list, 2, PyString_FromString("db")); + PyList_SetItem(list, 3, PyString_FromString("options")); + PyList_SetItem(list, 4, PyString_FromString("tty")); + PyList_SetItem(list, 5, PyString_FromString("error")); + PyList_SetItem(list, 6, PyString_FromString("status")); + PyList_SetItem(list, 7, PyString_FromString("user")); + } + + return list; + } + + return Py_FindMethod(pgobj_methods, (PyObject *) self, name); +} + +/* object type definition */ +staticforward PyTypeObject PgType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "pgobject", /* tp_name */ + sizeof(pgobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor) pg_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) pg_getattr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ +}; + + +/* query object methods */ +static struct PyMethodDef pgquery_methods[] = { + {"getresult", (PyCFunction) pg_getresult, 1}, /* get last result */ + {"dictresult", (PyCFunction) pg_dictresult, 1}, /* get result as dict*/ + {"fieldname", (PyCFunction) pg_fieldname, 1}, /* get field name */ + {"fieldnum", (PyCFunction) pg_fieldnum, 1}, /* get field number */ + {"listfields", (PyCFunction) pg_listfields, 1}, /* list fields names */ + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +pg_querygetattr(pgqueryobject * self, char *name) +{ + /* list postgreSQL connection fields */ + return Py_FindMethod(pgquery_methods, (PyObject *) self, name); +} + +/* query type definition */ +staticforward PyTypeObject PgQueryType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "pgqueryobject", /* tp_name */ + sizeof(pgqueryobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor) pg_querydealloc,/* tp_dealloc */ + (printfunc) pg_print, /* tp_print */ + (getattrfunc) pg_querygetattr,/* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ +}; + + + +/* --------------------------------------------------------------------- */ + +/* MODULE FUNCTIONS */ + +#ifdef DEFAULT_VARS + +/* gets default host */ +static PyObject * +pggetdefhost(PyObject *self, PyObject *args) +{ + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method get_defhost() takes no parameter."); + return NULL; + } + + Py_XINCREF(pg_default_host); + return pg_default_host; +} + +/* sets default host */ +static PyObject * +pgsetdefhost(PyObject * self, PyObject *args) +{ + char *temp = NULL; + PyObject *old; + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "z", &temp)) + { + PyErr_SetString(PyExc_TypeError, + "set_defhost(name), with name (string/None)."); + return NULL; + } + + /* adjusts value */ + old = pg_default_host; + + if (temp) + pg_default_host = PyString_FromString(temp); + else + { + Py_INCREF(Py_None); + pg_default_host = Py_None; + } + + return old; +} + +/* gets default base */ +static PyObject * +pggetdefbase(PyObject * self, PyObject *args) +{ + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method get_defbase() takes no parameter."); + return NULL; + } + + Py_XINCREF(pg_default_base); + return pg_default_base; +} + +/* sets default base */ +static PyObject * +pgsetdefbase(PyObject * self, PyObject *args) +{ + char *temp = NULL; + PyObject *old; + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "z", &temp)) + { + PyErr_SetString(PyExc_TypeError, + "set_defbase(name), with name (string/None)."); + return NULL; + } + + /* adjusts value */ + old = pg_default_base; + + if (temp) + pg_default_base = PyString_FromString(temp); + else + { + Py_INCREF(Py_None); + pg_default_base = Py_None; + } + + return old; +} + +/* gets default options */ +static PyObject * +pggetdefopt(PyObject * self, PyObject *args) +{ + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method get_defopt() takes no parameter."); + return NULL; + } + + Py_XINCREF(pg_default_opt); + return pg_default_opt; +} + +/* sets default opt */ +static PyObject * +pgsetdefopt(PyObject * self, PyObject *args) +{ + char *temp = NULL; + PyObject *old; + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "z", &temp)) + { + PyErr_SetString(PyExc_TypeError, + "set_defopt(name), with name (string/None)."); + return NULL; + } + + /* adjusts value */ + old = pg_default_opt; + + if (temp) + pg_default_opt = PyString_FromString(temp); + else + { + Py_INCREF(Py_None); + pg_default_opt = Py_None; + } + + return old; +} + +/* gets default tty */ +static PyObject * +pggetdeftty(PyObject * self, PyObject *args) +{ + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method get_deftty() takes no parameter."); + return NULL; + } + + Py_XINCREF(pg_default_tty); + return pg_default_tty; +} + +/* sets default tty */ +static PyObject * +pgsetdeftty(PyObject * self, PyObject *args) +{ + char *temp = NULL; + PyObject *old; + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "z", &temp)) + { + PyErr_SetString(PyExc_TypeError, + "set_deftty(name), with name (string/None)."); + return NULL; + } + + /* adjusts value */ + old = pg_default_tty; + + if (temp) + pg_default_tty = PyString_FromString(temp); + else + { + Py_INCREF(Py_None); + pg_default_tty = Py_None; + } + + return old; +} + +/* gets default username */ +static PyObject * +pggetdefuser(PyObject * self, PyObject *args) +{ + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method get_defuser() takes no parameter."); + + return NULL; + } + + Py_XINCREF(pg_default_user); + return pg_default_user; +} + +/* sets default username */ +static PyObject * +pgsetdefuser(PyObject * self, PyObject *args) +{ + char *temp = NULL; + PyObject *old; + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "z", &temp)) + { + PyErr_SetString(PyExc_TypeError, + "set_defuser(name), with name (string/None)."); + return NULL; + } + + /* adjusts value */ + old = pg_default_user; + + if (temp) + pg_default_user = PyString_FromString(temp); + else + { + Py_INCREF(Py_None); + pg_default_user = Py_None; + } + + return old; +} + +/* sets default password */ +static PyObject * +pgsetdefpasswd(PyObject * self, PyObject *args) +{ + char *temp = NULL; + PyObject *old; + + /* gets arguments */ + if (!PyArg_ParseTuple(args, "z", &temp)) + { + PyErr_SetString(PyExc_TypeError, + "set_defpasswd(password), with password (string/ +None)."); + return NULL; + } + + /* adjusts value */ + old = pg_default_passwd; + + if (temp) + pg_default_passwd = PyString_FromString(temp); + else + { + Py_INCREF(Py_None); + pg_default_passwd = Py_None; + } + + return old; +} + +/* gets default port */ +static PyObject * +pggetdefport(PyObject * self, PyObject *args) +{ + /* checks args */ + if (!PyArg_ParseTuple(args, "")) + { + PyErr_SetString(PyExc_SyntaxError, + "method get_defport() takes no parameter."); + return NULL; + } + + Py_XINCREF(pg_default_port); + return pg_default_port; +} + +/* sets default port */ +static PyObject * +pgsetdefport(PyObject * self, PyObject *args) +{ + long int port = -2; + PyObject *old; + + /* gets arguments */ + if ((!PyArg_ParseTuple(args, "l", &port)) || (port < -1)) + { + PyErr_SetString(PyExc_TypeError, "set_defport(port), with port " + "(positive integer/-1)."); + return NULL; + } + + /* adjusts value */ + old = pg_default_port; + + if (port != -1) + pg_default_port = PyLong_FromLong(port); + else + { + Py_INCREF(Py_None); + pg_default_port = Py_None; + } + + return old; +} + +#endif /* DEFAULT_VARS */ + +/* List of functions defined in the module */ + +static struct PyMethodDef pg_methods[] = { + {"connect", (PyCFunction) pgconnect, 3}, /* connect to a database */ + +#ifdef DEFAULT_VARS + {"get_defhost", pggetdefhost, 1}, /* gets default host */ + {"set_defhost", pgsetdefhost, 1}, /* sets default host */ + {"get_defbase", pggetdefbase, 1}, /* gets default base */ + {"set_defbase", pgsetdefbase, 1}, /* sets default base */ + {"get_defopt", pggetdefopt, 1}, /* gets default options */ + {"set_defopt", pgsetdefopt, 1}, /* sets default options */ + {"get_deftty", pggetdeftty, 1}, /* gets default debug tty */ + {"set_deftty", pgsetdeftty, 1}, /* sets default debug tty */ + {"get_defport", pggetdefport, 1}, /* gets default port */ + {"set_defport", pgsetdefport, 1}, /* sets default port */ + {"get_defuser", pggetdefuser, 1}, /* gets default user */ + {"set_defuser", pgsetdefuser, 1}, /* sets default user */ + {"set_defpasswd", pgsetdefpasswd, 1}, /* sets default passwd */ +#endif /* DEFAULT_VARS */ + {NULL, NULL} /* sentinel */ +}; + +static char pg__doc__[] = "Python interface to PostgreSQL DB"; + +/* Initialization function for the module */ +void init_pg(void); /* Python doesn't prototype this */ +void +init_pg(void) +{ + PyObject *mod, *dict, *v; + + /* Initialize here because some WIN platforms get confused otherwise */ + PglargeType.ob_type = PgType.ob_type = PgQueryType.ob_type = &PyType_Type; + + /* Create the module and add the functions */ + mod = Py_InitModule4("_pg", pg_methods, pg__doc__, NULL, PYTHON_API_VERSION); + dict = PyModule_GetDict(mod); + + /* Add some symbolic constants to the module */ + PGError = PyString_FromString("pg.error"); + PyDict_SetItemString(dict, "error", PGError); + + /* Make the version available */ + v = PyString_FromString(PyPgVersion); + PyDict_SetItemString(dict, "version", v); + PyDict_SetItemString(dict, "__version__", v); + Py_DECREF(v); + +#ifdef LARGE_OBJECTS + /* create mode for large objects */ + PyDict_SetItemString(dict, "INV_READ", PyInt_FromLong(INV_READ)); + PyDict_SetItemString(dict, "INV_WRITE", PyInt_FromLong(INV_WRITE)); + + /* position flags for lo_lseek */ + PyDict_SetItemString(dict, "SEEK_SET", PyInt_FromLong(SEEK_SET)); + PyDict_SetItemString(dict, "SEEK_CUR", PyInt_FromLong(SEEK_CUR)); + PyDict_SetItemString(dict, "SEEK_END", PyInt_FromLong(SEEK_END)); +#endif /* LARGE_OBJECTS */ + +#ifdef DEFAULT_VARS + /* prepares default values */ + Py_INCREF(Py_None); pg_default_host = Py_None; + Py_INCREF(Py_None); pg_default_base = Py_None; + Py_INCREF(Py_None); pg_default_opt = Py_None; + Py_INCREF(Py_None); pg_default_port = Py_None; + Py_INCREF(Py_None); pg_default_tty = Py_None; + Py_INCREF(Py_None); pg_default_user = Py_None; + Py_INCREF(Py_None); pg_default_passwd = Py_None; +#endif /* DEFAULT_VARS */ + + /* Check for errors */ + if (PyErr_Occurred()) + Py_FatalError("can't initialize module _pg"); +} diff --git a/src/interfaces/python/pgsqldb.py b/src/interfaces/python/pgsqldb.py new file mode 100644 index 0000000000..df768b68b2 --- /dev/null +++ b/src/interfaces/python/pgsqldb.py @@ -0,0 +1,46 @@ +# pgsqldb.py +# Written by D'Arcy J.M. Cain + +# This library implements the DB-SIG API +# It includes the pg module and builds on it + +from _pg import * + +import string + +class _cursor: + """For cursor object""" + + def __init__(self, conn): + self.conn = conn + self.cursor = None + self.arraysize = 1 + self.description = None + self.name = string.split(`self`)[3][:-1] + + def close(self): + if self.conn == None: raise self.conn.error, "Cursor has been closed" + if self.cursor == None: raise self.conn.error, "No cursor created" + self.conn.query('CLOSE %s' % self.name) + self.conn = None + + def __del__(self): + if self.cursor != None and self.conn != None: + self.conn.query('CLOSE %s' % self.name) + + +class pgsqldb: + """This class wraps the pg connection type in a DB-SIG API interface""" + + def __init__(self, *args, **kw): + self.db = apply(connect, args, kw) + + # Create convience methods, in a way that is still overridable. + for e in ('query', 'reset', 'close', 'getnotify', 'inserttable', + 'putline', 'getline', 'endcopy', + 'host', 'port', 'db', 'options', + 'tty', 'error', 'status', 'user', + 'locreate', 'getlo', 'loimport'): + if not hasattr(self,e) and hasattr(self.db,e): + exec 'self.%s = self.db.%s' % ( e, e ) +