PyGreSQL-4.0004075500017500000000000000000001112713757600116755ustar darcywheelPyGreSQL-4.0/docs004075500017500000000000000000001112713757600126255ustar darcywheelPyGreSQL-4.0/docs/announce.txt010064400017500000000000000011471112713757600152530ustar darcywheel====================== PyGreSQL Announcements ====================== ------------------------------- Release of PyGreSQL version 4.0 ------------------------------- PyGreSQL v4.0 has been released. It is available at: ftp://ftp.PyGreSQL.org/pub/distrib/PyGreSQL-4.0.tgz. If you are running NetBSD, look in the packages directory under databases. There is also a package in the FreeBSD ports collection. Please refer to `changelog.txt `_ for things that have changed in this version. Please refer to `readme.txt `_ for general information. | D'Arcy J.M. Cain | darcy@PyGreSQL.org PyGreSQL-4.0/docs/default.css010064400017500000000000000126241112713757600150440ustar darcywheel/* :Author: David Goodger :Contact: goodger@users.sourceforge.net :Date: $Date: 2006/01/22 16:15:33 $ :Revision: $Revision: 1.2 $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to customize this style sheet. */ /* used to remove borders from tables and images */ .borderless, table.borderless td, table.borderless th { border: 0 } table.borderless td, table.borderless th { /* Override padding for "table.docutils td" with "! important". The right padding separates the table cells. */ padding: 0 0.5em 0 0 ! important } .first { /* Override more specific margin styles with "! important". */ margin-top: 0 ! important } .last, .with-subtitle { margin-bottom: 0 ! important } .hidden { display: none } a.toc-backref { text-decoration: none ; color: black } blockquote.epigraph { margin: 2em 5em ; } dl.docutils dd { margin-bottom: 0.5em } /* Uncomment (and remove this text!) to get bold-faced definition list terms dl.docutils dt { font-weight: bold } */ div.abstract { margin: 2em 5em } div.abstract p.topic-title { font-weight: bold ; text-align: center } div.admonition, div.attention, div.caution, div.danger, div.error, div.hint, div.important, div.note, div.tip, div.warning { margin: 2em ; border: medium outset ; padding: 1em } div.admonition p.admonition-title, div.hint p.admonition-title, div.important p.admonition-title, div.note p.admonition-title, div.tip p.admonition-title { font-weight: bold ; font-family: sans-serif } div.attention p.admonition-title, div.caution p.admonition-title, div.danger p.admonition-title, div.error p.admonition-title, div.warning p.admonition-title { color: red ; font-weight: bold ; font-family: sans-serif } /* Uncomment (and remove this text!) to get reduced vertical space in compound paragraphs. div.compound .compound-first, div.compound .compound-middle { margin-bottom: 0.5em } div.compound .compound-last, div.compound .compound-middle { margin-top: 0.5em } */ div.dedication { margin: 2em 5em ; text-align: center ; font-style: italic } div.dedication p.topic-title { font-weight: bold ; font-style: normal } div.figure { margin-left: 2em ; margin-right: 2em } div.footer, div.header { clear: both; font-size: smaller } div.line-block { display: block ; margin-top: 1em ; margin-bottom: 1em } div.line-block div.line-block { margin-top: 0 ; margin-bottom: 0 ; margin-left: 1.5em } div.sidebar { margin-left: 1em ; border: medium outset ; padding: 1em ; background-color: #ffffee ; width: 40% ; float: right ; clear: right } div.sidebar p.rubric { font-family: sans-serif ; font-size: medium } div.system-messages { margin: 5em } div.system-messages h1 { color: red } div.system-message { border: medium outset ; padding: 1em } div.system-message p.system-message-title { color: red ; font-weight: bold } div.topic { margin: 2em } h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { margin-top: 0.4em } h1.title { text-align: center } h2.subtitle { text-align: center } hr.docutils { width: 75% } img.align-left { clear: left } img.align-right { clear: right } ol.simple, ul.simple { margin-bottom: 1em } ol.arabic { list-style: decimal } ol.loweralpha { list-style: lower-alpha } ol.upperalpha { list-style: upper-alpha } ol.lowerroman { list-style: lower-roman } ol.upperroman { list-style: upper-roman } p.attribution { text-align: right ; margin-left: 50% } p.caption { font-style: italic } p.credits { font-style: italic ; font-size: smaller } p.label { white-space: nowrap } p.rubric { font-weight: bold ; font-size: larger ; color: maroon ; text-align: center } p.sidebar-title { font-family: sans-serif ; font-weight: bold ; font-size: larger } p.sidebar-subtitle { font-family: sans-serif ; font-weight: bold } p.topic-title { font-weight: bold } pre.address { margin-bottom: 0 ; margin-top: 0 ; font-family: serif ; font-size: 100% } pre.literal-block, pre.doctest-block { margin-left: 2em ; margin-right: 2em ; background-color: #eeeeee } span.classifier { font-family: sans-serif ; font-style: oblique } span.classifier-delimiter { font-family: sans-serif ; font-weight: bold } span.interpreted { font-family: sans-serif } span.option { white-space: nowrap } span.pre { white-space: pre } span.problematic { color: red } span.section-subtitle { /* font-size relative to parent (h1..h6 element) */ font-size: 80% } table.citation { border-left: solid 1px gray; margin-left: 1px } table.docinfo { margin: 2em 4em } table.docutils { margin-top: 0.5em ; margin-bottom: 0.5em } table.footnote { border-left: solid 1px black; margin-left: 1px } table.docutils td, table.docutils th, table.docinfo td, table.docinfo th { padding-left: 0.5em ; padding-right: 0.5em ; vertical-align: top } table.docutils th.field-name, table.docinfo th.docinfo-name { font-weight: bold ; text-align: left ; white-space: nowrap ; padding-left: 0 } h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { font-size: 100% } tt.docutils { background-color: #eeeeee } ul.auto-toc { list-style-type: none } PyGreSQL-4.0/docs/readme.html010064400017500000000000000331271112713757600150320ustar darcywheel PyGreSQL - Python interface for PostgreSQL

PyGreSQL - Python interface for PostgreSQL

PyGreSQL version 4.0

Copyright notice

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

Further modifications copyright (c) 1997-2008 by D'Arcy J.M. Cain (darcy@druid.net)

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.

Introduction

PostgreSQL is a highly scalable, SQL compliant, open source object-relational database management system. With more than 15 years of development history, it is quickly becoming the de facto database for enterprise level open source solutions. Best of all, PostgreSQL's source code is available under the most liberal open source license: the BSD license.

Python Python is an interpreted, interactive, object-oriented programming language. It is often compared to Tcl, Perl, Scheme or Java. Python combines remarkable power with very clear syntax. It has modules, classes, exceptions, very high level dynamic data types, and dynamic typing. There are interfaces to many system calls and libraries, as well as to various windowing systems (X11, Motif, Tk, Mac, MFC). New built-in modules are easily written in C or C++. Python is also usable as an extension language for applications that need a programmable interface. The Python implementation is copyrighted but freely usable and distributable, even for commercial use.

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 is developed and tested on a NetBSD system, but it should also run on most other platforms where PostgreSQL and Python is running. It is based on the PyGres95 code written by Pascal Andre (andre@chimay.via.ecp.fr). D'Arcy (darcy@druid.net) renamed it to PyGreSQL starting with version 2.0 and serves as the "BDFL" of PyGreSQL.

The current version PyGreSQL 4.0 needs PostgreSQL 7.2 and Python 2.3 or above.

Distribution files

pgmodule.c the C Python module (_pg)
pg.py the "classic" PyGreSQL module
pgdb.py DB-SIG DB-API 2.0 compliant API wrapper for PygreSQL
docs/

documentation directory

Contains: readme.txt, announce.txt, install.txt, changelog.txt, future.txt, pg.txt and pgdb.txt.

All text files are in ReST format, so HTML versions can be easily created with buildhtml.py from docutils.

tutorial/

demos directory

Contains: basics.py, syscat.py, advanced.py and func.py.

The samples here have been taken from the PostgreSQL manual and were used for module testing. They demonstrate some PostgreSQL features.

Installation

You will find the installing instructions in install.txt.

Information and support

For support

Python:
see http://www.python.org/community/
PostgreSQL:
see http://www.postgresql.org/support/
PyGreSQL:

Contact the PyGreSQL mailing list concerning PyGreSQL 2.0 and up.

If you would like to proposes changes, please join the PyGreSQL mailing list and send context diffs there.

See http://mailman.vex.net/mailman/listinfo/pygresql to join the mailing list.

Please note that messages to individual developers will generally not be answered directly. All questions, comments and code changes must be submitted to the mailing list for peer review and archiving.

PyGreSQL programming information

You may either choose to use the "classic" PyGreSQL interface provided by the pg module or else the newer DB-API 2.0 compliant interface provided by the pgdb module.

DB-API 2.0 (Python Database API Specification v2.0) is a specification for connecting to databases (not only PostGreSQL) from Python that has been developed by the Python DB-SIG in 1999.

The programming information is available in the files pg.txt and pgdb.txt.

Note that PyGreSQL is not thread-safe on the connection level. Therefore we recommend using DBUtils <http://www.webwareforpython.org/DBUtils> for multi-threaded environments, which supports both PyGreSQL interfaces.

ChangeLog and Future

The ChangeLog with past changes is in the file changelog.txt.

A to do list and wish list is in the file future.txt.

PyGreSQL-4.0/docs/pgdb.html010064400017500000000000000054311112713757600145060ustar darcywheel PyGreSQL Programming Information

PyGreSQL Programming Information

The DB-API compliant interface (pgdb module)

1   Introduction

You may either choose to use the "classic" PyGreSQL interface provided by the pg module or else the DB-API 2.0 compliant interface provided by the pgdb module.

DB-API 2.0 (Python Database API Specification v2.0) is a specification for connecting to databases (not only PostGreSQL) from Python that has been developed by the Python DB-SIG in 1999.

The following documentation covers only the newer pgdb API.

The authoritative programming information for the DB-API is availabe at
http://www.python.org/dev/peps/pep-0249/
A tutorial-like introduction to the DB-API can be found at
http://www2.linuxjournal.com/lj-issues/issue49/2605.html

2   The pgdb module

Note

This section of the documentation still needs to be written.

PyGreSQL-4.0/docs/pg.html010064400017500000000000003045131112713757600142030ustar darcywheel PyGreSQL Programming Information

PyGreSQL Programming Information

The classic PyGreSQL interface (pg module)

Contents

1   Introduction

You may either choose to use the "classic" PyGreSQL interface provided by the pg module or else the DB-API 2.0 compliant interface provided by the pgdb module.

The following documentation covers only the older pg API.

The pg module handles three types of objects,

  • the pgobject, which handles the connection and all the requests to the database,
  • the pglarge object, which handles all the accesses to PostgreSQL large objects,
  • the pgqueryobject that handles query results

and it provides a convenient wrapper class DB for the pgobject.

If you want to see a simple example of the use of some of these functions, see http://ontario.bikerides.ca where you can find a link at the bottom to the actual Python code for the page.

2   Module functions and constants

The pg module defines a few functions that allow to connect to a database and 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 the tag [DV].

All variables are set to None at module initialization, specifying that standard environment variables should be used.

2.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:If successful, the pgobject handling the connection
Exceptions raised:
TypeError:bad argument type, or too many arguments
SyntaxError:duplicate argument definition
pg.InternalError:
 some error occurred during pg connection definition

(plus all exceptions relative to object allocation)

Description:
This function 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.

Examples:

import pg

con1 = pg.connect('testdb', 'myhost', 5432, None, None, 'bob', None)
con2 = pg.connect(dbname='testdb', host='localhost', user='bob')

2.2   get_defhost, set_defhost - default server host [DV]

Syntax:

get_defhost()
Parameters:
None
Return type:
string, None:default host specification
Exceptions raised:
TypeError: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.3   get_defport, set_defport - default server port [DV]

Syntax:

get_defport()
Parameters:
None
Return type:
integer, None:default port specification
Exceptions raised:
TypeError: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.4   get_defopt, set_defopt - default connection options [DV]

Syntax:

get_defopt()
Parameters:
None
Return type:
string, None:default options specification
Exceptions raised:
TypeError: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.5   get_deftty, set_deftty - default debug tty [DV]

Syntax:

get_deftty()
Parameters:
None
Return type:
string, None:default debug terminal specification
Exceptions raised:
TypeError: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.6   get_defbase, set_defbase - default database name [DV]

Syntax:

get_defbase()
Parameters:
None
Return type:
string, None:default database name specification
Exceptions raised:
TypeError: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.7   escape_string - escape a string for use within SQL

Syntax:

escape_string(string)
Parameters:
string:the string that is to be escaped
Return type:
str:the escaped string
Exceptions raised:
TypeError:bad argument type, or too many arguments
Description:
This function escapes a string for use within an SQL command. This is useful when inserting data values as literal constants in SQL commands. Certain characters (such as quotes and backslashes) must be escaped to prevent them from being interpreted specially by the SQL parser. escape_string performs this operation. Note that there is also a pgobject method with the same name which takes connection properties into account.

Caution!

It is especially important to do proper escaping when handling strings that were received from an untrustworthy source. Otherwise there is a security risk: you are vulnerable to "SQL injection" attacks wherein unwanted SQL commands are fed to your database.

Example:

name = raw_input("Name? ")
phone = con.query("select phone from employees"
  " where name='%s'" % escape_string(name)).getresult()

2.8   escape_bytea - escape binary data for use within SQL as type bytea

Syntax:

escape_bytea(datastring)
Parameters:
datastring:string containing the binary data that is to be escaped
Return type:
str:the escaped string
Exceptions raised:
TypeError:bad argument type, or too many arguments
Description:
Escapes binary data for use within an SQL command with the type bytea. As with escape_string, this is only used when inserting data directly into an SQL command string. Note that there is also a pgobject method with the same name which takes connection properties into account.

Example:

picture = file('garfield.gif', 'rb').read()
con.query("update pictures set img='%s' where name='Garfield'"
  % escape_bytea(picture))

2.9   unescape_bytea -- unescape bytea data that has been retrieved as text

Syntax:

unescape_bytea(string)
Parameters:
datastring:the bytea data string that has been retrieved as text
Return type:
str:string containing the binary data
Exceptions raised:
TypeError:bad argument type, or too many arguments
Description:
Converts an escaped string representation of binary data into binary data - the reverse of escape_bytea. This is needed when retrieving bytea data with the getresult() or dictresult() method.

Example:

picture = unescape_bytea(con.query(
  "select img from pictures where name='Garfield'").getresult[0][0])
file('garfield.gif', 'wb').write(picture)

2.10   set_decimal -- set a decimal type to be used for numeric values

Syntax:

set_decimal(cls)
Parameters:
cls:the Python class to be used for PostgreSQL numeric values
Description:
This function can be used to specify the Python class that shall be used by PyGreSQL to hold PostgreSQL numeric values. The default class is decimal.Decimal if available, otherwise the float type is used.

2.11   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 the libpq description in the PostgreSQL user manual for more information about them. These constants are:

version, __version__:
 constants that give the current version.
INV_READ, INV_WRITE:
 large objects access modes, used by (pgobject.)locreate and (pglarge.)open
SEEK_SET, SEEK_CUR, SEEK_END:
 positional flags, used by (pglarge.)seek

3   Connection objects: pgobject

This object handles 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.

Caution!

Some methods give direct access to the connection socket. 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.

These methods are specified by the tag [DA].

Note

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].

3.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 or lost connection
pg.ProgrammingError:
 error in query
pg.InternalError':
 error during query processing
Description:
This method simply sends a SQL query to the database. If the query is an insert statement that inserted exactly one row into a table that has OIDs, the return value is the OID of the newly inserted row. If the query is an update or delete statement, or an insert statement that did not insert exactly one row in a table with OIDs, then the numer of rows affected is returned as a string. If it is a statement that returns rows as a result (usually a select statement, but maybe also an "insert/update ... returning" statement), this method returns a pgqueryobject that can be accessed via the getresult() or dictresult() method or simply printed. Otherwise, it returns None.

3.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 connection.

3.3   cancel - abandon processing of current SQL command

Syntax:

cancel()
Parameters:
None
Return type:
None
Exceptions raised:
TypeError:too many (any) arguments
Description:
This method requests that the server abandon processing of the current SQL command.

3.4   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.

3.5   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.

3.6   getnotify - gets the last notify from the server

Syntax:

getnotify()
Parameters:
None
Return type:
tuple, None:last notify from server
Exceptions raised:
TypeError:too many parameters
TypeError: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.

3.7   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
Exceptions raised:
TypeError:invalid connection, bad argument type, or too many arguments
MemoryError:insert buffer could not be allocated
ValueError:unsupported values
Description:
This method allow to quickly insert large blocks of data in a table: It inserts the whole values list into the given table. Internally, it uses the COPY command of the PostgreSQL database. 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.

Caution!

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.

3.8   putline - writes a line to the server socket [DA]

Syntax:

putline(line)
Parameters:
line:line to be written (string)
Return type:
None
Exceptions raised:
TypeError:invalid connection, bad parameter type, or too many parameters
Description:
This method allows to directly write a string to the server socket.

3.9   getline - gets a line from server socket [DA]

Syntax:

getline()
Parameters:
None
Return type:
string:the line read
Exceptions raised:
TypeError:invalid connection
TypeError:too many parameters
MemoryError:buffer overflow
Description:
This method allows to directly read a string from the server socket.

3.10   endcopy - synchronizes client and server [DA]

Syntax:

endcopy()
Parameters:
None
Return type:
None
Exceptions raised:
TypeError:invalid connection
TypeError: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.

3.11   locreate - create a large object in the database [LO]

Syntax:

locreate(mode)
Parameters:
mode:large object create mode
Return type:
pglarge:object handling the PostGreSQL large object

Exceptions raised:

TypeError:invalid connection, bad parameter type, or too many parameters
pg.OperationalError:
 creation error
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.

3.12   getlo - build 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 PostGreSQL large object
Exceptions raised:
TypeError:invalid connection, 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.

3.13   loimport - import a file to a large object [LO]

Syntax:

loimport(name)
Parameters:
name:the name of the file to be imported (string)
Return type:
pglarge:object handling the PostGreSQL large object
Exceptions raised:
TypeError:invalid connection, bad argument type, or too many arguments
pg.OperationalError:
 error during file import
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.

3.14   Object attributes

Every pgobject defines a set of read-only attributes that describe the connection and its status. These attributes are:

host:the host name 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:user name on the database system (string)
protocol_version:
 the frontend/backend protocol being used (integer)
server_version:the backend version (integer, e.g. 80305 for 8.3.5)
status:the status of the connection (integer: 1 - OK, 0 - bad)
error:the last warning/error message from the server (string)

4   The DB wrapper class

The pgobject methods are wrapped in the class DB. The preferred way to use this module is as follows:

import pg

db = pg.DB(...) # see below

for r in db.query( # just for example
    """SELECT foo,bar
       FROM foo_bar_table
       WHERE foo !~ bar"""
    ).dictresult():

    print '%(foo)s %(bar)s' % r

This class can be subclassed as in this example:

import pg

class DB_ride(pg.DB):
  """This class encapsulates the database functions and the specific
     methods for the ride database."""

  def __init__(self):
      """Opens a database connection to the rides database"""

      pg.DB.__init__(self, dbname = 'ride')
      self.query("""SET DATESTYLE TO 'ISO'""")

  [Add or override methods here]

The following describes the methods and variables of this class.

4.1   Initialization

The DB class is initialized with the same arguments as the connect function 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.

You can also initialize the DB class with an existing _pg or pgdb connection. Pass this connection as a single unnamed parameter, or as a single parameter named db. This allows you to use all of the methods of the DB class with a DB-API 2 compliant connection. Note that the close() and reopen() methods are inoperative in this case.

4.2   pkey - return the primary key of a table

Syntax:

pkey(table)
Parameters:
table:name of table
Return type:
string:Name of the field which is the primary key of the table
Description:
This method returns the primary key of a table. For composite primary keys, the return value will be a frozenset. Note that this raises an exception if the table does not have a primary key.

4.3   get_databases - get list of databases in the system

Syntax:

get_databases()
Parameters:
None
Return type:
list:all databases in the system
Description:
Although you can do this with a simple select, it is added here for convenience.

4.4   get_relations - get list of relations in connected database

Syntax:

get_relations(kinds)
Parameters:
kinds:a string or sequence of type letters
Description:
The type letters are r = ordinary table, i = index, S = sequence, v = view, c = composite type, s = special, t = TOAST table. If kinds is None or an empty string, all relations are returned (this is also the default). Although you can do this with a simple select, it is added here for convenience.

4.5   get_tables - get list of tables in connected database

Syntax:

get_tables()
Parameters:
None
Returns:
list:all tables in connected database
Description:
Although you can do this with a simple select, it is added here for convenience.

4.6   get_attnames - get the attribute names of a table

Syntax:

get_attnames(table)
Parameters:
table:name of table
Returns:
dictionary:The keys are the attribute names, the values are the type names of the attributes.
Description:
Given the name of a table, digs out the set of attribute names.

4.7   has_table_privilege - check whether current user has specified table privilege

Syntax:

has_table_privilege(table, privilege)
Parameters:
table:name of table
privilege:privilege to be checked - default is 'select'
Description:
Returns True if the current user has the specified privilege for the table.

4.8   get - get a row from a database table or view

Syntax:

get(table, arg, [keyname])
Parameters:
table:name of table or view
arg:either a dictionary or the value to be looked up
keyname:name of field to use as key (optional)
Return type:
dictionary:The keys are the attribute names, the values are the 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. For a composite key, keyname can also be a sequence of key names. The OID is also put into the dictionary if the table has one, but in order to allow the caller to work with multiple tables, it is munged as oid(schema.table).

4.9   insert - insert a row into a database table

Syntax:

insert(table, [d,] [return_changes,] [key = val, ...])
Parameters:
table:name of table
d:optional dictionary of values
return_changes:Return values in new row - default True
Return type:
dictionary:The dictionary of values inserted
Description:

This method inserts a row into a table. If the optional dictionary is not supplied then the required values must be included as keyword/value pairs. If a dictionary is supplied then any keywords provided will be added to or replace the entry in the dictionary.

The dictionary is then, if possible, reloaded with the values actually inserted in order to pick up values modified by rules, triggers, etc.

Due to the way that this function works in PostgreSQL versions below 8.2, you may find inserts taking longer and longer as your table gets bigger. If this happens and it is a table with OID but no primary key you can overcome this problem by simply adding an index onto the OID of any table that you think may get large over time. You may also consider using the inserttable() method described in section 3.

Note: With PostgreSQL versions before 8.2 the table being inserted to must have a primary key or an OID to use this method properly. If not then the dictionary will not be filled in as described. Also, if this method is called within a transaction, the transaction will abort.

Note: The method currently doesn't support insert into views although PostgreSQL does.

4.10   update - update a row in a database table

Syntax:

update(table, [d,] [key = val, ...])
Parameters:
table:name of table
d:optional dictionary of values
Return type:
dictionary:the new row
Description:

Similar to insert but updates an existing row. The update is based on the OID value as munged by get or passed as keyword, or on the primary key of the table. The dictionary is modified, if possible, to reflect any changes caused by the update due to triggers, rules, default values, etc.

Like insert, the dictionary is optional and updates will be performed on the fields in the keywords. There must be an OID or primary key either in the dictionary where the OID must be munged, or in the keywords where it can be simply the string "oid".

4.11   clear - clears row values in memory

Syntax:

clear(table, [a])
Parameters:
table:name of table
a:optional dictionary of values
Return type:
dictionary:an empty row
Description:

This method clears all the attributes to values determined by the types. Numeric types are set to 0, Booleans are set to 'f', dates are set to 'now()' 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.

If the dictionary is not supplied a new one is created.

4.12   delete - delete a row from a database table

Syntax:

delete(table, [d,] [key = val, ...])
Parameters:
table:name of table
d:optional dictionary of values
Returns:
None
Description:
This method deletes the row from a table. It deletes based on the OID value as munged by get or passed as keyword, or on the primary key of the table. The return value is the number of deleted rows (i.e. 0 if the row did not exist and 1 if the row was deleted).

4.13   escape_string - escape a string for use within SQL

Syntax:

escape_string(string)
Parameters:
string:the string that is to be escaped
Return type:
str:the escaped string
Description:
Similar to the module function with the same name, but the behavior of this method is adjusted depending on the connection properties (such as character encoding).

4.14   escape_bytea - escape binary data for use within SQL as type bytea

Syntax:

escape_bytea(datastring)
Parameters:
datastring:string containing the binary data that is to be escaped
Return type:
str:the escaped string
Description:
Similar to the module function with the same name, but the behavior of this method is adjusted depending on the connection properties (in particular, whether standard-conforming strings are enabled).

4.15   unescape_bytea -- unescape bytea data that has been retrieved as text

Syntax:

unescape_bytea(string)
Parameters:
datastring:the bytea data string that has been retrieved as text
Return type:
str:string containing the binary data
Description:
See the module function with the same name.

5   pgqueryobject methods

5.1   getresult - get query values as list of tuples

Syntax:

getresult()
Parameters:
None
Return type:
list:result values as a list of tuples
Exceptions raised:
TypeError:too many parameters
pg.InternalError:
 invalid previous result
Description:
This method returns the list of the values returned by the query. More information about this result may be accessed using listfields(), fieldname() and fieldnum() methods.

5.2   dictresult - get query values as list of dictionaries

Syntax:

dictresult()
Parameters:
None
Return type:
list:result values as a list of dictionaries
Exceptions raised:
TypeError:too many parameters
pg.InternalError:
 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.

5.3   listfields - lists fields names of previous query result

Syntax:

listfields()
Parameters:
None
Return type:
list:field names
Exceptions raised:
TypeError:too many parameters
pg.InternalError:
 invalid previous result, or lost 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.

5.4   fieldname, fieldnum - field name/number conversion

Syntax:

fieldname(i)
Parameters:
i:field number (integer)
Return type:
string:field name
Exceptions raised:
TypeError:invalid connection, bad parameter type, or too many parameters
ValueError:invalid field number
pg.InternalError:
 invalid previous result, or lost 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 as the result values.

Syntax:

fieldnum(name)
Parameters:
name:field name (string)
Return type:
integer:field number
Exceptions raised:
TypeError:invalid connection, bad parameter type, or too many parameters
ValueError:unknown field name
pg.InternalError:
 invalid previous result, or lost 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.

5.5   ntuples - return number of tuples in query object

Syntax:

ntuples()
Parameters:
None
Return type:
integer:number of tuples in pgqueryobject
Exceptions raised:
TypeError:Too many arguments.
Description:
This method returns the number of tuples found in a query.

6   Large objects: pglarge

This object handles all the request concerning a PostgreSQL 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 pglarge 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.

See also the PostgreSQL programmer's guide for more information about the large object interface.

6.1   open - opens a large object

Syntax:

open(mode)
Parameters:
mode:open mode definition (integer)
Return type:
None
Exceptions raised:
TypeError:invalid connection, 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).

6.2   close - closes a large object

Syntax:

close()
Parameters:
None
Return type:
None
Exceptions raised:
TypeError:invalid connection
TypeError: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.

6.3   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:
TypeError:invalid connection, invalid object, bad parameter type, or too many parameters
ValueError:if size is negative
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:
TypeError:invalid connection, 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
Exceptions raised:
TypeError:binvalid connection or invalid object, 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
Exceptions raised:
TypeError:invalid connection or invalid object
TypeError: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
Exceptions raised:
TypeError:invalid connection or invalid object
TypeError:too many parameters
IOError:object is not closed, or unlink error
Description:
This methods unlinks (deletes) the PostgreSQL large object.

6.4   size - gives the large object size

Syntax:

size()
Parameters:
None
Return type:
integer:the large object size
Exceptions raised:
TypeError:invalid connection or invalid object
TypeError: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. It was implemented because this function is very useful for a web interfaced database. Currently, the large object needs to be opened first.

6.5   export - saves a large object to a file

Syntax:

export(name)
Parameters:
name:file to be created
Return type:
None
Exceptions raised:
TypeError:invalid connection or invalid object, 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.

6.6   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

Caution!

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.

PyGreSQL-4.0/docs/install.html010064400017500000000000000266671112713757600152560ustar darcywheel PyGreSQL Installation

PyGreSQL Installation

1   General

You must first have installed Python and PostgreSQL on your system. If you want to access remote database only, you don't need to install the full PostgreSQL server, but only the C interface (libpq). If you are on Windows, make sure that the directory with libpq.dll is in your PATH environment variable.

The current version of PyGreSQL has been tested with Python 2.5 and PostGreSQL 8.3. Older version should work as well, but you will need at least Python 2.3 and PostgreSQL 7.4.

PyGreSQL will be installed as three modules, a dynamic module called _pg.pyd, and two pure Python wrapper modules called pg.py and pgdb.py. All three files will be installed directly into the Python site-packages directory. To uninstall PyGreSQL, simply remove these three files again.

2   Installing from a Binary Distribution

This is the easiest way to install PyGreSQL.

You can currently download PyGreSQL as Linux RPM, NetBSD package and Windows installer. Make sure the required Python version of the binary package matches the Python version you have installed.

Install the package as usual on your system.

Note that the documentation is currently only included in the source package.

3   Installing from Source

If you want to install PyGreSQL from Source, or there is no binary package available for your platform, follow these instructions.

Make sure the Python header files and PostgreSQL client and server header files are installed. These come usually with the "devel" packages on Unix systems and the installer executables on Windows systems.

If you are using a precompiled PostgreSQL, you will also need the pg_config tool. This is usually also part of the "devel" package on Unix, and will be installed as part of the database server feature on Windows systems.

3.1   Building and installing with Distutils

You can build and install PyGreSQL using Distutils.

Download and unpack the PyGreSQL source tarball if you haven't already done so.

Type the following commands to build and install PyGreSQL:

python setup.py build
python setup.py install

If you are using MinGW to build PyGreSQL under Microsoft Windows, please note that Python newer version 2.3 is using msvcr71 instead of msvcrt as its common runtime library. You can allow for that by editing the file %MinGWpath%/lib/gcc/%MinGWversion%/specs and changing the entry that reads -lmsvcrt to -lmsvcr71. You may also need to copy libpq.lib to libpq.a in the PostgreSQL lib directory. Then use the following command to build and install PyGreSQL:

python setup.py build -c mingw32 install

Now you should be ready to use PyGreSQL.

3.2   Compiling Manually

The source file for compiling the dynamic module is called pgmodule.c. You have two options. You can compile PyGreSQL as a stand-alone module or you can build it into the Python interpreter.

3.3   Stand-Alone

  • In the directory containing pgmodule.c, run the following command:

    cc -fpic -shared -o _pg.so -I$PYINC -I$PGINC -I$PSINC -L$PGLIB -lpq pgmodule.c
    

    where you have to set:

    PYINC = path to the Python include files
            (usually something like /usr/include/python)
    PGINC = path to the PostgreSQL client include files
            (something like /usr/include/pgsql or /usr/include/postgresql)
    PSINC = path to the PostgreSQL server include files
            (like /usr/include/pgsql/server or /usr/include/postgresql/server)
    PGLIB = path to the PostgreSQL object code libraries (usually /usr/lib)
    

    If you are not sure about the above paths, try something like:

    PYINC=`find /usr -name Python.h`
    PGINC=`find /usr -name libpq-fe.h`
    PSINC=`find /usr -name postgres.h`
    PGLIB=`find /usr -name libpq.so`
    

    If you have the pg_config tool installed, you can set:

    PGINC=`pg_config --includedir`
    PSINC=`pg_config --includedir-server`
    PGLIB=`pg_config --libdir`
    

    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.

    On some systems you may need to include -lcrypt in the list of libraries to make it compile.

  • Test the new module. Something like the following should work:

    $ python
    
    >>> 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)
    
  • Finally, move the _pg.so, pg.py, and pgdb.py to a directory in your PYTHONPATH. A good place would be /usr/lib/python/site-packages if your Python modules are in /usr/lib/python.

3.4   Built-in to Python interpreter

  • Find the directory where your Setup file lives (usually in the Modules subdirectory) in the Python source hierarchy and copy or symlink the pgmodule.c file there.

  • Add the following line to your 'Setup' file:

    _pg  pgmodule.c -I$PGINC -I$PSINC -L$PGLIB -lpq
    

    where:

    PGINC = path to the PostgreSQL client include files (see above)
    PSINC = path to the PostgreSQL server include files (see above)
    PGLIB = path to the PostgreSQL object code libraries (see above)
    

    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 (see above)
    

    On some systems you may need to include -lcrypt in the list of libraries to make it compile.

  • 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.

  • Rebuild Python from the root directory of the Python source hierarchy by running make -f Makefile.pre.in boot and make && make install.

  • For more details read the documentation at the top of Makefile.pre.in.

PyGreSQL-4.0/docs/index.html010064400017500000000000000130001112713757600146700ustar darcywheel PyGreSQL - PostgreSQL module for Python
PyGreSQL
Version 4.0
:: PostgreSQL module for Python ::

PyGreSQL – PostgreSQL module for Python

PyGreSQL is an open-source 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.

This software is copyright © 1995, Pascal Andre.
Further modifications are copyright © 1997-2006 by D'Arcy J.M. Cain.

See the copyright notice for detailed information.

Documentation

The following information is also available in the docs folder of the distribution:

CVS Access

The CVS repository is available through CVSWeb.

Mailing list

You can join the mailing list to discuss future development of the PyGreSQL interface. This is usually a low volume list except when there are new features being added.

Examples

I am starting to collect examples of applications that use PyGreSQL. So far I only have a few but if you have an example for me, you can either send me the files or the URL for me to point to.

Here is a List of motorcycle rides in Ontario that uses a PostgreSQL database to store the rides. There is a link at the bottom of the page to view the source code.

Oleg Broytmann has written a simple example RGB database demo.

PyGreSQL-4.0/docs/future.html010064400017500000000000000061461112713757600151100ustar darcywheel PyGreSQL future directions

PyGreSQL future directions

To Do

  • Documentation for the pgdb module (everything specific to PyGreSQL).
  • The large object and direct access functions need much more attention.
  • The C module needs to be cleaned up and redundant code merged, and should get its own unit test module.
  • The fetch method should use real cursors.
  • What shall we do with the "tutorial" directory (it's rather a tutorial for Postgres/SQL than for PyGreSQL, it's using only the query method from the classic pg module and no other PyGreSQL functionality, it's rather a demo than a tutorial)?

Proposed Patches

  • Notice handling with PQsetNoticeReceiver and PQsetNoticeProcessor (one possible implementation was already suggested by Dmitry Dvoinikov http://mailman.vex.net/pipermail/pygresql/2005-November/001530.html). Maybe also make notifications accessible via the optional cursor and connection attribute "messages" proposed in the DB-API specs.

Wish List

  • Make SQLSTATE error codes available.
  • Make use of PQexecParams() and PQprepare(). This could speed up executemany() and allow retrieving binary data directly by setting the resultFormat parameter to one.
  • Support optional "errorhandler" extension.
  • Support optional cursor and connection attribute "messages".
  • Connection as context manager (see http://tinyurl.com/6j9cef).
  • Users should be able to register their own types with _pg.
  • Let pg and pgdb support namedtuples (as available in Py 2.6). pg could get a new method namedresult(), and pgdb could provide a row factory for namedtuples (similar to sqlite3).
  • New methods in the classic module, similar to getresult() and dictresult(), but returning dictionaries of rows instead of lists of rows (with primary key or oids as keys).
  • Make PyGreSQL thread-safe on the connection level.
  • The API documentation could be created with Epydoc.
  • Write a tutorial for beginners and advanced use.
  • More and better documented examples.
PyGreSQL-4.0/docs/changelog.html010064400017500000000000000422131112713757600155200ustar darcywheel PyGreSQL ChangeLog

PyGreSQL ChangeLog

Version 4.0 (2009-01-01)

  • Dropped support for Python below 2.3 and PostgreSQL below 7.4.
  • Improved performance of fetchall() for large result sets by speeding up the type casts (as suggested by Peter Schuller).
  • Exposed exceptions as attributes of the connection object.
  • Exposed connection as attribute of the cursor object.
  • Cursors now support the iteration protocol.
  • Added new method to get parameter settings.
  • Added customizable row_factory as suggested by Simon Pamies.
  • Separated between mandatory and additional type objects.
  • Added keyword args to insert, update and delete methods.
  • Added exception handling for direct copy.
  • Release the GIL while making a connection (as suggested by Peter Schuller).
  • If available, use decimal.Decimal for numeric types.
  • Allow DB wrapper to be used with DB-API 2 connections (as suggested by Chris Hilton).
  • Made private attributes of DB wrapper accessible.
  • Dropped dependence on mx.DateTime module.
  • Support for PQescapeStringConn() and PQescapeByteaConn(); these are now also used by the internal _quote() functions.
  • Added 'int8' to INTEGER types. New SMALLINT type.
  • Added a way to find the number of rows affected by a query() with the classic pg module by returning it as a string. For single inserts, query() still returns the oid as an integer. The pgdb module already provides the "rowcount" cursor attribute for the same purpose.
  • Improved getnotify() by calling PQconsumeInput() instead of submitting an empty command.
  • Removed compatibility code for old OID munging style.
  • The insert() and update() methods now use the "returning" clause if possible to get all changed values, and they also check in advance whether a subsequent select is possible, so that ongoing transactions won't break if there is no select privilege.
  • Added "protocol_version" and "server_version" attributes.
  • Revived the "user" attribute.
  • The pg module now works correctly with composite primary keys; these are represented as frozensets.
  • Removed the undocumented and actually unnecessary "view" parameter from the get() method.
  • get() raises a nicer ProgrammingError instead of a KeyError if no primary key was found.
  • delete() now also works based on the primary key if no oid available and returns whether the row existed or not.

Version 3.8.1 (2006-06-05)

  • Use string methods instead of deprecated string functions.
  • Only use SQL-standard way of escaping quotes.
  • Added the functions escape_string() and escape/unescape_bytea() (as suggested by Charlie Dyson and Kavous Bojnourdi a long time ago).
  • Reverted code in clear() method that set date to current.
  • Added code for backwards compatibility in OID munging code.
  • Reorder attnames tests so that "interval" is checked for before "int."
  • If caller supplies key dictionary, make sure that all has a namespace.

Version 3.8 (2006-02-17)

  • Installed new favicon.ico from Matthew Sporleder <mspo@mspo.com>
  • Replaced snprintf by PyOS_snprintf.
  • Removed NO_SNPRINTF switch which is not needed any longer
  • Clean up some variable names and namespace
  • Add get_relations() method to get any type of relation
  • Rewrite get_tables() to use get_relations()
  • Use new method in get_attnames method to get attributes of views as well
  • Add Binary type
  • Number of rows is now -1 after executing no-result statements
  • Fix some number handling
  • Non-simple types do not raise an error any more
  • Improvements to documentation framework
  • Take into account that nowadays not every table must have an oid column
  • Simplification and improvement of the inserttable() function
  • Fix up unit tests
  • The usual assortment of minor fixes and enhancements

Version 3.7 (2005-09-07)

Improvement of pgdb module:

  • Use Python standard datetime if mxDateTime is not available

Major improvements and clean-up in classic pg module:

  • All members of the underlying connection directly available in DB
  • Fixes to quoting function
  • Add checks for valid database connection to methods
  • Improved namespace support, handle search_path correctly
  • Removed old dust and unnessesary imports, added docstrings
  • Internal sql statements as one-liners, smoothed out ugly code

Version 3.6.2 (2005-02-23)

  • Further fixes to namespace handling

Version 3.6.1 (2005-01-11)

  • Fixes to namespace handling

Version 3.6 (2004-12-17)

  • Better DB-API 2.0 compliance
  • Exception hierarchy moved into C module and made available to both APIs
  • Fix error in update method that caused false exceptions
  • Moved to standard exception hierarchy in classic API
  • Added new method to get transaction state
  • Use proper Python constants where appropriate
  • Use Python versions of strtol, etc. Allows Win32 build.
  • Bug fixes and cleanups

Version 3.5 (2004-08-29)

Fixes and enhancements:

  • Add interval to list of data types
  • fix up method wrapping especially close()
  • retry pkeys once if table missing in case it was just added
  • wrap query method separately to handle debug better
  • use isinstance instead of type
  • fix free/PQfreemem issue - finally
  • miscellaneous cleanups and formatting

Version 3.4 (2004-06-02)

Some cleanups and fixes. This is the first version where PyGreSQL is moved back out of the PostgreSQL tree. A lot of the changes mentioned below were actually made while in the PostgreSQL tree since their last release.

  • Allow for larger integer returns
  • Return proper strings for true and false
  • Cleanup convenience method creation
  • Enhance debugging method
  • Add reopen method
  • Allow programs to preload field names for speedup
  • Move OID handling so that it returns long instead of int
  • Miscellaneous cleanups and formatting

Version 3.3 (2001-12-03)

A few cleanups. Mostly there was some confusion about the latest version and so I am bumping the number to keep it straight.

  • Added NUMERICOID to list of returned types. This fixes a bug when returning aggregates in the latest version of PostgreSQL.

Version 3.2 (2001-06-20)

Note that there are very few changes to PyGreSQL between 3.1 and 3.2. The main reason for the release is the move into the PostgreSQL development tree. Even the WIN32 changes are pretty minor.

Version 3.1 (2000-11-06)

  • Fix some quoting functions. In particular handle NULLs better.
  • Use a method to add primary key information rather than direct manipulation of the class structures
  • Break decimal out in _quote (in pg.py) and treat it as float
  • Treat timestamp like date for quoting purposes
  • Remove a redundant SELECT from the get method speeding it, and insert (since it calls get) up a little.
  • Add test for BOOL type in typecast method to pgdbTypeCache class (tv@beamnet.de)
  • Fix pgdb.py to send port as integer to lower level function (dildog@l0pht.com)
  • Change pg.py to speed up some operations
  • Allow updates on tables with no primary keys

Version 3.0 (2000-05-30)

Version 2.4 (1999-06-15)

  • Insert returns None if the user doesn't have select permissions on the table. It can (and does) happen that one has insert but not select permissions on a table.
  • Added ntuples() method to query object (brit@druid.net)
  • Corrected a bug related to getresult() and the money type
  • Corrected a bug related to negative money amounts
  • Allow update based on primary key if munged oid not available and table has a primary key
  • Add many __doc__ strings (andre@via.ecp.fr)
  • Get method works with views if key specified

Version 2.3 (1999-04-17)

  • 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 (1998-12-21)

  • Added user and password support thanks to Ng Pheng Siong (ngps@post1.com)
  • Insert queries return the inserted oid
  • Add new pg wrapper (C module 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 (1998-03-07)

  • return fields as proper Python objects for field type
  • Cleaned up pgext.py
  • Added dictresult method

Version 2.0 (1997-12-23)

  • Updated code for PostgreSQL 6.2.1 and Python 1.5
  • Reformatted code and converted to use full ANSI style prototypes
  • 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
  • Various code changes - mostly stylistic

Version 1.0b (1995-11-04)

  • 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 (1995-10-30)

A 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 (1995-10-10)

The first public release.

  • Large objects implementation
  • Many bug fixes, enhancements, ...

Version 0.1a (1995-10-07)

  • Basic libpq functions (SQL access)
PyGreSQL-4.0/docs/announce.html010064400017500000000000000027021112713757600153760ustar darcywheel PyGreSQL Announcements

PyGreSQL Announcements

Release of PyGreSQL version 4.0

PyGreSQL v4.0 has been released.

It is available at: ftp://ftp.PyGreSQL.org/pub/distrib/PyGreSQL-4.0.tgz.

If you are running NetBSD, look in the packages directory under databases. There is also a package in the FreeBSD ports collection.

Please refer to changelog.txt for things that have changed in this version.

Please refer to readme.txt for general information.

D'Arcy J.M. Cain
PyGreSQL-4.0/docs/readme.txt010064400017500000000000000161221112713757600147010ustar darcywheel========================================== PyGreSQL - Python interface for PostgreSQL ========================================== -------------------- PyGreSQL version 4.0 -------------------- .. meta:: :description: PyGreSQL - Python interface for PostgreSQL :keywords: PyGreSQL, PostGreSQL, Python .. contents:: Contents Copyright notice ================ 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 Further modifications copyright (c) 1997-2008 by D'Arcy J.M. Cain (darcy@druid.net) 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. Introduction ============ **PostgreSQL** is a highly scalable, SQL compliant, open source object-relational database management system. With more than 15 years of development history, it is quickly becoming the de facto database for enterprise level open source solutions. Best of all, PostgreSQL's source code is available under the most liberal open source license: the BSD license. **Python** Python is an interpreted, interactive, object-oriented programming language. It is often compared to Tcl, Perl, Scheme or Java. Python combines remarkable power with very clear syntax. It has modules, classes, exceptions, very high level dynamic data types, and dynamic typing. There are interfaces to many system calls and libraries, as well as to various windowing systems (X11, Motif, Tk, Mac, MFC). New built-in modules are easily written in C or C++. Python is also usable as an extension language for applications that need a programmable interface. The Python implementation is copyrighted but freely usable and distributable, even for commercial use. **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 is developed and tested on a NetBSD system, but it should also run on most other platforms where PostgreSQL and Python is running. It is based on the PyGres95 code written by Pascal Andre (andre@chimay.via.ecp.fr). D'Arcy (darcy@druid.net) renamed it to PyGreSQL starting with version 2.0 and serves as the "BDFL" of PyGreSQL. The current version PyGreSQL 4.0 needs PostgreSQL 7.2 and Python 2.3 or above. Where to get ... ? ================== Home sites of the different packages ------------------------------------ **Python**: http://www.python.org **PostgreSQL**: http://www.postgresql.org **PyGreSQL**: http://www.pygresql.org Download PyGreSQL here ---------------------- The **released version of the source code** is available at * ftp://ftp.pygresql.org/pub/distrib/PyGreSQL.tgz You can also check the latest **pre-release version** at * ftp://ftp.pygresql.org/pub/distrib/PyGreSQL-beta.tgz A **Linux RPM** can be picked up from * ftp://ftp.pygresql.org/pub/distrib/pygresql.i386.rpm A **NetBSD package** is available in their pkgsrc collection * ftp://ftp.netbsd.org/pub/NetBSD/packages/pkgsrc/databases/py-postgresql/README.html A **FreeBSD package** is available in their ports collection * http://www.freebsd.org/cgi/cvsweb.cgi/ports/databases/py-PyGreSQL/ A **Win32 package** for various Python versions is available at * ftp://ftp.pygresql.org/pub/distrib/PyGreSQL-4.0.win32-py2.3.exe * ftp://ftp.pygresql.org/pub/distrib/PyGreSQL-4.0.win32-py2.4.exe * ftp://ftp.pygresql.org/pub/distrib/PyGreSQL-4.0.win32-py2.5.exe * ftp://ftp.pygresql.org/pub/distrib/PyGreSQL-4.0.win32-py2.6.exe You can also find PyGreSQL on the **Python Package Index** at * http://pypi.python.org/pypi/PyGreSQL/ Distribution files ================== ========== = pgmodule.c the C Python module (_pg) pg.py the "classic" PyGreSQL module pgdb.py DB-SIG DB-API 2.0 compliant API wrapper for PygreSQL docs/ documentation directory Contains: readme.txt, announce.txt, install.txt, changelog.txt, future.txt, pg.txt and pgdb.txt. All text files are in ReST format, so HTML versions can be easily created with buildhtml.py from docutils. tutorial/ demos directory Contains: basics.py, syscat.py, advanced.py and func.py. The samples here have been taken from the PostgreSQL manual and were used for module testing. They demonstrate some PostgreSQL features. ========== = Installation ============ You will find the installing instructions in `install.txt `_. Information and support ======================= For general information ----------------------- **Python**: http://www.python.org **PostgreSQL**: http://www.postgresql.org **PyGreSQL**: http://www.pygresql.org For support ----------- **Python**: see http://www.python.org/community/ **PostgreSQL**: see http://www.postgresql.org/support/ **PyGreSQL**: Contact the PyGreSQL mailing list concerning PyGreSQL 2.0 and up. If you would like to proposes changes, please join the PyGreSQL mailing list and send context diffs there. See http://mailman.vex.net/mailman/listinfo/pygresql to join the mailing list. Please note that messages to individual developers will generally not be answered directly. All questions, comments and code changes must be submitted to the mailing list for peer review and archiving. PyGreSQL programming information -------------------------------- You may either choose to use the "classic" PyGreSQL interface provided by the `pg` module or else the newer DB-API 2.0 compliant interface provided by the `pgdb` module. `DB-API 2.0 `_ (Python Database API Specification v2.0) is a specification for connecting to databases (not only PostGreSQL) from Python that has been developed by the Python DB-SIG in 1999. The programming information is available in the files `pg.txt `_ and `pgdb.txt `_. Note that PyGreSQL is not thread-safe on the connection level. Therefore we recommend using `DBUtils ` for multi-threaded environments, which supports both PyGreSQL interfaces. ChangeLog and Future ==================== The ChangeLog with past changes is in the file `changelog.txt `_. A to do list and wish list is in the file `future.txt `_. PyGreSQL-4.0/docs/pgdb.txt010064400017500000000000000023541112713757600143620ustar darcywheel================================ PyGreSQL Programming Information ================================ -------------------------------------------- The DB-API compliant interface (pgdb module) -------------------------------------------- .. meta:: :description: The DB-API compliant interface (pgdb module) :keywords: PyGreSQL, pgdb, DB-API, PostGreSQL, Python .. sectnum:: .. contents:: Contents Introduction ============ You may either choose to use the `"classic" PyGreSQL interface `_ provided by the `pg` module or else the `DB-API 2.0 compliant interface `_ provided by the `pgdb` module. `DB-API 2.0 `_ (Python Database API Specification v2.0) is a specification for connecting to databases (not only PostGreSQL) from Python that has been developed by the Python DB-SIG in 1999. The following documentation covers only the newer `pgdb` API. The authoritative programming information for the DB-API is availabe at http://www.python.org/dev/peps/pep-0249/ A tutorial-like introduction to the DB-API can be found at http://www2.linuxjournal.com/lj-issues/issue49/2605.html The pgdb module =============== .. note:: This section of the documentation still needs to be written. PyGreSQL-4.0/docs/pg.txt010064400017500000000000001135501112713757600140550ustar darcywheel================================ PyGreSQL Programming Information ================================ ------------------------------------------ The classic PyGreSQL interface (pg module) ------------------------------------------ .. meta:: :description: The classic PyGreSQL interface (pg module) :keywords: PyGreSQL, pg, PostGreSQL, Python .. sectnum:: .. contents:: Contents Introduction ============ You may either choose to use the `"classic" PyGreSQL interface `_ provided by the `pg` module or else the `DB-API 2.0 compliant interface `_ provided by the `pgdb` module. The following documentation covers only the older `pg` API. The `pg` module handles three types of objects, - the `pgobject`, which handles the connection and all the requests to the database, - the `pglarge` object, which handles all the accesses to PostgreSQL large objects, - the `pgqueryobject` that handles query results and it provides a convenient wrapper class `DB` for the `pgobject`. If you want to see a simple example of the use of some of these functions, see http://ontario.bikerides.ca where you can find a link at the bottom to the actual Python code for the page. Module functions and constants ============================== The `pg` module defines a few functions that allow to connect to a database and 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 the tag [DV]. All variables are set to `None` at module initialization, specifying that standard environment variables should be used. 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: If successful, the `pgobject` handling the connection Exceptions raised: :TypeError: bad argument type, or too many arguments :SyntaxError: duplicate argument definition :pg.InternalError: some error occurred during pg connection definition (plus all exceptions relative to object allocation) Description: This function 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. Examples:: import pg con1 = pg.connect('testdb', 'myhost', 5432, None, None, 'bob', None) con2 = pg.connect(dbname='testdb', host='localhost', user='bob') get_defhost, set_defhost - default server host [DV] --------------------------------------------------- Syntax:: get_defhost() Parameters: None Return type: :string, None: default host specification Exceptions raised: :TypeError: 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. get_defport, set_defport - default server port [DV] --------------------------------------------------- Syntax:: get_defport() Parameters: None Return type: :integer, None: default port specification Exceptions raised: :TypeError: 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. get_defopt, set_defopt - default connection options [DV] -------------------------------------------------------- Syntax:: get_defopt() Parameters: None Return type: :string, None: default options specification Exceptions raised: :TypeError: 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. get_deftty, set_deftty - default debug tty [DV] ----------------------------------------------- Syntax:: get_deftty() Parameters: None Return type: :string, None: default debug terminal specification Exceptions raised: :TypeError: 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. get_defbase, set_defbase - default database name [DV] ----------------------------------------------------- Syntax:: get_defbase() Parameters: None Return type: :string, None: default database name specification Exceptions raised: :TypeError: 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. escape_string - escape a string for use within SQL -------------------------------------------------- Syntax:: escape_string(string) Parameters: :string: the string that is to be escaped Return type: :str: the escaped string Exceptions raised: :TypeError: bad argument type, or too many arguments Description: This function escapes a string for use within an SQL command. This is useful when inserting data values as literal constants in SQL commands. Certain characters (such as quotes and backslashes) must be escaped to prevent them from being interpreted specially by the SQL parser. `escape_string` performs this operation. Note that there is also a `pgobject` method with the same name which takes connection properties into account. .. caution:: It is especially important to do proper escaping when handling strings that were received from an untrustworthy source. Otherwise there is a security risk: you are vulnerable to "SQL injection" attacks wherein unwanted SQL commands are fed to your database. Example:: name = raw_input("Name? ") phone = con.query("select phone from employees" " where name='%s'" % escape_string(name)).getresult() escape_bytea - escape binary data for use within SQL as type `bytea` -------------------------------------------------------------------- Syntax:: escape_bytea(datastring) Parameters: :datastring: string containing the binary data that is to be escaped Return type: :str: the escaped string Exceptions raised: :TypeError: bad argument type, or too many arguments Description: Escapes binary data for use within an SQL command with the type `bytea`. As with `escape_string`, this is only used when inserting data directly into an SQL command string. Note that there is also a `pgobject` method with the same name which takes connection properties into account. Example:: picture = file('garfield.gif', 'rb').read() con.query("update pictures set img='%s' where name='Garfield'" % escape_bytea(picture)) unescape_bytea -- unescape `bytea` data that has been retrieved as text ----------------------------------------------------------------------- Syntax:: unescape_bytea(string) Parameters: :datastring: the `bytea` data string that has been retrieved as text Return type: :str: string containing the binary data Exceptions raised: :TypeError: bad argument type, or too many arguments Description: Converts an escaped string representation of binary data into binary data - the reverse of `escape_bytea`. This is needed when retrieving `bytea` data with the `getresult()` or `dictresult()` method. Example:: picture = unescape_bytea(con.query( "select img from pictures where name='Garfield'").getresult[0][0]) file('garfield.gif', 'wb').write(picture) set_decimal -- set a decimal type to be used for numeric values --------------------------------------------------------------- Syntax:: set_decimal(cls) Parameters: :cls: the Python class to be used for PostgreSQL numeric values Description: This function can be used to specify the Python class that shall be used by PyGreSQL to hold PostgreSQL numeric values. The default class is decimal.Decimal if available, otherwise the float type is used. 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 the libpq description in the PostgreSQL user manual for more information about them. These constants are: :version, __version__: constants that give the current version. :INV_READ, INV_WRITE: large objects access modes, used by `(pgobject.)locreate` and `(pglarge.)open` :SEEK_SET, SEEK_CUR, SEEK_END: positional flags, used by `(pglarge.)seek` Connection objects: pgobject ============================ This object handles 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. .. caution:: Some methods give direct access to the connection socket. *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. **These methods are specified by the tag [DA].** .. note:: 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].** 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 or lost connection :pg.ProgrammingError: error in query :pg.InternalError': error during query processing Description: This method simply sends a SQL query to the database. If the query is an insert statement that inserted exactly one row into a table that has OIDs, the return value is the OID of the newly inserted row. If the query is an update or delete statement, or an insert statement that did not insert exactly one row in a table with OIDs, then the numer of rows affected is returned as a string. If it is a statement that returns rows as a result (usually a select statement, but maybe also an "insert/update ... returning" statement), this method returns a `pgqueryobject` that can be accessed via the `getresult()` or `dictresult()` method or simply printed. Otherwise, it returns `None`. 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 connection. cancel - abandon processing of current SQL command -------------------------------------------------- Syntax:: cancel() Parameters: None Return type: None Exceptions raised: :TypeError: too many (any) arguments Description: This method requests that the server abandon processing of the current SQL command. 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. 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. getnotify - gets the last notify from the server ------------------------------------------------ Syntax:: getnotify() Parameters: None Return type: :tuple, None: last notify from server Exceptions raised: :TypeError: too many parameters :TypeError: 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`. 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 Exceptions raised: :TypeError: invalid connection, bad argument type, or too many arguments :MemoryError: insert buffer could not be allocated :ValueError: unsupported values Description: This method allow to *quickly* insert large blocks of data in a table: It inserts the whole values list into the given table. Internally, it uses the COPY command of the PostgreSQL database. 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. .. caution:: *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. putline - writes a line to the server socket [DA] ------------------------------------------------- Syntax:: putline(line) Parameters: :line: line to be written (string) Return type: None Exceptions raised: :TypeError: invalid connection, bad parameter type, or too many parameters Description: This method allows to directly write a string to the server socket. getline - gets a line from server socket [DA] --------------------------------------------- Syntax:: getline() Parameters: None Return type: :string: the line read Exceptions raised: :TypeError: invalid connection :TypeError: too many parameters :MemoryError: buffer overflow Description: This method allows to directly read a string from the server socket. endcopy - synchronizes client and server [DA] --------------------------------------------- Syntax:: endcopy() Parameters: None Return type: None Exceptions raised: :TypeError: invalid connection :TypeError: 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. locreate - create a large object in the database [LO] ----------------------------------------------------- Syntax:: locreate(mode) Parameters: :mode: large object create mode Return type: :pglarge: object handling the PostGreSQL large object Exceptions raised: :TypeError: invalid connection, bad parameter type, or too many parameters :pg.OperationalError: creation error 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. getlo - build 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 PostGreSQL large object Exceptions raised: :TypeError: invalid connection, 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. loimport - import a file to a large object [LO] ----------------------------------------------- Syntax:: loimport(name) Parameters: :name: the name of the file to be imported (string) Return type: :pglarge: object handling the PostGreSQL large object Exceptions raised: :TypeError: invalid connection, bad argument type, or too many arguments :pg.OperationalError: error during file import 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. Object attributes ----------------- Every `pgobject` defines a set of read-only attributes that describe the connection and its status. These attributes are: :host: the host name 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: user name on the database system (string) :protocol_version: the frontend/backend protocol being used (integer) :server_version: the backend version (integer, e.g. 80305 for 8.3.5) :status: the status of the connection (integer: 1 - OK, 0 - bad) :error: the last warning/error message from the server (string) The DB wrapper class ==================== The `pgobject` methods are wrapped in the class `DB`. The preferred way to use this module is as follows:: import pg db = pg.DB(...) # see below for r in db.query( # just for example """SELECT foo,bar FROM foo_bar_table WHERE foo !~ bar""" ).dictresult(): print '%(foo)s %(bar)s' % r This class can be subclassed as in this example:: import pg class DB_ride(pg.DB): """This class encapsulates the database functions and the specific methods for the ride database.""" def __init__(self): """Opens a database connection to the rides database""" pg.DB.__init__(self, dbname = 'ride') self.query("""SET DATESTYLE TO 'ISO'""") [Add or override methods here] The following describes the methods and variables of this class. Initialization -------------- The DB class is initialized with the same arguments as the connect function 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. You can also initialize the DB class with an existing `_pg` or `pgdb` connection. Pass this connection as a single unnamed parameter, or as a single parameter named `db`. This allows you to use all of the methods of the DB class with a DB-API 2 compliant connection. Note that the `close()` and `reopen()` methods are inoperative in this case. pkey - return the primary key of a table ---------------------------------------- Syntax:: pkey(table) Parameters: :table: name of table Return type: :string: Name of the field which is the primary key of the table Description: This method returns the primary key of a table. For composite primary keys, the return value will be a frozenset. Note that this raises an exception if the table does not have a primary key. get_databases - get list of databases in the system --------------------------------------------------- Syntax:: get_databases() Parameters: None Return type: :list: all databases in the system Description: Although you can do this with a simple select, it is added here for convenience. get_relations - get list of relations in connected database ----------------------------------------------------------- Syntax:: get_relations(kinds) Parameters: :kinds: a string or sequence of type letters Description: The type letters are `r` = ordinary table, `i` = index, `S` = sequence, `v` = view, `c` = composite type, `s` = special, `t` = TOAST table. If `kinds` is None or an empty string, all relations are returned (this is also the default). Although you can do this with a simple select, it is added here for convenience. get_tables - get list of tables in connected database ----------------------------------------------------- Syntax:: get_tables() Parameters: None Returns: :list: all tables in connected database Description: Although you can do this with a simple select, it is added here for convenience. get_attnames - get the attribute names of a table ------------------------------------------------- Syntax:: get_attnames(table) Parameters: :table: name of table Returns: :dictionary: The keys are the attribute names, the values are the type names of the attributes. Description: Given the name of a table, digs out the set of attribute names. has_table_privilege - check whether current user has specified table privilege ------------------------------------------------------------------------------ Syntax:: has_table_privilege(table, privilege) Parameters: :table: name of table :privilege: privilege to be checked - default is 'select' Description: Returns True if the current user has the specified privilege for the table. get - get a row from a database table or view --------------------------------------------- Syntax:: get(table, arg, [keyname]) Parameters: :table: name of table or view :arg: either a dictionary or the value to be looked up :keyname: name of field to use as key (optional) Return type: :dictionary: The keys are the attribute names, the values are the 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. For a composite key, `keyname` can also be a sequence of key names. The OID is also put into the dictionary if the table has one, but in order to allow the caller to work with multiple tables, it is munged as `oid(schema.table)`. insert - insert a row into a database table ------------------------------------------- Syntax:: insert(table, [d,] [return_changes,] [key = val, ...]) Parameters: :table: name of table :d: optional dictionary of values :return_changes: Return values in new row - default True Return type: :dictionary: The dictionary of values inserted Description: This method inserts a row into a table. If the optional dictionary is not supplied then the required values must be included as keyword/value pairs. If a dictionary is supplied then any keywords provided will be added to or replace the entry in the dictionary. The dictionary is then, if possible, reloaded with the values actually inserted in order to pick up values modified by rules, triggers, etc. Due to the way that this function works in PostgreSQL versions below 8.2, you may find inserts taking longer and longer as your table gets bigger. If this happens and it is a table with OID but no primary key you can overcome this problem by simply adding an index onto the OID of any table that you think may get large over time. You may also consider using the inserttable() method described in section 3. Note: With PostgreSQL versions before 8.2 the table being inserted to must have a primary key or an OID to use this method properly. If not then the dictionary will not be filled in as described. Also, if this method is called within a transaction, the transaction will abort. Note: The method currently doesn't support insert into views although PostgreSQL does. update - update a row in a database table ----------------------------------------- Syntax:: update(table, [d,] [key = val, ...]) Parameters: :table: name of table :d: optional dictionary of values Return type: :dictionary: the new row Description: Similar to insert but updates an existing row. The update is based on the OID value as munged by get or passed as keyword, or on the primary key of the table. The dictionary is modified, if possible, to reflect any changes caused by the update due to triggers, rules, default values, etc. Like insert, the dictionary is optional and updates will be performed on the fields in the keywords. There must be an OID or primary key either in the dictionary where the OID must be munged, or in the keywords where it can be simply the string "oid". clear - clears row values in memory ----------------------------------- Syntax:: clear(table, [a]) Parameters: :table: name of table :a: optional dictionary of values Return type: :dictionary: an empty row Description: This method clears all the attributes to values determined by the types. Numeric types are set to 0, Booleans are set to 'f', dates are set to 'now()' 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. If the dictionary is not supplied a new one is created. delete - delete a row from a database table ------------------------------------------- Syntax:: delete(table, [d,] [key = val, ...]) Parameters: :table: name of table :d: optional dictionary of values Returns: None Description: This method deletes the row from a table. It deletes based on the OID value as munged by get or passed as keyword, or on the primary key of the table. The return value is the number of deleted rows (i.e. 0 if the row did not exist and 1 if the row was deleted). escape_string - escape a string for use within SQL -------------------------------------------------- Syntax:: escape_string(string) Parameters: :string: the string that is to be escaped Return type: :str: the escaped string Description: Similar to the module function with the same name, but the behavior of this method is adjusted depending on the connection properties (such as character encoding). escape_bytea - escape binary data for use within SQL as type `bytea` -------------------------------------------------------------------- Syntax:: escape_bytea(datastring) Parameters: :datastring: string containing the binary data that is to be escaped Return type: :str: the escaped string Description: Similar to the module function with the same name, but the behavior of this method is adjusted depending on the connection properties (in particular, whether standard-conforming strings are enabled). unescape_bytea -- unescape `bytea` data that has been retrieved as text ----------------------------------------------------------------------- Syntax:: unescape_bytea(string) Parameters: :datastring: the `bytea` data string that has been retrieved as text Return type: :str: string containing the binary data Description: See the module function with the same name. pgqueryobject methods ===================== getresult - get query values as list of tuples ----------------------------------------------- Syntax:: getresult() Parameters: None Return type: :list: result values as a list of tuples Exceptions raised: :TypeError: too many parameters :pg.InternalError: invalid previous result Description: This method returns the list of the values returned by the query. More information about this result may be accessed using listfields(), fieldname() and fieldnum() methods. dictresult - get query values as list of dictionaries ----------------------------------------------------- Syntax:: dictresult() Parameters: None Return type: :list: result values as a list of dictionaries Exceptions raised: :TypeError: too many parameters :pg.InternalError: 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. listfields - lists fields names of previous query result -------------------------------------------------------- Syntax:: listfields() Parameters: None Return type: :list: field names Exceptions raised: :TypeError: too many parameters :pg.InternalError: invalid previous result, or lost 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. fieldname, fieldnum - field name/number conversion -------------------------------------------------- Syntax:: fieldname(i) Parameters: :i: field number (integer) Return type: :string: field name Exceptions raised: :TypeError: invalid connection, bad parameter type, or too many parameters :ValueError: invalid field number :pg.InternalError: invalid previous result, or lost 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 as the result values. Syntax:: fieldnum(name) Parameters: :name: field name (string) Return type: :integer: field number Exceptions raised: :TypeError: invalid connection, bad parameter type, or too many parameters :ValueError: unknown field name :pg.InternalError: invalid previous result, or lost 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. ntuples - return number of tuples in query object ------------------------------------------------- Syntax:: ntuples() Parameters: None Return type: :integer: number of tuples in `pgqueryobject` Exceptions raised: :TypeError: Too many arguments. Description: This method returns the number of tuples found in a query. Large objects: pglarge ====================== This object handles all the request concerning a PostgreSQL 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 `pglarge` 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. See also the PostgreSQL programmer's guide for more information about the large object interface. open - opens a large object --------------------------- Syntax:: open(mode) Parameters: :mode: open mode definition (integer) Return type: None Exceptions raised: :TypeError: invalid connection, 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). close - closes a large object ----------------------------- Syntax:: close() Parameters: None Return type: None Exceptions raised: :TypeError: invalid connection :TypeError: 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. 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: :TypeError: invalid connection, invalid object, bad parameter type, or too many parameters :ValueError: if `size` is negative :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: :TypeError: invalid connection, 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 Exceptions raised: :TypeError: binvalid connection or invalid object, 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 Exceptions raised: :TypeError: invalid connection or invalid object :TypeError: 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 Exceptions raised: :TypeError: invalid connection or invalid object :TypeError: too many parameters :IOError: object is not closed, or unlink error Description: This methods unlinks (deletes) the PostgreSQL large object. size - gives the large object size ---------------------------------- Syntax:: size() Parameters: None Return type: :integer: the large object size Exceptions raised: :TypeError: invalid connection or invalid object :TypeError: 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. It was implemented because this function is very useful for a web interfaced database. Currently, the large object needs to be opened first. export - saves a large object to a file --------------------------------------- Syntax:: export(name) Parameters: :name: file to be created Return type: None Exceptions raised: :TypeError: invalid connection or invalid object, 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. 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 .. caution:: *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. PyGreSQL-4.0/docs/install.txt010064400017500000000000000151171112713757600151150ustar darcywheel===================== PyGreSQL Installation ===================== .. sectnum:: .. contents:: Contents General ======= You must first have installed Python and PostgreSQL on your system. If you want to access remote database only, you don't need to install the full PostgreSQL server, but only the C interface (libpq). If you are on Windows, make sure that the directory with libpq.dll is in your ``PATH`` environment variable. The current version of PyGreSQL has been tested with Python 2.5 and PostGreSQL 8.3. Older version should work as well, but you will need at least Python 2.3 and PostgreSQL 7.4. PyGreSQL will be installed as three modules, a dynamic module called _pg.pyd, and two pure Python wrapper modules called pg.py and pgdb.py. All three files will be installed directly into the Python site-packages directory. To uninstall PyGreSQL, simply remove these three files again. Installing from a Binary Distribution ===================================== This is the easiest way to install PyGreSQL. You can currently download PyGreSQL as Linux RPM, NetBSD package and Windows installer. Make sure the required Python version of the binary package matches the Python version you have installed. Install the package as usual on your system. Note that the documentation is currently only included in the source package. Installing from Source ====================== If you want to install PyGreSQL from Source, or there is no binary package available for your platform, follow these instructions. Make sure the Python header files and PostgreSQL client and server header files are installed. These come usually with the "devel" packages on Unix systems and the installer executables on Windows systems. If you are using a precompiled PostgreSQL, you will also need the pg_config tool. This is usually also part of the "devel" package on Unix, and will be installed as part of the database server feature on Windows systems. Building and installing with Distutils -------------------------------------- You can build and install PyGreSQL using `Distutils `_. Download and unpack the PyGreSQL source tarball if you haven't already done so. Type the following commands to build and install PyGreSQL:: python setup.py build python setup.py install If you are using `MinGW `_ to build PyGreSQL under Microsoft Windows, please note that Python newer version 2.3 is using msvcr71 instead of msvcrt as its common runtime library. You can allow for that by editing the file ``%MinGWpath%/lib/gcc/%MinGWversion%/specs`` and changing the entry that reads ``-lmsvcrt`` to ``-lmsvcr71``. You may also need to copy ``libpq.lib`` to ``libpq.a`` in the PostgreSQL ``lib`` directory. Then use the following command to build and install PyGreSQL:: python setup.py build -c mingw32 install Now you should be ready to use PyGreSQL. Compiling Manually ------------------ The source file for compiling the dynamic module is called pgmodule.c. You have two options. You can compile PyGreSQL as a stand-alone module or you can build it into the Python interpreter. Stand-Alone ----------- * In the directory containing ``pgmodule.c``, run the following command:: cc -fpic -shared -o _pg.so -I$PYINC -I$PGINC -I$PSINC -L$PGLIB -lpq pgmodule.c where you have to set:: PYINC = path to the Python include files (usually something like /usr/include/python) PGINC = path to the PostgreSQL client include files (something like /usr/include/pgsql or /usr/include/postgresql) PSINC = path to the PostgreSQL server include files (like /usr/include/pgsql/server or /usr/include/postgresql/server) PGLIB = path to the PostgreSQL object code libraries (usually /usr/lib) If you are not sure about the above paths, try something like:: PYINC=`find /usr -name Python.h` PGINC=`find /usr -name libpq-fe.h` PSINC=`find /usr -name postgres.h` PGLIB=`find /usr -name libpq.so` If you have the ``pg_config`` tool installed, you can set:: PGINC=`pg_config --includedir` PSINC=`pg_config --includedir-server` PGLIB=`pg_config --libdir` 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. On some systems you may need to include ``-lcrypt`` in the list of libraries to make it compile. * Test the new module. Something like the following should work:: $ python >>> 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) * Finally, move the ``_pg.so``, ``pg.py``, and ``pgdb.py`` to a directory in your ``PYTHONPATH``. A good place would be ``/usr/lib/python/site-packages`` if your Python modules are in ``/usr/lib/python``. Built-in to Python interpreter ------------------------------ * Find the directory where your ``Setup`` file lives (usually in the ``Modules`` subdirectory) in the Python source hierarchy and copy or symlink the ``pgmodule.c`` file there. * Add the following line to your 'Setup' file:: _pg pgmodule.c -I$PGINC -I$PSINC -L$PGLIB -lpq where:: PGINC = path to the PostgreSQL client include files (see above) PSINC = path to the PostgreSQL server include files (see above) PGLIB = path to the PostgreSQL object code libraries (see above) 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 (see above) On some systems you may need to include ``-lcrypt`` in the list of libraries to make it compile. * 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. * Rebuild Python from the root directory of the Python source hierarchy by running ``make -f Makefile.pre.in boot`` and ``make && make install``. * For more details read the documentation at the top of ``Makefile.pre.in``. PyGreSQL-4.0/docs/future.txt010064400017500000000000000040031112713757600147510ustar darcywheel========================== PyGreSQL future directions ========================== To Do ----- - Documentation for the pgdb module (everything specific to PyGreSQL). - The large object and direct access functions need much more attention. - The C module needs to be cleaned up and redundant code merged, and should get its own unit test module. - The fetch method should use real cursors. - What shall we do with the "tutorial" directory (it's rather a tutorial for Postgres/SQL than for PyGreSQL, it's using only the query method from the classic pg module and no other PyGreSQL functionality, it's rather a demo than a tutorial)? Proposed Patches ---------------- - Notice handling with PQsetNoticeReceiver and PQsetNoticeProcessor (one possible implementation was already suggested by Dmitry Dvoinikov http://mailman.vex.net/pipermail/pygresql/2005-November/001530.html). Maybe also make notifications accessible via the optional cursor and connection attribute "messages" proposed in the DB-API specs. Wish List --------- - Make SQLSTATE error codes available. - Make use of PQexecParams() and PQprepare(). This could speed up executemany() and allow retrieving binary data directly by setting the resultFormat parameter to one. - Support optional "errorhandler" extension. - Support optional cursor and connection attribute "messages". - Connection as context manager (see http://tinyurl.com/6j9cef). - Users should be able to register their own types with _pg. - Let pg and pgdb support namedtuples (as available in Py 2.6). pg could get a new method namedresult(), and pgdb could provide a row factory for namedtuples (similar to sqlite3). - New methods in the classic module, similar to getresult() and dictresult(), but returning dictionaries of rows instead of lists of rows (with primary key or oids as keys). - Make PyGreSQL thread-safe on the connection level. - The API documentation could be created with Epydoc. - Write a tutorial for beginners and advanced use. - More and better documented examples. PyGreSQL-4.0/docs/changelog.txt010064400017500000000000000265141112713757600154010ustar darcywheel================== PyGreSQL ChangeLog ================== Version 4.0 (2009-01-01) ------------------------ - Dropped support for Python below 2.3 and PostgreSQL below 7.4. - Improved performance of fetchall() for large result sets by speeding up the type casts (as suggested by Peter Schuller). - Exposed exceptions as attributes of the connection object. - Exposed connection as attribute of the cursor object. - Cursors now support the iteration protocol. - Added new method to get parameter settings. - Added customizable row_factory as suggested by Simon Pamies. - Separated between mandatory and additional type objects. - Added keyword args to insert, update and delete methods. - Added exception handling for direct copy. - Release the GIL while making a connection (as suggested by Peter Schuller). - If available, use decimal.Decimal for numeric types. - Allow DB wrapper to be used with DB-API 2 connections (as suggested by Chris Hilton). - Made private attributes of DB wrapper accessible. - Dropped dependence on mx.DateTime module. - Support for PQescapeStringConn() and PQescapeByteaConn(); these are now also used by the internal _quote() functions. - Added 'int8' to INTEGER types. New SMALLINT type. - Added a way to find the number of rows affected by a query() with the classic pg module by returning it as a string. For single inserts, query() still returns the oid as an integer. The pgdb module already provides the "rowcount" cursor attribute for the same purpose. - Improved getnotify() by calling PQconsumeInput() instead of submitting an empty command. - Removed compatibility code for old OID munging style. - The insert() and update() methods now use the "returning" clause if possible to get all changed values, and they also check in advance whether a subsequent select is possible, so that ongoing transactions won't break if there is no select privilege. - Added "protocol_version" and "server_version" attributes. - Revived the "user" attribute. - The pg module now works correctly with composite primary keys; these are represented as frozensets. - Removed the undocumented and actually unnecessary "view" parameter from the get() method. - get() raises a nicer ProgrammingError instead of a KeyError if no primary key was found. - delete() now also works based on the primary key if no oid available and returns whether the row existed or not. Version 3.8.1 (2006-06-05) -------------------------- - Use string methods instead of deprecated string functions. - Only use SQL-standard way of escaping quotes. - Added the functions escape_string() and escape/unescape_bytea() (as suggested by Charlie Dyson and Kavous Bojnourdi a long time ago). - Reverted code in clear() method that set date to current. - Added code for backwards compatibility in OID munging code. - Reorder attnames tests so that "interval" is checked for before "int." - If caller supplies key dictionary, make sure that all has a namespace. Version 3.8 (2006-02-17) ------------------------ - Installed new favicon.ico from Matthew Sporleder - Replaced snprintf by PyOS_snprintf. - Removed NO_SNPRINTF switch which is not needed any longer - Clean up some variable names and namespace - Add get_relations() method to get any type of relation - Rewrite get_tables() to use get_relations() - Use new method in get_attnames method to get attributes of views as well - Add Binary type - Number of rows is now -1 after executing no-result statements - Fix some number handling - Non-simple types do not raise an error any more - Improvements to documentation framework - Take into account that nowadays not every table must have an oid column - Simplification and improvement of the inserttable() function - Fix up unit tests - The usual assortment of minor fixes and enhancements Version 3.7 (2005-09-07) ------------------------ Improvement of pgdb module: - Use Python standard `datetime` if `mxDateTime` is not available Major improvements and clean-up in classic pg module: - All members of the underlying connection directly available in `DB` - Fixes to quoting function - Add checks for valid database connection to methods - Improved namespace support, handle `search_path` correctly - Removed old dust and unnessesary imports, added docstrings - Internal sql statements as one-liners, smoothed out ugly code Version 3.6.2 (2005-02-23) -------------------------- - Further fixes to namespace handling Version 3.6.1 (2005-01-11) -------------------------- - Fixes to namespace handling Version 3.6 (2004-12-17) ------------------------ - Better DB-API 2.0 compliance - Exception hierarchy moved into C module and made available to both APIs - Fix error in update method that caused false exceptions - Moved to standard exception hierarchy in classic API - Added new method to get transaction state - Use proper Python constants where appropriate - Use Python versions of strtol, etc. Allows Win32 build. - Bug fixes and cleanups Version 3.5 (2004-08-29) ------------------------ Fixes and enhancements: - Add interval to list of data types - fix up method wrapping especially close() - retry pkeys once if table missing in case it was just added - wrap query method separately to handle debug better - use isinstance instead of type - fix free/PQfreemem issue - finally - miscellaneous cleanups and formatting Version 3.4 (2004-06-02) ------------------------ Some cleanups and fixes. This is the first version where PyGreSQL is moved back out of the PostgreSQL tree. A lot of the changes mentioned below were actually made while in the PostgreSQL tree since their last release. - Allow for larger integer returns - Return proper strings for true and false - Cleanup convenience method creation - Enhance debugging method - Add reopen method - Allow programs to preload field names for speedup - Move OID handling so that it returns long instead of int - Miscellaneous cleanups and formatting Version 3.3 (2001-12-03) ------------------------ A few cleanups. Mostly there was some confusion about the latest version and so I am bumping the number to keep it straight. - Added NUMERICOID to list of returned types. This fixes a bug when returning aggregates in the latest version of PostgreSQL. Version 3.2 (2001-06-20) ------------------------ Note that there are very few changes to PyGreSQL between 3.1 and 3.2. The main reason for the release is the move into the PostgreSQL development tree. Even the WIN32 changes are pretty minor. - Add Win32 support (gerhard@bigfoot.de) - Fix some DB-API quoting problems (niall.smart@ebeon.com) - Moved development into PostgreSQL development tree. Version 3.1 (2000-11-06) ------------------------ - Fix some quoting functions. In particular handle NULLs better. - Use a method to add primary key information rather than direct manipulation of the class structures - Break decimal out in `_quote` (in pg.py) and treat it as float - Treat timestamp like date for quoting purposes - Remove a redundant SELECT from the `get` method speeding it, and `insert` (since it calls `get`) up a little. - Add test for BOOL type in typecast method to `pgdbTypeCache` class (tv@beamnet.de) - Fix pgdb.py to send port as integer to lower level function (dildog@l0pht.com) - Change pg.py to speed up some operations - Allow updates on tables with no primary keys Version 3.0 (2000-05-30) ------------------------ - Remove strlen() call from pglarge_write() and get size from object (Richard@Bouska.cz) - Add a little more error checking to the quote function in the wrapper - Add extra checking in `_quote` function - Wrap query in pg.py for debugging - Add DB-API 2.0 support to pgmodule.c (andre@via.ecp.fr) - Add DB-API 2.0 wrapper pgdb.py (andre@via.ecp.fr) - Correct keyword clash (temp) in tutorial - Clean up layout of tutorial - Return NULL values as None (rlawrence@lastfoot.com) (WARNING: This will cause backwards compatibility issues) - Change None to NULL in insert and update - Change hash-bang lines to use /usr/bin/env - Clearing date should be blank (NULL) not TODAY - Quote backslashes in strings in `_quote` (brian@CSUA.Berkeley.EDU) - Expanded and clarified build instructions (tbryan@starship.python.net) - Make code thread safe (Jerome.Alet@unice.fr) - Add README.distutils (mwa@gate.net & jeremy@cnri.reston.va.us) - Many fixes and increased DB-API compliance by chifungfan@yahoo.com, tony@printra.net, jeremy@alum.mit.edu and others to get the final version ready to release. Version 2.4 (1999-06-15) ------------------------ - Insert returns None if the user doesn't have select permissions on the table. It can (and does) happen that one has insert but not select permissions on a table. - Added ntuples() method to query object (brit@druid.net) - Corrected a bug related to getresult() and the money type - Corrected a bug related to negative money amounts - Allow update based on primary key if munged oid not available and table has a primary key - Add many __doc__ strings (andre@via.ecp.fr) - Get method works with views if key specified Version 2.3 (1999-04-17) ------------------------ - 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 (1998-12-21) ------------------------ - Added user and password support thanks to Ng Pheng Siong (ngps@post1.com) - Insert queries return the inserted oid - Add new `pg` wrapper (C module 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 (1998-03-07) ------------------------ - return fields as proper Python objects for field type - Cleaned up pgext.py - Added dictresult method Version 2.0 (1997-12-23) ------------------------- - Updated code for PostgreSQL 6.2.1 and Python 1.5 - Reformatted code and converted to use full ANSI style prototypes - 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 - Various code changes - mostly stylistic Version 1.0b (1995-11-04) ------------------------- - 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 (1995-10-30) ------------------------- A 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 (1995-10-10) ------------------------- The first public release. - Large objects implementation - Many bug fixes, enhancements, ... Version 0.1a (1995-10-07) ------------------------- - Basic libpq functions (SQL access) PyGreSQL-4.0/docs/docs.css010064400017500000000000000030101112713757600143350ustar darcywheel/* Stylesheet for use with Docutils. Customized for PyGreSQL docs. */ @import url(default.css); body { margin: 8pt; padding: 8pt; background-color: #f8f8ff; color: #000008; text-align: justify; font-family: Arial, Verdana, Helvetica, sans-serif; font-size: 11pt; } a { text-decoration: none; } a:hover { text-decoration: underline; } .title, .subtitle { color: #003; } .topic-title { color: #006; font-size: 14pt; } h1, h2, h3, h4 { color: #006; } h1 { padding-top: 20pt; font-size: 17pt; } div#pygresql-changelog div.section h1 { font-size: 12pt; } h1.title { font-size: 20pt; } h2 { font-size: 14pt; } h2.subtitle { font-size: 16pt; } h3 { font-size: 13pt; } h4 { font-size: 12pt; } a.toc-backref { color: #006; } ul.simple li { margin-top: 4pt; } ul.simple ul li { margin-top: 2pt; } div.contents ul { list-style-type: none; } div.contents ul li { margin-top: 4pt; font-size: 12pt; } div.contents ul ul li { margin-top: 2pt; font-size: 11pt; } cite { font-style: normal; font-family: monospace; font-weight: bold; } table.field-list th.field-name { font-style: normal; font-family: monospace; font-weight: bold; } tt.literal, pre.literal-block { font-style: normal; font-family: monospace; font-weight: bold; background-color: #fff; } tt.literal { padding-left: 2pt; padding-right: 2pt; } pre.literal-block { padding: 4pt; border: 1px dotted #ccc; } table.docutils { border-spacing: 0px; border-collapse: collapse; } table.docutils td { margin: 0px; padding: 2pt; } PyGreSQL-4.0/tutorial004075500017500000000000000000001112713757600135405ustar darcywheelPyGreSQL-4.0/tutorial/syscat.py010075500017500000000000000124021112713757600154760ustar darcywheel# syscat.py - parses some system catalogs # inspired from the PostgreSQL tutorial # adapted to Python 1995 by Pascal ANDRE print """ __________________________________________________________________ MODULE SYSCAT.PY : PARSES SOME POSTGRESQL SYSTEM CATALOGS This module is designed for being imported from python prompt In order to run the samples included here, first create a connection using : cnx = syscat.DB(...) The "..." should be replaced with whatever arguments you need to open an existing database. Usually all you need is the name of the database and, in fact, if it is the same as your login name, you can leave it empty. then start the demo with: syscat.demo(cnx) Some results may be empty, depending on your base status." __________________________________________________________________ """ from pg import DB import sys # waits for a key def wait_key(): print "Press " sys.stdin.read(1) # lists all simple indices def list_simple_ind(pgcnx): result = pgcnx.query("""SELECT bc.relname AS class_name, ic.relname AS index_name, a.attname FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a WHERE i.indrelid = bc.oid AND i.indexrelid = bc.oid AND i.indkey[0] = a.attnum AND a.attrelid = bc.oid AND i.indproc = '0'::oid AND a.attisdropped = 'f' ORDER BY class_name, index_name, attname""") return result # list all user defined attributes and their type in user-defined classes def list_all_attr(pgcnx): result = pgcnx.query("""SELECT c.relname, a.attname, t.typname FROM pg_class c, pg_attribute a, pg_type t WHERE c.relkind = 'r' and c.relname !~ '^pg_' AND c.relname !~ '^Inv' and a.attnum > 0 AND a.attrelid = c.oid and a.atttypid = t.oid AND a.attisdropped = 'f' ORDER BY relname, attname""") return result # list all user defined base type def list_user_base_type(pgcnx): result = pgcnx.query("""SELECT u.usename, t.typname FROM pg_type t, pg_user u WHERE u.usesysid = int2in(int4out(t.typowner)) AND t.typrelid = '0'::oid and t.typelem = '0'::oid AND u.usename <> 'postgres' order by usename, typname""") return result # list all right-unary operators def list_right_unary_operator(pgcnx): result = pgcnx.query("""SELECT o.oprname AS right_unary, lt.typname AS operand, result.typname AS return_type FROM pg_operator o, pg_type lt, pg_type result WHERE o.oprkind='r' and o.oprleft = lt.oid AND o.oprresult = result.oid ORDER BY operand""") return result # list all left-unary operators def list_left_unary_operator(pgcnx): result = pgcnx.query("""SELECT o.oprname AS left_unary, rt.typname AS operand, result.typname AS return_type FROM pg_operator o, pg_type rt, pg_type result WHERE o.oprkind='l' AND o.oprright = rt.oid AND o.oprresult = result.oid ORDER BY operand""") return result # list all binary operators def list_binary_operator(pgcnx): result = pgcnx.query("""SELECT o.oprname AS binary_op, rt.typname AS right_opr, lt.typname AS left_opr, result.typname AS return_type FROM pg_operator o, pg_type rt, pg_type lt, pg_type result WHERE o.oprkind = 'b' AND o.oprright = rt.oid AND o.oprleft = lt.oid AND o.oprresult = result.oid""") return result # returns the name, args and return type from all function of lang l def list_lang_func(pgcnx, l): result = pgcnx.query("""SELECT p.proname, p.pronargs, t.typname FROM pg_proc p, pg_language l, pg_type t WHERE p.prolang = l.oid AND p.prorettype = t.oid AND l.lanname = '%s' ORDER BY proname""" % l) return result # lists all the aggregate functions and the type to which they can be applied def list_agg_func(pgcnx): result = pgcnx.query("""SELECT p.proname, t.typname FROM pg_aggregate a, pg_proc p, pg_type t WHERE a.aggfnoid = p.oid and p.proargtypes[0] = t.oid ORDER BY proname, typname""") return result # lists all the operator classes that can be used with each access method as # well as the operators that can be used with the respective operator classes def list_op_class(pgcnx): result = pgcnx.query("""SELECT am.amname, opc.opcname, opr.oprname FROM pg_am am, pg_amop amop, pg_opclass opc, pg_operator opr WHERE amop.amopid = am.oid and amop.amopclaid = opc.oid AND amop.amopopr = opr.oid order by amname, opcname, oprname""") return result # demo function - runs all examples def demo(pgcnx): import sys, os save_stdout = sys.stdout sys.stdout = os.popen("more", "w") print "Listing simple indices ..." print list_simple_ind(pgcnx) print "Listing all attributes ..." print list_all_attr(pgcnx) print "Listing all user-defined base types ..." print list_user_base_type(pgcnx) print "Listing all left-unary operators defined ..." print list_left_unary_operator(pgcnx) print "Listing all right-unary operators defined ..." print list_right_unary_operator(pgcnx) print "Listing all binary operators ..." print list_binary_operator(pgcnx) print "Listing C external function linked ..." print list_lang_func(pgcnx, 'C') print "Listing C internal functions ..." print list_lang_func(pgcnx, 'internal') print "Listing SQL functions defined ..." print list_lang_func(pgcnx, 'sql') print "Listing 'aggregate functions' ..." print list_agg_func(pgcnx) print "Listing 'operator classes' ..." print list_op_class(pgcnx) del sys.stdout sys.stdout = save_stdout PyGreSQL-4.0/tutorial/func.py010075500017500000000000000165031112713757600151310ustar darcywheel# func.py - demonstrate the use of SQL functions # inspired from the PostgreSQL tutorial # adapted to Python 1995 by Pascal ANDRE print """ __________________________________________________________________ MODULE FUNC.PY : SQL FUNCTION DEFINITION TUTORIAL This module is designed for being imported from python prompt In order to run the samples included here, first create a connection using : cnx = func.DB(...) The "..." should be replaced with whatever arguments you need to open an existing database. Usually all you need is the name of the database and, in fact, if it is the same as your login name, you can leave it empty. then start the demo with: func.demo(cnx) __________________________________________________________________ """ from pg import DB import sys # waits for a key def wait_key(): print "Press " sys.stdin.read(1) # basic functions declaration def base_func(pgcnx): print "-----------------------------" print "-- Creating SQL Functions on Base Types" print "-- a CREATE FUNCTION statement lets you create a new " print "-- function that can be used in expressions (in SELECT, " print "-- INSERT, etc.). We will start with functions that " print "-- return values of base types." print "-----------------------------" print print "--" print "-- let's create a simple SQL function that takes no arguments" print "-- and returns 1" print print "CREATE FUNCTION one() RETURNS int4" print " AS 'SELECT 1 as ONE' LANGUAGE 'sql'" pgcnx.query("""CREATE FUNCTION one() RETURNS int4 AS 'SELECT 1 as ONE' LANGUAGE 'sql'""") wait_key() print print "--" print "-- functions can be used in any expressions (eg. in the target" print "-- list or qualifications)" print print "SELECT one() AS answer" print pgcnx.query("SELECT one() AS answer") print print "--" print "-- here's how you create a function that takes arguments. The" print "-- following function returns the sum of its two arguments:" print print "CREATE FUNCTION add_em(int4, int4) RETURNS int4" print " AS 'SELECT $1 + $2' LANGUAGE 'sql'" pgcnx.query("""CREATE FUNCTION add_em(int4, int4) RETURNS int4 AS 'SELECT $1 + $2' LANGUAGE 'sql'""") print print "SELECT add_em(1, 2) AS answer" print pgcnx.query("SELECT add_em(1, 2) AS answer") # functions on composite types def comp_func(pgcnx): print "-----------------------------" print "-- Creating SQL Functions on Composite Types" print "-- it is also possible to create functions that return" print "-- values of composite types." print "-----------------------------" print print "-- before we create more sophisticated functions, let's " print "-- populate an EMP table" print print "CREATE TABLE EMP (" print " name text," print " salary int4," print " age int4," print " dept varchar(16)" print ")" pgcnx.query("""CREATE TABLE EMP ( name text, salary int4, age int4, dept varchar(16))""") print print "INSERT INTO EMP VALUES ('Sam', 1200, 16, 'toy')" print "INSERT INTO EMP VALUES ('Claire', 5000, 32, 'shoe')" print "INSERT INTO EMP VALUES ('Andy', -1000, 2, 'candy')" print "INSERT INTO EMP VALUES ('Bill', 4200, 36, 'shoe')" print "INSERT INTO EMP VALUES ('Ginger', 4800, 30, 'candy')" pgcnx.query("INSERT INTO EMP VALUES ('Sam', 1200, 16, 'toy')") pgcnx.query("INSERT INTO EMP VALUES ('Claire', 5000, 32, 'shoe')") pgcnx.query("INSERT INTO EMP VALUES ('Andy', -1000, 2, 'candy')") pgcnx.query("INSERT INTO EMP VALUES ('Bill', 4200, 36, 'shoe')") pgcnx.query("INSERT INTO EMP VALUES ('Ginger', 4800, 30, 'candy')") wait_key() print print "-- the argument of a function can also be a tuple. For " print "-- instance, double_salary takes a tuple of the EMP table" print print "CREATE FUNCTION double_salary(EMP) RETURNS int4" print " AS 'SELECT $1.salary * 2 AS salary' LANGUAGE 'sql'" pgcnx.query("""CREATE FUNCTION double_salary(EMP) RETURNS int4 AS 'SELECT $1.salary * 2 AS salary' LANGUAGE 'sql'""") print print "SELECT name, double_salary(EMP) AS dream" print "FROM EMP" print "WHERE EMP.dept = 'toy'" print pgcnx.query("""SELECT name, double_salary(EMP) AS dream FROM EMP WHERE EMP.dept = 'toy'""") print print "-- the return value of a function can also be a tuple. However," print "-- make sure that the expressions in the target list is in the " print "-- same order as the columns of EMP." print print "CREATE FUNCTION new_emp() RETURNS EMP" print " AS 'SELECT \'None\'::text AS name," print " 1000 AS salary," print " 25 AS age," print " \'none\'::varchar(16) AS dept'" print " LANGUAGE 'sql'" pgcnx.query("""CREATE FUNCTION new_emp() RETURNS EMP AS 'SELECT \\\'None\\\'::text AS name, 1000 AS salary, 25 AS age, \\\'none\\\'::varchar(16) AS dept' LANGUAGE 'sql'""") wait_key() print print "-- you can then project a column out of resulting the tuple by" print "-- using the \"function notation\" for projection columns. " print "-- (ie. bar(foo) is equivalent to foo.bar) Note that we don't" print "-- support new_emp().name at this moment." print print "SELECT name(new_emp()) AS nobody" print pgcnx.query("SELECT name(new_emp()) AS nobody") print print "-- let's try one more function that returns tuples" print "CREATE FUNCTION high_pay() RETURNS setof EMP" print " AS 'SELECT * FROM EMP where salary > 1500'" print " LANGUAGE 'sql'" pgcnx.query("""CREATE FUNCTION high_pay() RETURNS setof EMP AS 'SELECT * FROM EMP where salary > 1500' LANGUAGE 'sql'""") print print "SELECT name(high_pay()) AS overpaid" print pgcnx.query("SELECT name(high_pay()) AS overpaid") # function with multiple SQL commands def mult_func(pgcnx): print "-----------------------------" print "-- Creating SQL Functions with multiple SQL statements" print "-- you can also create functions that do more than just a" print "-- SELECT." print "-----------------------------" print print "-- you may have noticed that Andy has a negative salary. We'll" print "-- create a function that removes employees with negative " print "-- salaries." print print "SELECT * FROM EMP" print pgcnx.query("SELECT * FROM EMP") print print "CREATE FUNCTION clean_EMP () RETURNS int4" print " AS 'DELETE FROM EMP WHERE EMP.salary <= 0" print " SELECT 1 AS ignore_this'" print " LANGUAGE 'sql'" pgcnx.query("CREATE FUNCTION clean_EMP () RETURNS int4 AS 'DELETE FROM EMP WHERE EMP.salary <= 0; SELECT 1 AS ignore_this' LANGUAGE 'sql'") print print "SELECT clean_EMP()" print pgcnx.query("SELECT clean_EMP()") print print "SELECT * FROM EMP" print pgcnx.query("SELECT * FROM EMP") # base cleanup def demo_cleanup(pgcnx): print "-- remove functions that were created in this file" print print "DROP FUNCTION clean_EMP()" print "DROP FUNCTION high_pay()" print "DROP FUNCTION new_emp()" print "DROP FUNCTION add_em(int4, int4)" print "DROP FUNCTION one()" print print "DROP TABLE EMP CASCADE" pgcnx.query("DROP FUNCTION clean_EMP()") pgcnx.query("DROP FUNCTION high_pay()") pgcnx.query("DROP FUNCTION new_emp()") pgcnx.query("DROP FUNCTION add_em(int4, int4)") pgcnx.query("DROP FUNCTION one()") pgcnx.query("DROP TABLE EMP CASCADE") # main demo function def demo(pgcnx): base_func(pgcnx) comp_func(pgcnx) mult_func(pgcnx) demo_cleanup(pgcnx) PyGreSQL-4.0/tutorial/basics.py010075500017500000000000000243631112713757600154450ustar darcywheel#! /usr/bin/env python # basics.py - basic SQL commands tutorial # inspired from the Postgres95 tutorial # adapted to Python 1995 by Pascal ANDRE print """ __________________________________________________________________ MODULE BASICS.PY : BASIC POSTGRES SQL COMMANDS TUTORIAL This module is designed for being imported from python prompt In order to run the samples included here, first create a connection using : cnx = basics.DB(...) The "..." should be replaced with whatever arguments you need to open an existing database. Usually all you need is the name of the database and, in fact, if it is the same as your login name, you can leave it empty. then start the demo with: basics.demo(cnx) __________________________________________________________________ """ from pg import DB import sys # waits for a key def wait_key(): print "Press " sys.stdin.read(1) # table creation commands def create_table(pgcnx): print "-----------------------------" print "-- Creating a table:" print "-- a CREATE TABLE is used to create base tables. POSTGRES" print "-- SQL has its own set of built-in types. (Note that" print "-- keywords are case-insensitive but identifiers are " print "-- case-sensitive.)" print "-----------------------------" print print "Sending query :" print "CREATE TABLE weather (" print " city varchar(80)," print " temp_lo int," print " temp_hi int," print " prcp float8," print " date date" print ")" pgcnx.query("""CREATE TABLE weather (city varchar(80), temp_lo int, temp_hi int, prcp float8, date date)""") print print "Sending query :" print "CREATE TABLE cities (" print " name varchar(80)," print " location point" print ")" pgcnx.query("""CREATE TABLE cities ( name varchar(80), location point)""") # data insertion commands def insert_data(pgcnx): print "-----------------------------" print "-- Inserting data:" print "-- an INSERT statement is used to insert a new row into" print "-- a table. There are several ways you can specify what" print "-- columns the data should go to." print "-----------------------------" print print "-- 1. the simplest case is when the list of value correspond to" print "-- the order of the columns specified in CREATE TABLE." print print "Sending query :" print "INSERT INTO weather " print " VALUES ('San Francisco', 46, 50, 0.25, '11/27/1994')" pgcnx.query("""INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '11/27/1994')""") print print "Sending query :" print "INSERT INTO cities " print " VALUES ('San Francisco', '(-194.0, 53.0)')" pgcnx.query("""INSERT INTO cities VALUES ('San Francisco', '(-194.0, 53.0)')""") print wait_key() print "-- 2. you can also specify what column the values correspond " print " to. (The columns can be specified in any order. You may " print " also omit any number of columns. eg. unknown precipitation" print " below)" print "Sending query :" print "INSERT INTO weather (city, temp_lo, temp_hi, prcp, date)" print " VALUES ('San Francisco', 43, 57, 0.0, '11/29/1994')" pgcnx.query("INSERT INTO weather (date, city, temp_hi, temp_lo)" \ "VALUES ('11/29/1994', 'Hayward', 54, 37)") # direct selection commands def select_data1(pgcnx): print "-----------------------------" print "-- Retrieving data:" print "-- a SELECT statement is used for retrieving data. The " print "-- basic syntax is:" print "-- SELECT columns FROM tables WHERE predicates" print "-----------------------------" print print "-- a simple one would be the query:" print "SELECT * FROM weather" print print "The result is :" q = pgcnx.query("SELECT * FROM weather") print q print print "-- you may also specify expressions in the target list (the " print "-- 'AS column' specifies the column name of the result. It is " print "-- optional.)" print "The query :" print " SELECT city, (temp_hi+temp_lo)/2 AS temp_avg, date " print " FROM weather" print "Gives :" print pgcnx.query("""SELECT city, (temp_hi+temp_lo)/2 AS temp_avg, date FROM weather""") print print "-- if you want to retrieve rows that satisfy certain condition" print "-- (ie. a restriction), specify the condition in WHERE. The " print "-- following retrieves the weather of San Francisco on rainy " print "-- days." print "SELECT *" print "FROM weather" print "WHERE city = 'San Francisco' " print " and prcp > 0.0" print pgcnx.query("""SELECT * FROM weather WHERE city = 'San Francisco' AND prcp > 0.0""") print print "-- here is a more complicated one. Duplicates are removed when " print "-- DISTINCT is specified. ORDER BY specifies the column to sort" print "-- on. (Just to make sure the following won't confuse you, " print "-- DISTINCT and ORDER BY can be used separately.)" print "SELECT DISTINCT city" print "FROM weather" print "ORDER BY city;" print pgcnx.query("SELECT DISTINCT city FROM weather ORDER BY city") # selection to a temporary table def select_data2(pgcnx): print "-----------------------------" print "-- Retrieving data into other classes:" print "-- a SELECT ... INTO statement can be used to retrieve " print "-- data into another class." print "-----------------------------" print print "The query :" print "SELECT * INTO TABLE temptab " print "FROM weather" print "WHERE city = 'San Francisco' " print " and prcp > 0.0" pgcnx.query("""SELECT * INTO TABLE temptab FROM weather WHERE city = 'San Francisco' and prcp > 0.0""") print "Fills the table temptab, that can be listed with :" print "SELECT * from temptab" print pgcnx.query("SELECT * from temptab") # aggregate creation commands def create_aggregate(pgcnx): print "-----------------------------" print "-- Aggregates" print "-----------------------------" print print "Let's consider the query :" print "SELECT max(temp_lo)" print "FROM weather;" print pgcnx.query("SELECT max(temp_lo) FROM weather") print print "-- Aggregate with GROUP BY" print "SELECT city, max(temp_lo)" print "FROM weather " print "GROUP BY city;" print pgcnx.query( """SELECT city, max(temp_lo) FROM weather GROUP BY city""") # table join commands def join_table(pgcnx): print "-----------------------------" print "-- Joining tables:" print "-- queries can access multiple tables at once or access" print "-- the same table in such a way that multiple instances" print "-- of the table are being processed at the same time." print "-----------------------------" print print "-- suppose we want to find all the records that are in the " print "-- temperature range of other records. W1 and W2 are aliases " print "--for weather." print print "SELECT W1.city, W1.temp_lo, W1.temp_hi, " print " W2.city, W2.temp_lo, W2.temp_hi" print "FROM weather W1, weather W2" print "WHERE W1.temp_lo < W2.temp_lo " print " and W1.temp_hi > W2.temp_hi" print print pgcnx.query("""SELECT W1.city, W1.temp_lo, W1.temp_hi, W2.city, W2.temp_lo, W2.temp_hi FROM weather W1, weather W2 WHERE W1.temp_lo < W2.temp_lo and W1.temp_hi > W2.temp_hi""") print print "-- let's join two tables. The following joins the weather table" print "-- and the cities table." print print "SELECT city, location, prcp, date" print "FROM weather, cities" print "WHERE name = city" print print pgcnx.query("""SELECT city, location, prcp, date FROM weather, cities WHERE name = city""") print print "-- since the column names are all different, we don't have to " print "-- specify the table name. If you want to be clear, you can do " print "-- the following. They give identical results, of course." print print "SELECT w.city, c.location, w.prcp, w.date" print "FROM weather w, cities c" print "WHERE c.name = w.city;" print print pgcnx.query("""SELECT w.city, c.location, w.prcp, w.date FROM weather w, cities c WHERE c.name = w.city""") # data updating commands def update_data(pgcnx): print "-----------------------------" print "-- Updating data:" print "-- an UPDATE statement is used for updating data. " print "-----------------------------" print print "-- suppose you discover the temperature readings are all off by" print "-- 2 degrees as of Nov 28, you may update the data as follow:" print print "UPDATE weather" print " SET temp_hi = temp_hi - 2, temp_lo = temp_lo - 2" print " WHERE date > '11/28/1994'" print pgcnx.query("""UPDATE weather SET temp_hi = temp_hi - 2, temp_lo = temp_lo - 2 WHERE date > '11/28/1994'""") print print "SELECT * from weather" print pgcnx.query("SELECT * from weather") # data deletion commands def delete_data(pgcnx): print "-----------------------------" print "-- Deleting data:" print "-- a DELETE statement is used for deleting rows from a " print "-- table." print "-----------------------------" print print "-- suppose you are no longer interested in the weather of " print "-- Hayward, you can do the following to delete those rows from" print "-- the table" print print "DELETE FROM weather WHERE city = 'Hayward'" pgcnx.query("DELETE FROM weather WHERE city = 'Hayward'") print print "SELECT * from weather" print print pgcnx.query("SELECT * from weather") print print "-- you can also delete all the rows in a table by doing the " print "-- following. (This is different from DROP TABLE which removes " print "-- the table in addition to the removing the rows.)" print print "DELETE FROM weather" pgcnx.query("DELETE FROM weather") print print "SELECT * from weather" print pgcnx.query("SELECT * from weather") # table removal commands def remove_table(pgcnx): print "-----------------------------" print "-- Removing the tables:" print "-- DROP TABLE is used to remove tables. After you have" print "-- done this, you can no longer use those tables." print "-----------------------------" print print "DROP TABLE weather, cities, temptab" pgcnx.query("DROP TABLE weather, cities, temptab") # main demo function def demo(pgcnx): create_table(pgcnx) wait_key() insert_data(pgcnx) wait_key() select_data1(pgcnx) select_data2(pgcnx) create_aggregate(pgcnx) join_table(pgcnx) update_data(pgcnx) delete_data(pgcnx) remove_table(pgcnx) PyGreSQL-4.0/tutorial/advanced.py010075500017500000000000000153731112713757600157470ustar darcywheel#! /usr/bin/env python # advanced.py - demo of advanced features of PostGres. Some may not be ANSI. # inspired from the Postgres tutorial # adapted to Python 1995 by Pascal Andre print """ __________________________________________________________________ MODULE ADVANCED.PY : ADVANCED POSTGRES SQL COMMANDS TUTORIAL This module is designed for being imported from python prompt In order to run the samples included here, first create a connection using : cnx = advanced.DB(...) The "..." should be replaced with whatever arguments you need to open an existing database. Usually all you need is the name of the database and, in fact, if it is the same as your login name, you can leave it empty. then start the demo with: advanced.demo(cnx) __________________________________________________________________ """ from pg import DB import sys # waits for a key def wait_key(): print "Press " sys.stdin.read(1) # inheritance features def inherit_demo(pgcnx): print "-----------------------------" print "-- Inheritance:" print "-- a table can inherit from zero or more tables. A query" print "-- can reference either all rows of a table or all rows " print "-- of a table plus all of its descendants." print "-----------------------------" print print "-- For example, the capitals table inherits from cities table." print "-- (It inherits all data fields from cities.)" print print "CREATE TABLE cities (" print " name text," print " population float8," print " altitude int" print ")" print print "CREATE TABLE capitals (" print " state varchar(2)" print ") INHERITS (cities)" pgcnx.query("""CREATE TABLE cities ( name text, population float8, altitude int)""") pgcnx.query("""CREATE TABLE capitals ( state varchar(2)) INHERITS (cities)""") wait_key() print print "-- now, let's populate the tables" print print "INSERT INTO cities VALUES ('San Francisco', 7.24E+5, 63)" print "INSERT INTO cities VALUES ('Las Vegas', 2.583E+5, 2174)" print "INSERT INTO cities VALUES ('Mariposa', 1200, 1953)" print print "INSERT INTO capitals VALUES ('Sacramento', 3.694E+5, 30, 'CA')" print "INSERT INTO capitals VALUES ('Madison', 1.913E+5, 845, 'WI')" print pgcnx.query("INSERT INTO cities VALUES ('San Francisco', 7.24E+5, 63)") pgcnx.query("INSERT INTO cities VALUES ('Las Vegas', 2.583E+5, 2174)") pgcnx.query("INSERT INTO cities VALUES ('Mariposa', 1200, 1953)") pgcnx.query("INSERT INTO capitals VALUES ('Sacramento',3.694E+5,30,'CA')") pgcnx.query("INSERT INTO capitals VALUES ('Madison', 1.913E+5, 845, 'WI')") print print "SELECT * FROM cities" print pgcnx.query("SELECT * FROM cities") print "SELECT * FROM capitals" print pgcnx.query("SELECT * FROM capitals") print print "-- like before, a regular query references rows of the base" print "-- table only" print print "SELECT name, altitude" print "FROM cities" print "WHERE altitude > 500;" print pgcnx.query("""SELECT name, altitude FROM cities WHERE altitude > 500""") print print "-- on the other hand, you can find all cities, including " print "-- capitals, that are located at an altitude of 500 'ft " print "-- or higher by:" print print "SELECT c.name, c.altitude" print "FROM cities* c" print "WHERE c.altitude > 500" print pgcnx.query("""SELECT c.name, c.altitude FROM cities* c WHERE c.altitude > 500""") # arrays attributes def array_demo(pgcnx): print "----------------------" print "-- Arrays:" print "-- attributes can be arrays of base types or user-defined " print "-- types" print "----------------------" print print "CREATE TABLE sal_emp (" print " name text," print " pay_by_quarter int4[]," print " pay_by_extra_quarter int8[]," print " schedule text[][]" print ")" pgcnx.query("""CREATE TABLE sal_emp ( name text, pay_by_quarter int4[], pay_by_extra_quarter int8[], schedule text[][])""") wait_key() print print "-- insert instances with array attributes. " print " Note the use of braces" print print "INSERT INTO sal_emp VALUES (" print " 'Bill'," print " '{10000,10000,10000,10000}'," print " '{9223372036854775800,9223372036854775800,9223372036854775800}'," print " '{{\"meeting\", \"lunch\"}, {}}')" print print "INSERT INTO sal_emp VALUES (" print " 'Carol'," print " '{20000,25000,25000,25000}'," print " '{9223372036854775807,9223372036854775807,9223372036854775807}'," print " '{{\"talk\", \"consult\"}, {\"meeting\"}}')" print pgcnx.query("""INSERT INTO sal_emp VALUES ( 'Bill', '{10000,10000,10000,10000}', '{9223372036854775800,9223372036854775800,9223372036854775800}', '{{\"meeting\", \"lunch\"}, {}}')""") pgcnx.query("""INSERT INTO sal_emp VALUES ( 'Carol', '{20000,25000,25000,25000}', '{9223372036854775807,9223372036854775807,9223372036854775807}', '{{\"talk\", \"consult\"}, {\"meeting\"}}')""") wait_key() print print "----------------------" print "-- queries on array attributes" print "----------------------" print print "SELECT name FROM sal_emp WHERE" print " sal_emp.pay_by_quarter[1] <> sal_emp.pay_by_quarter[2]" print print pgcnx.query("""SELECT name FROM sal_emp WHERE sal_emp.pay_by_quarter[1] <> sal_emp.pay_by_quarter[2]""") print print pgcnx.query("""SELECT name FROM sal_emp WHERE sal_emp.pay_by_extra_quarter[1] <> sal_emp.pay_by_extra_quarter[2]""") print print "-- retrieve third quarter pay of all employees" print print "SELECT sal_emp.pay_by_quarter[3] FROM sal_emp" print print pgcnx.query("SELECT sal_emp.pay_by_quarter[3] FROM sal_emp") print print "-- retrieve third quarter extra pay of all employees" print print "SELECT sal_emp.pay_by_extra_quarter[3] FROM sal_emp" print pgcnx.query("SELECT sal_emp.pay_by_extra_quarter[3] FROM sal_emp") print print "-- retrieve first two quarters of extra quarter pay of all employees" print print "SELECT sal_emp.pay_by_extra_quarter[1:2] FROM sal_emp" print print pgcnx.query("SELECT sal_emp.pay_by_extra_quarter[1:2] FROM sal_emp") print print "-- select subarrays" print print "SELECT sal_emp.schedule[1:2][1:1] FROM sal_emp WHERE" print " sal_emp.name = 'Bill'" print pgcnx.query("SELECT sal_emp.schedule[1:2][1:1] FROM sal_emp WHERE " \ "sal_emp.name = 'Bill'") # base cleanup def demo_cleanup(pgcnx): print "-- clean up (you must remove the children first)" print "DROP TABLE sal_emp" print "DROP TABLE capitals" print "DROP TABLE cities;" pgcnx.query("DROP TABLE sal_emp") pgcnx.query("DROP TABLE capitals") pgcnx.query("DROP TABLE cities") # main demo function def demo(pgcnx): inherit_demo(pgcnx) array_demo(pgcnx) demo_cleanup(pgcnx) PyGreSQL-4.0/setup.py010075500017500000000000000116431112713757600134730ustar darcywheel#!/usr/bin/env python # $Id: setup.py,v 1.27 2008/11/21 17:08:17 cito Exp $ """Setup script for PyGreSQL version 4.0 Authors and history: * PyGreSQL written 1997 by D'Arcy J.M. Cain * based on code written 1995 by Pascal Andre * setup script created 2000/04 Mark Alexander * tweaked 2000/05 Jeremy Hylton * win32 support 2001/01 by Gerhard Haering * tweaked 2006/02 and 2008/11 by Christoph Zwerschke Prerequisites to be installed: * Python including devel package (header files and distutils) * PostgreSQL libs and devel packages (header files of client and server) * PostgreSQL pg_config tool (usually included in the devel package) (the Windows installer has it as part of the database server feature) Tested with Python 2.5.2 and PostGreSQL 8.3.5. Older version should work as well, but you will need at least Python 2.3 and PostgreSQL 7.4. Use as follows: python setup.py build # to build the module python setup.py install # to install it You should use MinGW (www.mingw.org) for building on Win32: python setup.py build -c mingw32 install # use MinGW Note that Python newer than version 2.3 is using msvcr71 instead of msvcrt as its common runtime library. So, if you are using MinGW to build PyGreSQL, you should edit the file "%MinGWpath%/lib/gcc/%MinGWversion%/specs" and change the entry that reads -lmsvcrt to -lmsvcr71. See docs.python.org/doc/install/ for more information on using distutils to install Python programs. """ version = "4.0" import sys if not (2, 2) < sys.version_info[:2] < (3, 0): raise Exception("PyGreSQL %s requires a Python 2 version" " newer than 2.2." % version) import os from distutils.core import setup from distutils.extension import Extension def pg_config(s): """Retrieve information about installed version of PostgreSQL.""" f = os.popen("pg_config --%s" % s) d = f.readline().strip() if f.close() is not None: raise Exception("pg_config tool is not available.") if not d: raise Exception("Could not get %s information." % s) return d def mk_include(): """Create a temporary local include directory. The directory will contain a copy of the PostgreSQL server header files, where all features which are not necessary for PyGreSQL are disabled. """ os.mkdir('include') for f in os.listdir(pg_include_dir_server): if not f.endswith('.h'): continue d = open(os.path.join(pg_include_dir_server, f)).read() if f == 'pg_config.h': d += '\n'.join(('', '#undef ENABLE_NLS', '#undef USE_REPL_SNPRINTF', '#undef USE_SSL', '#undef USE_ZLIB', '#undef HAVE_STDINT_H', '#undef HAVE_SYS_TIME_H', '#undef HAVE_UNISTD_H', '#define _CRT_SECURE_NO_WARNINGS 1', '#define _USE_32BIT_TIME_T 1', '')) open(os.path.join('include', f), 'w').write(d) def rm_include(): """Remove the temporary local include directory.""" if os.path.exists('include'): for f in os.listdir('include'): os.remove(os.path.join('include', f)) os.rmdir('include') pg_include_dir = pg_config('includedir') pg_include_dir_server = pg_config('includedir-server') rm_include() mk_include() include_dirs = ['include', pg_include_dir, pg_include_dir_server] pg_libdir = pg_config('libdir') library_dirs = [pg_libdir] libraries=['pq'] if sys.platform == "win32": include_dirs.append(os.path.join(pg_include_dir_server, 'port/win32')) setup( name="PyGreSQL", version=version, description="Python PostgreSQL Interfaces", long_description = ("PyGreSQL is an open-source 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."), keywords="postgresql database api dbapi", author="D'Arcy J. M. Cain", author_email="darcy@PyGreSQL.org", url="http://www.pygresql.org", download_url = "ftp://ftp.pygresql.org/pub/distrib/", platforms = ["any"], license="Python", py_modules=['pg', 'pgdb'], ext_modules=[Extension( '_pg', ['pgmodule.c'], include_dirs = include_dirs, library_dirs = library_dirs, libraries = libraries, extra_compile_args = ['-O2'] )], classifiers=[ "Development Status :: 6 - Mature", "Intended Audience :: Developers", "License :: OSI Approved :: Python Software Foundation License", "Operating System :: OS Independent", "Programming Language :: C", "Programming Language :: Python", "Topic :: Database", "Topic :: Database :: Front-Ends", "Topic :: Software Development :: Libraries :: Python Modules" ] ) rm_include() PyGreSQL-4.0/pgmodule.c010064400017500000000000002473511112713757600137450ustar darcywheel/* * $Id: pgmodule.c,v 1.90 2008/12/03 00:17:15 cito Exp $ * 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, 1998, 1999 by D'Arcy J.M. Cain * (darcy@druid.net) subject to the same terms and conditions as above. * */ /* Note: This should be linked against the same C runtime lib as Python */ #include "postgres.h" #include "libpq-fe.h" #include "libpq/libpq-fs.h" #include "catalog/pg_type.h" /* these will be defined in Python.h again: */ #undef _POSIX_C_SOURCE #undef HAVE_STRERROR #undef snprintf #undef vsnprintf #include static PyObject *Error, *Warning, *InterfaceError, *DatabaseError, *InternalError, *OperationalError, *ProgrammingError, *IntegrityError, *DataError, *NotSupportedError; static const char *PyPgVersion = "4.0"; #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) typedef int Py_ssize_t; #define PY_SSIZE_T_MAX INT_MAX #define PY_SSIZE_T_MIN INT_MIN #endif /* taken from fileobject.c */ #define BUF(v) PyString_AS_STRING((PyStringObject *)(v)) /* default values */ #define MODULE_NAME "pgsql" #define PG_ARRAYSIZE 1 /* flags for object validity checks */ #define CHECK_OPEN 1 #define CHECK_CLOSE 2 #define CHECK_CNX 4 #define CHECK_RESULT 8 #define CHECK_DQL 16 /* query result types */ #define RESULT_EMPTY 1 #define RESULT_DML 2 #define RESULT_DDL 3 #define RESULT_DQL 4 /* flags for move methods */ #define QUERY_MOVEFIRST 1 #define QUERY_MOVELAST 2 #define QUERY_MOVENEXT 3 #define QUERY_MOVEPREV 4 /* moves names for errors */ const char *__movename[5] = {"", "movefirst", "movelast", "movenext", "moveprev"}; #define MAX_BUFFER_SIZE 8192 /* maximum transaction size */ #ifndef NO_DIRECT #define DIRECT_ACCESS 1 /* enables direct access functions */ #endif #ifndef NO_LARGE #define LARGE_OBJECTS 1 /* enables large objects support */ #endif #ifndef NO_DEF_VAR #define DEFAULT_VARS 1 /* enables default variables use */ #endif #ifndef PG_VERSION_NUM #ifdef PQnoPasswordSupplied #define PG_VERSION_NUM 80000 #else #define PG_VERSION_NUM 70400 #endif #endif /* Before 8.0, PQsetdbLogin was not thread-safe with kerberos. */ #if PG_VERSION_NUM >= 80000 || !(defined(KRB4) || defined(KRB5)) #define PQsetdbLoginIsThreadSafe 1 #endif /* --------------------------------------------------------------------- */ /* 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 */ DL_EXPORT(void) init_pg(void); int *get_type_array(PGresult *result, int nfields); static PyObject *decimal = NULL; /* decimal type */ /* --------------------------------------------------------------------- */ /* OBJECTS DECLARATION */ /* pg connection object */ typedef struct { PyObject_HEAD int valid; /* validity flag */ PGconn *cnx; /* PostGres connection handle */ PGresult *last_result; /* last result content */ } pgobject; staticforward PyTypeObject PgType; #define is_pgobject(v) ((v)->ob_type == &PgType) static PyObject * pgobject_New(void) { pgobject *pgobj; if ((pgobj = PyObject_NEW(pgobject, &PgType)) == NULL) return NULL; pgobj->valid = 1; pgobj->last_result = NULL; pgobj->cnx = NULL; return (PyObject *) pgobj; } /* pg query object */ typedef struct { PyObject_HEAD PGresult *last_result; /* last result content */ int result_type; /* type of previous result */ long current_pos; /* current position in last result */ long num_rows; /* number of (affected) rows */ } pgqueryobject; staticforward PyTypeObject PgQueryType; #define is_pgqueryobject(v) ((v)->ob_type == &PgQueryType) /* pg source object */ typedef struct { PyObject_HEAD int valid; /* validity flag */ pgobject *pgcnx; /* parent connection object */ PGresult *last_result; /* last result content */ int result_type; /* result type (DDL/DML/DQL) */ long arraysize; /* array size for fetch method */ int current_row; /* current selected row */ int max_row; /* number of rows in the result */ int num_fields; /* number of fields in each row */ } pgsourceobject; staticforward PyTypeObject PgSourceType; #define is_pgsourceobject(v) ((v)->ob_type == &PgSourceType) #ifdef LARGE_OBJECTS /* pg large object */ typedef struct { PyObject_HEAD pgobject *pgcnx; /* parent connection object */ Oid lo_oid; /* large object oid */ int lo_fd; /* large object fd */ } pglargeobject; staticforward PyTypeObject PglargeType; #define is_pglargeobject(v) ((v)->ob_type == &PglargeType) #endif /* LARGE_OBJECTS */ /* --------------------------------------------------------------------- */ /* INTERNAL FUNCTIONS */ /* prints result (mostly useful for debugging) */ /* Note: This is a simplified version of the Postgres function PQprint(). * PQprint() is not used because handing over a stream from Python to * Postgres can be problematic if they use different libs for streams. * Also, PQprint() is considered obsolete and may be removed sometime. */ static void print_result(FILE *fout, const PGresult *res) { int n = PQnfields(res); if (n > 0) { int i, j; int *fieldMax = NULL; char **fields = NULL; const char **fieldNames; int m = PQntuples(res); if (!(fieldNames = (const char **) calloc(n, sizeof(char *)))) { fprintf(stderr, "out of memory\n"); exit(1); } if (!(fieldMax = (int *) calloc(n, sizeof(int)))) { fprintf(stderr, "out of memory\n"); exit(1); } for (j = 0; j < n; j++) { const char *s = PQfname(res, j); fieldNames[j] = s; fieldMax[j] = s ? strlen(s) : 0; } if (!(fields = (char **) calloc(n * (m + 1), sizeof(char *)))) { fprintf(stderr, "out of memory\n"); exit(1); } for (i = 0; i < m; i++) { for (j = 0; j < n; j++) { const char *val; int len; len = PQgetlength(res, i, j); val = PQgetvalue(res, i, j); if (len >= 1 && val && *val) { if (len > fieldMax[j]) fieldMax[j] = len; if (!(fields[i * n + j] = (char *) malloc(len + 1))) { fprintf(stderr, "out of memory\n"); exit(1); } strcpy(fields[i * n + j], val); } } } for (j = 0; j < n; j++) { const char *s = PQfname(res, j); int len = strlen(s); if (len > fieldMax[j]) fieldMax[j] = len; fprintf(fout, "%-*s", fieldMax[j], s); if (j + 1 < n) fputc('|', fout); } fputc('\n', fout); for (j = 0; j < n; j++) { for (i = fieldMax[j]; i--; fputc('-', fout)); if (j + 1 < n) fputc('+', fout); } fputc('\n', fout); for (i = 0; i < m; i++) { for (j = 0; j < n; j++) { char *s = fields[i * n + j]; fprintf(fout, "%-*s", fieldMax[j], s ? s : ""); if (j + 1 < n) fputc('|', fout); if (s) free(s); } fputc('\n', fout); } free(fields); fprintf(fout, "(%d row%s)\n\n", m, m == 1 ? "" : "s"); free(fieldMax); free((void *) fieldNames); } } /* checks connection validity */ static int check_cnx_obj(pgobject * self) { if (!self->valid) { PyErr_SetString(IntegrityError, "connection has been closed."); return 0; } return 1; } #ifdef LARGE_OBJECTS /* checks large object validity */ static int check_lo_obj(pglargeobject * self, int level) { if (!check_cnx_obj(self->pgcnx)) return 0; if (!self->lo_oid) { PyErr_SetString(IntegrityError, "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 */ /* checks source object validity */ static int check_source_obj(pgsourceobject * self, int level) { if (!self->valid) { PyErr_SetString(IntegrityError, "object has been closed"); return 0; } if ((level & CHECK_RESULT) && self->last_result == NULL) { PyErr_SetString(DatabaseError, "no result."); return 0; } if ((level & CHECK_DQL) && self->result_type != RESULT_DQL) { PyErr_SetString(DatabaseError, "last query did not return tuples."); return 0; } if ((level & CHECK_CNX) && !check_cnx_obj(self->pgcnx)) return 0; return 1; } /* shared functions for converting PG types to Python types */ int * get_type_array(PGresult *result, int nfields) { int *typ; int j; if ((typ = malloc(sizeof(int) * nfields)) == NULL) { PyErr_SetString(PyExc_MemoryError, "memory error in getresult()."); return NULL; } for (j = 0; j < nfields; j++) { switch (PQftype(result, j)) { case INT2OID: case INT4OID: case OIDOID: typ[j] = 1; break; case INT8OID: typ[j] = 2; break; case FLOAT4OID: case FLOAT8OID: typ[j] = 3; break; case NUMERICOID: typ[j] = 4; break; case CASHOID: typ[j] = 5; break; default: typ[j] = 6; break; } } return typ; } /* prototypes for constructors */ static pgsourceobject *pgsource_new(pgobject * pgcnx); /* --------------------------------------------------------------------- */ /* PG SOURCE OBJECT IMPLEMENTATION */ /* constructor (internal use only) */ static pgsourceobject * pgsource_new(pgobject * pgcnx) { pgsourceobject *npgobj; /* allocates new query object */ if ((npgobj = PyObject_NEW(pgsourceobject, &PgSourceType)) == NULL) return NULL; /* initializes internal parameters */ Py_XINCREF(pgcnx); npgobj->pgcnx = pgcnx; npgobj->last_result = NULL; npgobj->valid = 1; npgobj->arraysize = PG_ARRAYSIZE; return npgobj; } /* destructor */ static void pgsource_dealloc(pgsourceobject * self) { if (self->last_result) PQclear(self->last_result); Py_XDECREF(self->pgcnx); PyObject_Del(self); } /* closes object */ static char pgsource_close__doc__[] = "close() -- close query object without deleting it. " "All instances of the query object can no longer be used after this call."; static PyObject * pgsource_close(pgsourceobject * self, PyObject * args) { /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method close() takes no parameter."); return NULL; } /* frees result if necessary and invalidates object */ if (self->last_result) { PQclear(self->last_result); self->result_type = RESULT_EMPTY; self->last_result = NULL; } self->valid = 0; /* return None */ Py_INCREF(Py_None); return Py_None; } /* database query */ static char pgsource_execute__doc__[] = "execute(sql) -- execute a SQL statement (string).\n " "On success, this call returns the number of affected rows, " "or None for DQL (SELECT, ...) statements.\n" "The fetch (fetch(), fetchone() and fetchall()) methods can be used " "to get result rows."; static PyObject * pgsource_execute(pgsourceobject * self, PyObject * args) { char *query; /* checks validity */ if (!check_source_obj(self, CHECK_CNX)) return NULL; /* make sure that the connection object is valid */ if (!self->pgcnx->cnx) return NULL; /* get query args */ if (!PyArg_ParseTuple(args, "s", &query)) { PyErr_SetString(PyExc_TypeError, "execute(sql), with sql (string)."); return NULL; } /* frees previous result */ if (self->last_result) { PQclear(self->last_result); self->last_result = NULL; } self->max_row = 0; self->current_row = 0; self->num_fields = 0; /* gets result */ Py_BEGIN_ALLOW_THREADS self->last_result = PQexec(self->pgcnx->cnx, query); Py_END_ALLOW_THREADS /* checks result validity */ if (!self->last_result) { PyErr_SetString(PyExc_ValueError, PQerrorMessage(self->pgcnx->cnx)); return NULL; } /* checks result status */ switch (PQresultStatus(self->last_result)) { long num_rows; char *temp; /* query succeeded */ case PGRES_TUPLES_OK: /* DQL: returns None (DB-SIG compliant) */ self->result_type = RESULT_DQL; self->max_row = PQntuples(self->last_result); self->num_fields = PQnfields(self->last_result); Py_INCREF(Py_None); return Py_None; case PGRES_COMMAND_OK: /* other requests */ case PGRES_COPY_OUT: case PGRES_COPY_IN: self->result_type = RESULT_DDL; temp = PQcmdTuples(self->last_result); num_rows = -1; if (temp[0]) { self->result_type = RESULT_DML; num_rows = atol(temp); } return PyInt_FromLong(num_rows); /* query failed */ 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(ProgrammingError, PQerrorMessage(self->pgcnx->cnx)); break; default: PyErr_SetString(InternalError, "internal error: " "unknown result status."); break; } /* frees result and returns error */ PQclear(self->last_result); self->last_result = NULL; self->result_type = RESULT_EMPTY; return NULL; } /* gets oid status for last query (valid for INSERTs, 0 for other) */ static char pgsource_oidstatus__doc__[] = "oidstatus() -- return oid of last inserted row (if available)."; static PyObject * pgsource_oidstatus(pgsourceobject * self, PyObject * args) { Oid oid; /* checks validity */ if (!check_source_obj(self, CHECK_RESULT)) return NULL; /* checks args */ if ((args != NULL) && (!PyArg_ParseTuple(args, ""))) { PyErr_SetString(PyExc_TypeError, "method oidstatus() takes no parameters."); return NULL; } /* retrieves oid status */ if ((oid = PQoidValue(self->last_result)) == InvalidOid) { Py_INCREF(Py_None); return Py_None; } return PyInt_FromLong(oid); } /* fetches rows from last result */ static char pgsource_fetch__doc__[] = "fetch(num) -- return the next num rows from the last result in a list. " "If num parameter is omitted arraysize attribute value is used. " "If size equals -1, all rows are fetched."; static PyObject * pgsource_fetch(pgsourceobject * self, PyObject * args) { PyObject *rowtuple, *reslist, *str; int i, j; long size; /* checks validity */ if (!check_source_obj(self, CHECK_RESULT | CHECK_DQL)) return NULL; /* checks args */ size = self->arraysize; if (!PyArg_ParseTuple(args, "|l", &size)) { PyErr_SetString(PyExc_TypeError, "fetch(num), with num (integer, optional)."); return NULL; } /* seeks last line */ /* limit size to be within the amount of data we actually have */ if (size == -1 || (self->max_row - self->current_row) < size) size = self->max_row - self->current_row; /* allocate list for result */ if ((reslist = PyList_New(0)) == NULL) return NULL; /* builds result */ for (i = 0; i < size; ++i) { if ((rowtuple = PyTuple_New(self->num_fields)) == NULL) { Py_DECREF(reslist); return NULL; } for (j = 0; j < self->num_fields; j++) { if (PQgetisnull(self->last_result, self->current_row, j)) { Py_INCREF(Py_None); str = Py_None; } else str = PyString_FromString(PQgetvalue(self->last_result, self->current_row, j)); PyTuple_SET_ITEM(rowtuple, j, str); } PyList_Append(reslist, rowtuple); Py_DECREF(rowtuple); self->current_row++; } return reslist; } /* changes current row (internal wrapper for all "move" methods) */ static PyObject * pgsource_move(pgsourceobject * self, PyObject * args, int move) { /* checks validity */ if (!check_source_obj(self, CHECK_RESULT | CHECK_DQL)) return NULL; /* checks args */ if (!PyArg_ParseTuple(args, "")) { char errbuf[256]; PyOS_snprintf(errbuf, sizeof(errbuf), "method %s() takes no parameter.", __movename[move]); PyErr_SetString(PyExc_TypeError, errbuf); return NULL; } /* changes the current row */ switch (move) { case QUERY_MOVEFIRST: self->current_row = 0; break; case QUERY_MOVELAST: self->current_row = self->max_row - 1; break; case QUERY_MOVENEXT: if (self->current_row != self->max_row) self->current_row++; break; case QUERY_MOVEPREV: if (self->current_row > 0) self->current_row--; break; } Py_INCREF(Py_None); return Py_None; } /* move to first result row */ static char pgsource_movefirst__doc__[] = "movefirst() -- move to first result row."; static PyObject * pgsource_movefirst(pgsourceobject * self, PyObject * args) { return pgsource_move(self, args, QUERY_MOVEFIRST); } /* move to last result row */ static char pgsource_movelast__doc__[] = "movelast() -- move to last valid result row."; static PyObject * pgsource_movelast(pgsourceobject * self, PyObject * args) { return pgsource_move(self, args, QUERY_MOVELAST); } /* move to next result row */ static char pgsource_movenext__doc__[] = "movenext() -- move to next result row."; static PyObject * pgsource_movenext(pgsourceobject * self, PyObject * args) { return pgsource_move(self, args, QUERY_MOVENEXT); } /* move to previous result row */ static char pgsource_moveprev__doc__[] = "moveprev() -- move to previous result row."; static PyObject * pgsource_moveprev(pgsourceobject * self, PyObject * args) { return pgsource_move(self, args, QUERY_MOVEPREV); } /* finds field number from string/integer (internal use only) */ static int pgsource_fieldindex(pgsourceobject * self, PyObject * param, const char *usage) { int num; /* checks validity */ if (!check_source_obj(self, CHECK_RESULT | CHECK_DQL)) return -1; /* gets field number */ if (PyString_Check(param)) num = PQfnumber(self->last_result, PyString_AsString(param)); else if (PyInt_Check(param)) num = PyInt_AsLong(param); else { PyErr_SetString(PyExc_TypeError, usage); return -1; } /* checks field validity */ if (num < 0 || num >= self->num_fields) { PyErr_SetString(PyExc_ValueError, "Unknown field."); return -1; } return num; } /* builds field information from position (internal use only) */ static PyObject * pgsource_buildinfo(pgsourceobject * self, int num) { PyObject *result; /* allocates tuple */ result = PyTuple_New(3); if (!result) return NULL; /* affects field information */ PyTuple_SET_ITEM(result, 0, PyInt_FromLong(num)); PyTuple_SET_ITEM(result, 1, PyString_FromString(PQfname(self->last_result, num))); PyTuple_SET_ITEM(result, 2, PyInt_FromLong(PQftype(self->last_result, num))); return result; } /* lists fields info */ static char pgsource_listinfo__doc__[] = "listinfo() -- return information for all fields " "(position, name, type oid)."; static PyObject * pgsource_listinfo(pgsourceobject * self, PyObject * args) { int i; PyObject *result, *info; /* checks validity */ if (!check_source_obj(self, CHECK_RESULT | CHECK_DQL)) return NULL; /* gets args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method listinfo() takes no parameter."); return NULL; } /* builds result */ if ((result = PyTuple_New(self->num_fields)) == NULL) return NULL; for (i = 0; i < self->num_fields; i++) { info = pgsource_buildinfo(self, i); if (!info) { Py_DECREF(result); return NULL; } PyTuple_SET_ITEM(result, i, info); } /* returns result */ return result; }; /* list fields information for last result */ static char pgsource_fieldinfo__doc__[] = "fieldinfo(string|integer) -- return specified field information " "(position, name, type oid)."; static PyObject * pgsource_fieldinfo(pgsourceobject * self, PyObject * args) { static const char short_usage[] = "fieldinfo(desc), with desc (string|integer)."; int num; PyObject *param; /* gets args */ if (!PyArg_ParseTuple(args, "O", ¶m)) { PyErr_SetString(PyExc_TypeError, short_usage); return NULL; } /* checks args and validity */ if ((num = pgsource_fieldindex(self, param, short_usage)) == -1) return NULL; /* returns result */ return pgsource_buildinfo(self, num); }; /* retrieve field value */ static char pgsource_field__doc__[] = "field(string|integer) -- return specified field value."; static PyObject * pgsource_field(pgsourceobject * self, PyObject * args) { static const char short_usage[] = "field(desc), with desc (string|integer)."; int num; PyObject *param; /* gets args */ if (!PyArg_ParseTuple(args, "O", ¶m)) { PyErr_SetString(PyExc_TypeError, short_usage); return NULL; } /* checks args and validity */ if ((num = pgsource_fieldindex(self, param, short_usage)) == -1) return NULL; return PyString_FromString(PQgetvalue(self->last_result, self->current_row, num)); } /* query object methods */ static PyMethodDef pgsource_methods[] = { {"close", (PyCFunction) pgsource_close, METH_VARARGS, pgsource_close__doc__}, {"execute", (PyCFunction) pgsource_execute, METH_VARARGS, pgsource_execute__doc__}, {"oidstatus", (PyCFunction) pgsource_oidstatus, METH_VARARGS, pgsource_oidstatus__doc__}, {"fetch", (PyCFunction) pgsource_fetch, METH_VARARGS, pgsource_fetch__doc__}, {"movefirst", (PyCFunction) pgsource_movefirst, METH_VARARGS, pgsource_movefirst__doc__}, {"movelast", (PyCFunction) pgsource_movelast, METH_VARARGS, pgsource_movelast__doc__}, {"movenext", (PyCFunction) pgsource_movenext, METH_VARARGS, pgsource_movenext__doc__}, {"moveprev", (PyCFunction) pgsource_moveprev, METH_VARARGS, pgsource_moveprev__doc__}, {"field", (PyCFunction) pgsource_field, METH_VARARGS, pgsource_field__doc__}, {"fieldinfo", (PyCFunction) pgsource_fieldinfo, METH_VARARGS, pgsource_fieldinfo__doc__}, {"listinfo", (PyCFunction) pgsource_listinfo, METH_VARARGS, pgsource_listinfo__doc__}, {NULL, NULL} }; /* gets query object attributes */ static PyObject * pgsource_getattr(pgsourceobject * self, char *name) { /* pg connection object */ if (!strcmp(name, "pgcnx")) { if (check_source_obj(self, 0)) { Py_INCREF(self->pgcnx); return (PyObject *) (self->pgcnx); } Py_INCREF(Py_None); return Py_None; } /* arraysize */ if (!strcmp(name, "arraysize")) return PyInt_FromLong(self->arraysize); /* resulttype */ if (!strcmp(name, "resulttype")) return PyInt_FromLong(self->result_type); /* ntuples */ if (!strcmp(name, "ntuples")) return PyInt_FromLong(self->max_row); /* nfields */ if (!strcmp(name, "nfields")) return PyInt_FromLong(self->num_fields); /* attributes list */ if (!strcmp(name, "__members__")) { PyObject *list = PyList_New(5); PyList_SET_ITEM(list, 0, PyString_FromString("pgcnx")); PyList_SET_ITEM(list, 1, PyString_FromString("arraysize")); PyList_SET_ITEM(list, 2, PyString_FromString("resulttype")); PyList_SET_ITEM(list, 3, PyString_FromString("ntuples")); PyList_SET_ITEM(list, 4, PyString_FromString("nfields")); return list; } /* module name */ if (!strcmp(name, "__module__")) return PyString_FromString(MODULE_NAME); /* class name */ if (!strcmp(name, "__class__")) return PyString_FromString("pgsource"); /* seeks name in methods (fallback) */ return Py_FindMethod(pgsource_methods, (PyObject *) self, name); } /* sets query object attributes */ static int pgsource_setattr(pgsourceobject * self, char *name, PyObject * v) { /* arraysize */ if (!strcmp(name, "arraysize")) { if (!PyInt_Check(v)) { PyErr_SetString(PyExc_TypeError, "arraysize must be integer."); return -1; } self->arraysize = PyInt_AsLong(v); return 0; } /* unknown attribute */ PyErr_SetString(PyExc_TypeError, "not a writable attribute."); return -1; } /* prints query object in human readable format */ static int pgsource_print(pgsourceobject * self, FILE *fp, int flags) { switch (self->result_type) { case RESULT_DQL: print_result(fp, self->last_result); break; case RESULT_DDL: case RESULT_DML: fputs(PQcmdStatus(self->last_result), fp); break; case RESULT_EMPTY: default: fputs("Empty PostgreSQL source object.", fp); break; } return 0; } /* query type definition */ staticforward PyTypeObject PgSourceType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "pgsourceobject", /* tp_name */ sizeof(pgsourceobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor) pgsource_dealloc, /* tp_dealloc */ (printfunc) pgsource_print, /* tp_print */ (getattrfunc) pgsource_getattr, /* tp_getattr */ (setattrfunc) pgsource_setattr, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ }; /* --------------------------------------------------------------------- */ /* PG "LARGE" OBJECT IMPLEMENTATION */ #ifdef LARGE_OBJECTS /* constructor (internal use only) */ static pglargeobject * pglarge_new(pgobject * pgcnx, Oid oid) { pglargeobject *npglo; if ((npglo = PyObject_NEW(pglargeobject, &PglargeType)) == NULL) return NULL; Py_XINCREF(pgcnx); npglo->pgcnx = pgcnx; npglo->lo_fd = -1; npglo->lo_oid = oid; return npglo; } /* destructor */ static void pglarge_dealloc(pglargeobject * self) { if (self->lo_fd >= 0 && check_cnx_obj(self->pgcnx)) lo_close(self->pgcnx->cnx, self->lo_fd); Py_XDECREF(self->pgcnx); PyObject_Del(self); } /* opens large object */ static char pglarge_open__doc__[] = "open(mode) -- open access to large object with specified mode " "(INV_READ, INV_WRITE constants defined by module)."; static PyObject * pglarge_open(pglargeobject * self, PyObject * args) { int mode, fd; /* check validity */ if (!check_lo_obj(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 char pglarge_close__doc__[] = "close() -- close access to large object data."; static PyObject * pglarge_close(pglargeobject * self, PyObject * args) { /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method close() takes no parameters."); return NULL; } /* checks validity */ if (!check_lo_obj(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 char pglarge_read__doc__[] = "read(integer) -- read from large object to sized string. " "Object must be opened in read mode before calling this method."; static PyObject * pglarge_read(pglargeobject * self, PyObject * args) { int size; PyObject *buffer; /* checks validity */ if (!check_lo_obj(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 char pglarge_write__doc__[] = "write(string) -- write sized string to large object. " "Object must be opened in read mode before calling this method."; static PyObject * pglarge_write(pglargeobject * self, PyObject * args) { char *buffer; int size, bufsize; /* checks validity */ if (!check_lo_obj(self, CHECK_OPEN)) return NULL; /* gets arguments */ if (!PyArg_ParseTuple(args, "s#", &buffer, &bufsize)) { 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, bufsize)) < bufsize) { 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 char pglarge_seek__doc__[] = "seek(off, whence) -- move to specified position. Object must be opened " "before calling this method. whence can be SEEK_SET, SEEK_CUR or SEEK_END, " "constants defined by module."; static PyObject * pglarge_lseek(pglargeobject * self, PyObject * args) { /* offset and whence are initialized to keep compiler happy */ int ret, offset = 0, whence = 0; /* checks validity */ if (!check_lo_obj(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 char pglarge_size__doc__[] = "size() -- return large object size. " "Object must be opened before calling this method."; static PyObject * pglarge_size(pglargeobject * self, PyObject * args) { int start, end; /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method size() takes no parameters."); return NULL; } /* checks validity */ if (!check_lo_obj(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 char pglarge_tell__doc__[] = "tell() -- give current position in large object. " "Object must be opened before calling this method."; static PyObject * pglarge_tell(pglargeobject * self, PyObject * args) { int start; /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method tell() takes no parameters."); return NULL; } /* checks validity */ if (!check_lo_obj(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 char pglarge_export__doc__[] = "export(string) -- export large object data to specified file. " "Object must be closed when calling this method."; static PyObject * pglarge_export(pglargeobject * self, PyObject * args) { char *name; /* checks validity */ if (!check_lo_obj(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 char pglarge_unlink__doc__[] = "unlink() -- destroy large object. " "Object must be closed when calling this method."; static PyObject * pglarge_unlink(pglargeobject * self, PyObject * args) { /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method unlink() takes no parameters."); return NULL; } /* checks validity */ if (!check_lo_obj(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, METH_VARARGS, pglarge_open__doc__}, {"close", (PyCFunction) pglarge_close, METH_VARARGS, pglarge_close__doc__}, {"read", (PyCFunction) pglarge_read, METH_VARARGS, pglarge_read__doc__}, {"write", (PyCFunction) pglarge_write, METH_VARARGS, pglarge_write__doc__}, {"seek", (PyCFunction) pglarge_lseek, METH_VARARGS, pglarge_seek__doc__}, {"size", (PyCFunction) pglarge_size, METH_VARARGS, pglarge_size__doc__}, {"tell", (PyCFunction) pglarge_tell, METH_VARARGS, pglarge_tell__doc__}, {"export",(PyCFunction) pglarge_export,METH_VARARGS,pglarge_export__doc__}, {"unlink",(PyCFunction) pglarge_unlink,METH_VARARGS,pglarge_unlink__doc__}, {NULL, NULL} }; /* 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_obj(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_obj(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_SET_ITEM(list, 0, PyString_FromString("oid")); PyList_SET_ITEM(list, 1, PyString_FromString("pgcnx")); PyList_SET_ITEM(list, 2, PyString_FromString("error")); } return list; } /* module name */ if (!strcmp(name, "__module__")) return PyString_FromString(MODULE_NAME); /* class name */ if (!strcmp(name, "__class__")) return PyString_FromString("pglarge"); /* seeks name in methods (fallback) */ return Py_FindMethod(pglarge_methods, (PyObject *) self, name); } /* prints query object in human readable format */ static int pglarge_print(pglargeobject * self, FILE *fp, int flags) { char print_buffer[128]; PyOS_snprintf(print_buffer, sizeof(print_buffer), self->lo_fd >= 0 ? "Opened large object, oid %ld" : "Closed large object, oid %ld", (long) self->lo_oid); fputs(print_buffer, fp); return 0; } /* 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 */ (printfunc) pglarge_print, /* 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 QUERY OBJECT IMPLEMENTATION */ /* connects to a database */ static char connect__doc__[] = "connect(dbname, host, port, opt, tty) -- connect to a PostgreSQL database " "using specified parameters (optionals, keywords aware)."; 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 With the right compiler warnings, this * will issue a diagnostic. There is really no way around it. If I * don't declare kwlist as const char *kwlist[] then it complains when * I try to assign all those constant strings to it. */ if (!PyArg_ParseTupleAndKeywords(args, dict, "|zzizzzz", (char **) kwlist, &pgdbname, &pghost, &pgport, &pgopt, &pgtty, &pguser, &pgpasswd)) return NULL; #ifdef DEFAULT_VARS /* handles defaults variables (for uninitialised 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 = (pgobject *) pgobject_New()) == NULL) return NULL; if (pgport != -1) { memset(port_buffer, 0, sizeof(port_buffer)); sprintf(port_buffer, "%d", pgport); } #ifdef PQsetdbLoginIsThreadSafe Py_BEGIN_ALLOW_THREADS #endif npgobj->cnx = PQsetdbLogin(pghost, pgport == -1 ? NULL : port_buffer, pgopt, pgtty, pgdbname, pguser, pgpasswd); #ifdef PQsetdbLoginIsThreadSafe Py_END_ALLOW_THREADS #endif if (PQstatus(npgobj->cnx) == CONNECTION_BAD) { PyErr_SetString(InternalError, PQerrorMessage(npgobj->cnx)); Py_XDECREF(npgobj); return NULL; } return (PyObject *) npgobj; } /* pgobject methods */ /* destructor */ static void pg_dealloc(pgobject * self) { if (self->cnx) { Py_BEGIN_ALLOW_THREADS PQfinish(self->cnx); Py_END_ALLOW_THREADS } PyObject_Del(self); } /* close without deleting */ static char pg_close__doc__[] = "close() -- close connection. All instances of the connection object and " "derived objects (queries and large objects) can no longer be used after " "this call."; static PyObject * pg_close(pgobject * self, PyObject * args) { /* gets args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "close()."); return NULL; } /* connection object cannot already be closed */ if (!self->cnx) { PyErr_SetString(InternalError, "Connection already closed"); return NULL; } Py_BEGIN_ALLOW_THREADS PQfinish(self->cnx); Py_END_ALLOW_THREADS self->cnx = NULL; Py_INCREF(Py_None); return Py_None; } static void pgquery_dealloc(pgqueryobject * self) { if (self->last_result) PQclear(self->last_result); PyObject_Del(self); } /* resets connection */ static char pg_reset__doc__[] = "reset() -- reset connection with current parameters. All derived queries " "and large objects derived from this connection will not be usable after " "this call."; 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_TypeError, "method reset() takes no parameters."); return NULL; } /* resets the connection */ PQreset(self->cnx); Py_INCREF(Py_None); return Py_None; } /* cancels current command */ static char pg_cancel__doc__[] = "cancel() -- abandon processing of the current command."; static PyObject * pg_cancel(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_TypeError, "method cancel() takes no parameters."); return NULL; } /* request that the server abandon processing of the current command */ return PyInt_FromLong((long) PQrequestCancel(self->cnx)); } /* get connection socket */ static char pg_fileno__doc__[] = "fileno() -- return database connection socket file handle."; 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_TypeError, "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 } /* get number of rows */ static char pgquery_ntuples__doc__[] = "ntuples() -- returns number of tuples returned by query."; static PyObject * pgquery_ntuples(pgqueryobject * self, PyObject * args) { /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method ntuples() takes no parameters."); return NULL; } return PyInt_FromLong((long) PQntuples(self->last_result)); } /* list fields names from query result */ static char pgquery_listfields__doc__[] = "listfields() -- Lists field names from result."; static PyObject * pgquery_listfields(pgqueryobject * self, PyObject * args) { int i, n; char *name; PyObject *fieldstuple, *str; /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "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_SET_ITEM(fieldstuple, i, str); } return fieldstuple; } /* get field name from last result */ static char pgquery_fieldname__doc__[] = "fieldname() -- returns name of field from result from its position."; static PyObject * pgquery_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 char pgquery_fieldnum__doc__[] = "fieldnum() -- returns position in query for field from its name."; static PyObject * pgquery_fieldnum(pgqueryobject * self, PyObject * args) { int num; char *name; /* 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 char pgquery_getresult__doc__[] = "getresult() -- Gets the result of a query. The result is returned " "as a list of rows, each one a list of fields in the order returned " "by the server."; static PyObject * pgquery_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_TypeError, "method getresult() takes no parameters."); return NULL; } /* stores result in tuple */ m = PQntuples(self->last_result); n = PQnfields(self->last_result); reslist = PyList_New(m); typ = get_type_array(self->last_result, n); for (i = 0; i < m; i++) { if ((rowtuple = PyTuple_New(n)) == NULL) { Py_DECREF(reslist); reslist = NULL; goto exit; } for (j = 0; j < n; j++) { int k; char *s = PQgetvalue(self->last_result, i, j); char cashbuf[64]; PyObject *tmp_obj; if (PQgetisnull(self->last_result, i, j)) { Py_INCREF(Py_None); val = Py_None; } else switch (typ[j]) { case 1: val = PyInt_FromString(s, NULL, 10); break; case 2: val = PyLong_FromString(s, NULL, 10); break; case 3: tmp_obj = PyString_FromString(s); val = PyFloat_FromString(tmp_obj, NULL); Py_DECREF(tmp_obj); break; case 5: for (k = 0; *s && k < sizeof(cashbuf) / sizeof(cashbuf[0]) - 1; s++) { if (isdigit(*s) || *s == '.') cashbuf[k++] = *s; else if (*s == '(' || *s == '-') cashbuf[k++] = '-'; } cashbuf[k] = 0; s = cashbuf; case 4: if (decimal) { tmp_obj = Py_BuildValue("(s)", s); val = PyEval_CallObject(decimal, tmp_obj); } else { tmp_obj = PyString_FromString(s); val = PyFloat_FromString(tmp_obj, NULL); } Py_DECREF(tmp_obj); break; default: val = PyString_FromString(s); break; } if (val == NULL) { Py_DECREF(reslist); Py_DECREF(rowtuple); reslist = NULL; goto exit; } PyTuple_SET_ITEM(rowtuple, j, val); } PyList_SET_ITEM(reslist, i, rowtuple); } exit: free(typ); /* returns list */ return reslist; } /* retrieves last result as a list of dictionaries*/ static char pgquery_dictresult__doc__[] = "dictresult() -- Gets the result of a query. The result is returned " "as a list of rows, each one a dictionary with the field names used " "as the labels."; static PyObject * pgquery_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_TypeError, "method getresult() takes no parameters."); return NULL; } /* stores result in list */ m = PQntuples(self->last_result); n = PQnfields(self->last_result); reslist = PyList_New(m); typ = get_type_array(self->last_result, n); for (i = 0; i < m; i++) { if ((dict = PyDict_New()) == NULL) { Py_DECREF(reslist); reslist = NULL; goto exit; } for (j = 0; j < n; j++) { int k; char *s = PQgetvalue(self->last_result, i, j); char cashbuf[64]; PyObject *tmp_obj; if (PQgetisnull(self->last_result, i, j)) { Py_INCREF(Py_None); val = Py_None; } else switch (typ[j]) { case 1: val = PyInt_FromString(s, NULL, 10); break; case 2: val = PyLong_FromString(s, NULL, 10); break; case 3: tmp_obj = PyString_FromString(s); val = PyFloat_FromString(tmp_obj, NULL); Py_DECREF(tmp_obj); break; case 5: for (k = 0; *s && k < sizeof(cashbuf) / sizeof(cashbuf[0]) - 1; s++) { if (isdigit(*s) || *s == '.') cashbuf[k++] = *s; else if (*s == '(' || *s == '-') cashbuf[k++] = '-'; } cashbuf[k] = 0; s = cashbuf; case 4: if (decimal) { tmp_obj = Py_BuildValue("(s)", s); val = PyEval_CallObject(decimal, tmp_obj); } else { tmp_obj = PyString_FromString(s); val = PyFloat_FromString(tmp_obj, NULL); } Py_DECREF(tmp_obj); break; default: val = PyString_FromString(s); break; } if (val == NULL) { Py_DECREF(dict); Py_DECREF(reslist); reslist = NULL; goto exit; } PyDict_SetItemString(dict, PQfname(self->last_result, j), val); Py_DECREF(val); } PyList_SET_ITEM(reslist, i, dict); } exit: free(typ); /* returns list */ return reslist; } /* gets asynchronous notify */ static char pg_getnotify__doc__[] = "getnotify() -- get database notify for this connection."; static PyObject * pg_getnotify(pgobject * self, PyObject * args) { PGnotify *notify; if (!self->cnx) { PyErr_SetString(PyExc_TypeError, "Connection is not valid."); return NULL; } /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method getnotify() takes no parameters."); return NULL; } /* checks for NOTIFY messages */ PQconsumeInput(self->cnx); if ((notify = PQnotifies(self->cnx)) == NULL) { Py_INCREF(Py_None); return Py_None; } else { PyObject *notify_result, *temp; if ((notify_result = PyTuple_New(2)) == NULL || (temp = PyString_FromString(notify->relname)) == NULL) { return NULL; } PyTuple_SET_ITEM(notify_result, 0, temp); if ((temp = PyInt_FromLong(notify->be_pid)) == NULL) { Py_DECREF(notify_result); return NULL; } PyTuple_SET_ITEM(notify_result, 1, temp); PQfreemem(notify); return notify_result; } } /* source creation */ static char pg_source__doc__[] = "source() -- creates a new source object for this connection"; static PyObject * pg_source(pgobject * self, PyObject * args) { /* checks validity */ if (!check_cnx_obj(self)) return NULL; /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method source() takes no parameter."); return NULL; } /* allocate new pg query object */ return (PyObject *) pgsource_new(self); } /* database query */ static char pg_query__doc__[] = "query(sql) -- creates a new query object for this connection," " using sql (string) request."; 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; } /* frees previous result */ if (self->last_result) { PQclear(self->last_result); self->last_result = NULL; } /* gets result */ Py_BEGIN_ALLOW_THREADS result = PQexec(self->cnx, query); Py_END_ALLOW_THREADS /* checks result validity */ if (!result) { PyErr_SetString(PyExc_ValueError, PQerrorMessage(self->cnx)); return NULL; } /* checks result status */ if ((status = PQresultStatus(result)) != PGRES_TUPLES_OK) { 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(ProgrammingError, PQerrorMessage(self->cnx)); break; case PGRES_COMMAND_OK: { /* INSERT, UPDATE, DELETE */ Oid oid = PQoidValue(result); if (oid == InvalidOid) /* not a single insert */ { char *ret = PQcmdTuples(result); PQclear(result); if (ret[0]) /* return number of rows affected */ { return PyString_FromString(ret); } Py_INCREF(Py_None); return Py_None; } /* for a single insert, return the oid */ PQclear(result); return PyInt_FromLong(oid); } case PGRES_COPY_OUT: /* no data will be received */ case PGRES_COPY_IN: PQclear(result); Py_INCREF(Py_None); return Py_None; default: PyErr_SetString(InternalError, "internal error: " "unknown result status."); break; } PQclear(result); 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 static char pg_putline__doc__[] = "putline() -- sends a line directly to the backend"; /* 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 */ if (PQputline(self->cnx, line)) { PyErr_SetString(PyExc_IOError, PQerrorMessage(self->cnx)); return NULL; } Py_INCREF(Py_None); return Py_None; } /* direct access function : getline */ static char pg_getline__doc__[] = "getline() -- gets a line directly from the backend."; static PyObject * pg_getline(pgobject * self, PyObject * args) { char line[MAX_BUFFER_SIZE]; PyObject *str = NULL; /* GCC */ if (!self->cnx) { PyErr_SetString(PyExc_TypeError, "Connection is not valid."); return NULL; } /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "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 char pg_endcopy__doc__[] = "endcopy() -- synchronizes client and server"; 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_TypeError, "method endcopy() takes no parameters."); return NULL; } /* ends direct copy */ if (PQendcopy(self->cnx)) { PyErr_SetString(PyExc_IOError, PQerrorMessage(self->cnx)); return NULL; } Py_INCREF(Py_None); return Py_None; } #endif /* DIRECT_ACCESS */ static PyObject * pgquery_print(pgqueryobject * self, FILE *fp, int flags) { print_result(fp, self->last_result); return 0; } static PyObject * pgquery_repr(pgqueryobject * self) { return PyString_FromString(""); } /* insert table */ static char pg_inserttable__doc__[] = "inserttable(string, list) -- insert list in table. The fields in the " "list must be in the same order as in the table."; static PyObject * pg_inserttable(pgobject * self, PyObject * args) { PGresult *result; char *table, *buffer, *bufpt; size_t bufsiz; PyObject *list, *sublist, *item; PyObject *(*getitem) (PyObject *, Py_ssize_t); PyObject *(*getsubitem) (PyObject *, Py_ssize_t); int i, j, m, n; 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, "inserttable(table, content), with table (string) " "and content (list)."); return NULL; } /* checks list type */ if (PyTuple_Check(list)) { m = PyTuple_Size(list); getitem = PyTuple_GetItem; } else if (PyList_Check(list)) { m = PyList_Size(list); getitem = PyList_GetItem; } else { PyErr_SetString(PyExc_TypeError, "second arg must be some kind of array."); 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); Py_BEGIN_ALLOW_THREADS result = PQexec(self->cnx, buffer); Py_END_ALLOW_THREADS if (!result) { free(buffer); PyErr_SetString(PyExc_ValueError, PQerrorMessage(self->cnx)); return NULL; } PQclear(result); n = 0; /* not strictly necessary but avoids warning */ /* feed table */ for (i = 0; i < m; i++) { sublist = getitem(list, i); if (PyTuple_Check(sublist)) { j = PyTuple_Size(sublist); getsubitem = PyTuple_GetItem; } else if (PyList_Check(sublist)) { j = PyList_Size(sublist); getsubitem = PyList_GetItem; } else { PyErr_SetString(PyExc_TypeError, "second arg must contain some kind of arrays."); return NULL; } if (i) { if (j != n) { free(buffer); PyErr_SetString(PyExc_TypeError, "arrays contained in second arg must have same size."); return NULL; } } else { n = j; /* never used before this assignment */ } /* builds insert line */ bufpt = buffer; bufsiz = MAX_BUFFER_SIZE - 1; for (j = 0; j < n; j++) { if (j) { *bufpt++ = '\t'; --bufsiz; } item = getsubitem(sublist, j); /* convert item to string and append to buffer */ if (item == Py_None) { if (bufsiz > 2) { *bufpt++ = '\\'; *bufpt++ = 'N'; bufsiz -= 2; } else bufsiz = 0; } else if (PyString_Check(item)) { const char* t = PyString_AS_STRING(item); while (*t && bufsiz) { if (*t == '\\' || *t == '\t' || *t == '\n') { *bufpt++ = '\\'; --bufsiz; if (!bufsiz) break; } *bufpt++ = *t++; --bufsiz; } } else if (PyInt_Check(item) || PyLong_Check(item)) { PyObject* s = PyObject_Str(item); const char* t = PyString_AsString(s); while (*t && bufsiz) { *bufpt++ = *t++; --bufsiz; } Py_DECREF(s); } else { PyObject* s = PyObject_Repr(item); const char* t = PyString_AsString(s); while (*t && bufsiz) { if (*t == '\\' || *t == '\t' || *t == '\n') { *bufpt++ = '\\'; --bufsiz; if (!bufsiz) break; } *bufpt++ = *t++; --bufsiz; } Py_DECREF(s); } if (bufsiz <= 0) { free(buffer); PyErr_SetString(PyExc_MemoryError, "insert buffer overflow."); return NULL; } } *bufpt++ = '\n'; *bufpt = '\0'; /* sends data */ if (PQputline(self->cnx, buffer)) { PyErr_SetString(PyExc_IOError, PQerrorMessage(self->cnx)); PQendcopy(self->cnx); free(buffer); return NULL; } } /* ends query */ if (PQputline(self->cnx, "\\.\n")) { PyErr_SetString(PyExc_IOError, PQerrorMessage(self->cnx)); PQendcopy(self->cnx); free(buffer); return NULL; } if (PQendcopy(self->cnx)) { PyErr_SetString(PyExc_IOError, PQerrorMessage(self->cnx)); free(buffer); return NULL; } free(buffer); /* no error : returns nothing */ Py_INCREF(Py_None); return Py_None; } /* get transaction state */ static char pg_transaction__doc__[] = "Returns the current transaction status."; static PyObject * pg_transaction(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_TypeError, "method transaction() takes no parameters."); return NULL; } return PyInt_FromLong(PQtransactionStatus(self->cnx)); } /* get parameter setting */ static char pg_parameter__doc__[] = "Looks up a current parameter setting."; static PyObject * pg_parameter(pgobject * self, PyObject * args) { const char *name; if (!self->cnx) { PyErr_SetString(PyExc_TypeError, "Connection is not valid."); return NULL; } /* get query args */ if (!PyArg_ParseTuple(args, "s", &name)) { PyErr_SetString(PyExc_TypeError, "parameter(name), with name (string)."); return NULL; } name = PQparameterStatus(self->cnx, name); if (name) return PyString_FromString(name); /* unknown parameter, return None */ Py_INCREF(Py_None); return Py_None; } /* escape string */ static char pg_escape_string__doc__[] = "pg_escape_string(str) -- escape a string for use within SQL."; static PyObject * pg_escape_string(pgobject *self, PyObject *args) { char *from; /* our string argument */ char *to=NULL; /* the result */ int from_length; /* length of string */ int to_length; /* length of result */ PyObject *ret; /* string object to return */ if (!PyArg_ParseTuple(args, "s#", &from, &from_length)) return NULL; to_length = 2*from_length + 1; if (to_length < from_length) { /* overflow */ to_length = from_length; from_length = (from_length - 1)/2; } to = (char *)malloc(to_length); to_length = (int)PQescapeStringConn(self->cnx, to, from, (size_t)from_length, NULL); ret = Py_BuildValue("s#", to, to_length); if (to) free(to); if (!ret) /* pass on exception */ return NULL; return ret; } /* escape bytea */ static char pg_escape_bytea__doc__[] = "pg_escape_bytea(data) -- escape binary data for use within SQL as type bytea."; static PyObject * pg_escape_bytea(pgobject *self, PyObject *args) { unsigned char *from; /* our string argument */ unsigned char *to; /* the result */ int from_length; /* length of string */ size_t to_length; /* length of result */ PyObject *ret; /* string object to return */ if (!PyArg_ParseTuple(args, "s#", &from, &from_length)) return NULL; to = PQescapeByteaConn(self->cnx, from, (int)from_length, &to_length); ret = Py_BuildValue("s", to); if (to) PQfreemem((void *)to); if (!ret) /* pass on exception */ return NULL; return ret; } #ifdef LARGE_OBJECTS /* creates large object */ static char pg_locreate__doc__[] = "locreate() -- creates a new large object in the database."; static PyObject * pg_locreate(pgobject * self, PyObject * args) { int mode; Oid lo_oid; /* checks validity */ if (!check_cnx_obj(self)) return NULL; /* gets arguments */ if (!PyArg_ParseTuple(args, "i", &mode)) { PyErr_SetString(PyExc_TypeError, "locreate(mode), with mode (integer)."); return NULL; } /* creates large object */ lo_oid = lo_creat(self->cnx, mode); if (lo_oid == 0) { PyErr_SetString(OperationalError, "can't create large object."); return NULL; } return (PyObject *) pglarge_new(self, lo_oid); } /* init from already known oid */ static char pg_getlo__doc__[] = "getlo(long) -- create a large object instance for the specified oid."; static PyObject * pg_getlo(pgobject * self, PyObject * args) { int lo_oid; /* checks validity */ if (!check_cnx_obj(self)) return NULL; /* gets arguments */ if (!PyArg_ParseTuple(args, "i", &lo_oid)) { PyErr_SetString(PyExc_TypeError, "getlo(oid), with oid (integer)."); return NULL; } if (!lo_oid) { PyErr_SetString(PyExc_ValueError, "the object oid can't be null."); return NULL; } /* creates object */ return (PyObject *) pglarge_new(self, lo_oid); } /* import unix file */ static char pg_loimport__doc__[] = "loimport(string) -- create a new large object from specified file."; static PyObject * pg_loimport(pgobject * self, PyObject * args) { char *name; Oid lo_oid; /* checks validity */ if (!check_cnx_obj(self)) return NULL; /* gets arguments */ if (!PyArg_ParseTuple(args, "s", &name)) { PyErr_SetString(PyExc_TypeError, "loimport(name), with name (string)."); return NULL; } /* imports file and checks result */ lo_oid = lo_import(self->cnx, name); if (lo_oid == 0) { PyErr_SetString(OperationalError, "can't create large object."); return NULL; } return (PyObject *) pglarge_new(self, lo_oid); } #endif /* LARGE_OBJECTS */ /* connection object methods */ static struct PyMethodDef pgobj_methods[] = { {"source", (PyCFunction) pg_source, METH_VARARGS, pg_source__doc__}, {"query", (PyCFunction) pg_query, METH_VARARGS, pg_query__doc__}, {"reset", (PyCFunction) pg_reset, METH_VARARGS, pg_reset__doc__}, {"cancel", (PyCFunction) pg_cancel, METH_VARARGS, pg_cancel__doc__}, {"close", (PyCFunction) pg_close, METH_VARARGS, pg_close__doc__}, {"fileno", (PyCFunction) pg_fileno, METH_VARARGS, pg_fileno__doc__}, {"getnotify", (PyCFunction) pg_getnotify, METH_VARARGS, pg_getnotify__doc__}, {"inserttable", (PyCFunction) pg_inserttable, METH_VARARGS, pg_inserttable__doc__}, {"transaction", (PyCFunction) pg_transaction, METH_VARARGS, pg_transaction__doc__}, {"parameter", (PyCFunction) pg_parameter, METH_VARARGS, pg_parameter__doc__}, {"escape_string", (PyCFunction) pg_escape_string, METH_VARARGS, pg_escape_string__doc__}, {"escape_bytea", (PyCFunction) pg_escape_bytea, METH_VARARGS, pg_escape_bytea__doc__}, #ifdef DIRECT_ACCESS {"putline", (PyCFunction) pg_putline, 1, pg_putline__doc__}, {"getline", (PyCFunction) pg_getline, 1, pg_getline__doc__}, {"endcopy", (PyCFunction) pg_endcopy, 1, pg_endcopy__doc__}, #endif /* DIRECT_ACCESS */ #ifdef LARGE_OBJECTS {"locreate", (PyCFunction) pg_locreate, 1, pg_locreate__doc__}, {"getlo", (PyCFunction) pg_getlo, 1, pg_getlo__doc__}, {"loimport", (PyCFunction) pg_loimport, 1, pg_loimport__doc__}, #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 */ /* first exception - close which returns a different error */ if (strcmp(name, "close") && !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(PQuser(self->cnx)); /* protocol version */ if (!strcmp(name, "protocol_version")) return PyInt_FromLong(PQprotocolVersion(self->cnx)); /* backend version */ if (!strcmp(name, "server_version")) #if PG_VERSION_NUM < 80000 return PyInt_FromLong(PG_VERSION_NUM); #else return PyInt_FromLong(PQserverVersion(self->cnx)); #endif /* attributes list */ if (!strcmp(name, "__members__")) { PyObject *list = PyList_New(10); if (list) { PyList_SET_ITEM(list, 0, PyString_FromString("host")); PyList_SET_ITEM(list, 1, PyString_FromString("port")); PyList_SET_ITEM(list, 2, PyString_FromString("db")); PyList_SET_ITEM(list, 3, PyString_FromString("options")); PyList_SET_ITEM(list, 4, PyString_FromString("tty")); PyList_SET_ITEM(list, 5, PyString_FromString("error")); PyList_SET_ITEM(list, 6, PyString_FromString("status")); PyList_SET_ITEM(list, 7, PyString_FromString("user")); PyList_SET_ITEM(list, 8, PyString_FromString("protocol_version")); PyList_SET_ITEM(list, 9, PyString_FromString("server_version")); } 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) pgquery_getresult, METH_VARARGS, pgquery_getresult__doc__}, {"dictresult", (PyCFunction) pgquery_dictresult, METH_VARARGS, pgquery_dictresult__doc__}, {"fieldname", (PyCFunction) pgquery_fieldname, METH_VARARGS, pgquery_fieldname__doc__}, {"fieldnum", (PyCFunction) pgquery_fieldnum, METH_VARARGS, pgquery_fieldnum__doc__}, {"listfields", (PyCFunction) pgquery_listfields, METH_VARARGS, pgquery_listfields__doc__}, {"ntuples", (PyCFunction) pgquery_ntuples, METH_VARARGS, pgquery_ntuples__doc__}, {NULL, NULL} }; /* gets query object attributes */ static PyObject * pgquery_getattr(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) pgquery_dealloc, /* tp_dealloc */ (printfunc) pgquery_print, /* tp_print */ (getattrfunc) pgquery_getattr, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc) pgquery_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ }; /* --------------------------------------------------------------------- */ /* MODULE FUNCTIONS */ /* escape string */ static char escape_string__doc__[] = "escape_string(str) -- escape a string for use within SQL."; static PyObject * escape_string(PyObject *self, PyObject *args) { char *from; /* our string argument */ char *to=NULL; /* the result */ int from_length; /* length of string */ int to_length; /* length of result */ PyObject *ret; /* string object to return */ if (!PyArg_ParseTuple(args, "s#", &from, &from_length)) return NULL; to_length = 2*from_length + 1; if (to_length < from_length) { /* overflow */ to_length = from_length; from_length = (from_length - 1)/2; } to = (char *)malloc(to_length); to_length = (int)PQescapeString(to, from, (size_t)from_length); ret = Py_BuildValue("s#", to, to_length); if (to) free(to); if (!ret) /* pass on exception */ return NULL; return ret; } /* escape bytea */ static char escape_bytea__doc__[] = "escape_bytea(data) -- escape binary data for use within SQL as type bytea."; static PyObject * escape_bytea(PyObject *self, PyObject *args) { unsigned char *from; /* our string argument */ unsigned char *to; /* the result */ int from_length; /* length of string */ size_t to_length; /* length of result */ PyObject *ret; /* string object to return */ if (!PyArg_ParseTuple(args, "s#", &from, &from_length)) return NULL; to = PQescapeBytea(from, (int)from_length, &to_length); ret = Py_BuildValue("s", to); if (to) PQfreemem((void *)to); if (!ret) /* pass on exception */ return NULL; return ret; } /* unescape bytea */ static char unescape_bytea__doc__[] = "unescape_bytea(str) -- unescape bytea data that has been retrieved as text."; static PyObject *unescape_bytea(PyObject *self, PyObject *args) { unsigned char *from; /* our string argument */ unsigned char *to; /* the result */ size_t to_length; /* length of result string */ PyObject *ret; /* string object to return */ if (!PyArg_ParseTuple(args, "s", &from)) return NULL; to = PQunescapeBytea(from, &to_length); ret = Py_BuildValue("s#", to, (int)to_length); if (to) PQfreemem((void *)to); if (!ret) /* pass on exception */ return NULL; return ret; } /* set decimal */ static char set_decimal__doc__[] = "set_decimal(cls) -- set a decimal type to be used for numeric values."; static PyObject * set_decimal(PyObject * self, PyObject * args) { PyObject *ret = NULL; PyObject *cls; if (PyArg_ParseTuple(args, "O", &cls)) { if (cls == Py_None) { Py_XDECREF(decimal); decimal = NULL; Py_INCREF(Py_None); ret = Py_None; } else if (PyCallable_Check(cls)) { Py_XINCREF(cls); Py_XDECREF(decimal); decimal = cls; Py_INCREF(Py_None); ret = Py_None; } else PyErr_SetString(PyExc_TypeError, "decimal type must be None or callable"); } return ret; } #ifdef DEFAULT_VARS /* gets default host */ static char getdefhost__doc__[] = "get_defhost() -- return default database host."; static PyObject * pggetdefhost(PyObject * self, PyObject * args) { /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method get_defhost() takes no parameter."); return NULL; } Py_XINCREF(pg_default_host); return pg_default_host; } /* sets default host */ static char setdefhost__doc__[] = "set_defhost(string) -- set default database host. Return previous value."; 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 char getdefbase__doc__[] = "get_defbase() -- return default database name."; static PyObject * pggetdefbase(PyObject * self, PyObject * args) { /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method get_defbase() takes no parameter."); return NULL; } Py_XINCREF(pg_default_base); return pg_default_base; } /* sets default base */ static char setdefbase__doc__[] = "set_defbase(string) -- set default database name. Return previous value"; 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 char getdefopt__doc__[] = "get_defopt() -- return default database options."; static PyObject * pggetdefopt(PyObject * self, PyObject * args) { /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method get_defopt() takes no parameter."); return NULL; } Py_XINCREF(pg_default_opt); return pg_default_opt; } /* sets default opt */ static char setdefopt__doc__[] = "set_defopt(string) -- set default database options. Return previous value."; 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 char getdeftty__doc__[] = "get_deftty() -- return default database debug terminal."; static PyObject * pggetdeftty(PyObject * self, PyObject * args) { /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method get_deftty() takes no parameter."); return NULL; } Py_XINCREF(pg_default_tty); return pg_default_tty; } /* sets default tty */ static char setdeftty__doc__[] = "set_deftty(string) -- set default database debug terminal. " "Return previous value."; 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 char getdefuser__doc__[] = "get_defuser() -- return default database username."; static PyObject * pggetdefuser(PyObject * self, PyObject * args) { /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method get_defuser() takes no parameter."); return NULL; } Py_XINCREF(pg_default_user); return pg_default_user; } /* sets default username */ static char setdefuser__doc__[] = "set_defuser() -- set default database username. Return previous value."; 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 char setdefpasswd__doc__[] = "set_defpasswd() -- set default database 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; } Py_INCREF(Py_None); return Py_None; } /* gets default port */ static char getdefport__doc__[] = "get_defport() -- return default database port."; static PyObject * pggetdefport(PyObject * self, PyObject * args) { /* checks args */ if (!PyArg_ParseTuple(args, "")) { PyErr_SetString(PyExc_TypeError, "method get_defport() takes no parameter."); return NULL; } Py_XINCREF(pg_default_port); return pg_default_port; } /* sets default port */ static char setdefport__doc__[] = "set_defport(integer) -- set default database port. Return previous value."; 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 = PyInt_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, METH_VARARGS|METH_KEYWORDS, connect__doc__}, {"escape_string", (PyCFunction) escape_string, METH_VARARGS, escape_string__doc__}, {"escape_bytea", (PyCFunction) escape_bytea, METH_VARARGS, escape_bytea__doc__}, {"unescape_bytea", (PyCFunction) unescape_bytea, METH_VARARGS, unescape_bytea__doc__}, {"set_decimal", (PyCFunction) set_decimal, METH_VARARGS, set_decimal__doc__}, #ifdef DEFAULT_VARS {"get_defhost", pggetdefhost, METH_VARARGS, getdefhost__doc__}, {"set_defhost", pgsetdefhost, METH_VARARGS, setdefhost__doc__}, {"get_defbase", pggetdefbase, METH_VARARGS, getdefbase__doc__}, {"set_defbase", pgsetdefbase, METH_VARARGS, setdefbase__doc__}, {"get_defopt", pggetdefopt, METH_VARARGS, getdefopt__doc__}, {"set_defopt", pgsetdefopt, METH_VARARGS, setdefopt__doc__}, {"get_deftty", pggetdeftty, METH_VARARGS, getdeftty__doc__}, {"set_deftty", pgsetdeftty, METH_VARARGS, setdeftty__doc__}, {"get_defport", pggetdefport, METH_VARARGS, getdefport__doc__}, {"set_defport", pgsetdefport, METH_VARARGS, setdefport__doc__}, {"get_defuser", pggetdefuser, METH_VARARGS, getdefuser__doc__}, {"set_defuser", pgsetdefuser, METH_VARARGS, setdefuser__doc__}, {"set_defpasswd", pgsetdefpasswd, METH_VARARGS, setdefpasswd__doc__}, #endif /* DEFAULT_VARS */ {NULL, NULL} /* sentinel */ }; static char pg__doc__[] = "Python interface to PostgreSQL DB"; /* Initialization function for the module */ DL_EXPORT(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 = PgSourceType.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); /* Exceptions as defined by DB-API 2.0 */ Error = PyErr_NewException("pg.Error", PyExc_StandardError, NULL); PyDict_SetItemString(dict, "Error", Error); Warning = PyErr_NewException("pg.Warning", PyExc_StandardError, NULL); PyDict_SetItemString(dict, "Warning", Warning); InterfaceError = PyErr_NewException("pg.InterfaceError", Error, NULL); PyDict_SetItemString(dict, "InterfaceError", InterfaceError); DatabaseError = PyErr_NewException("pg.DatabaseError", Error, NULL); PyDict_SetItemString(dict, "DatabaseError", DatabaseError); InternalError = PyErr_NewException("pg.InternalError", DatabaseError, NULL); PyDict_SetItemString(dict, "InternalError", InternalError); OperationalError = PyErr_NewException("pg.OperationalError", DatabaseError, NULL); PyDict_SetItemString(dict, "OperationalError", OperationalError); ProgrammingError = PyErr_NewException("pg.ProgrammingError", DatabaseError, NULL); PyDict_SetItemString(dict, "ProgrammingError", ProgrammingError); IntegrityError = PyErr_NewException("pg.IntegrityError", DatabaseError, NULL); PyDict_SetItemString(dict, "IntegrityError", IntegrityError); DataError = PyErr_NewException("pg.DataError", DatabaseError, NULL); PyDict_SetItemString(dict, "DataError", DataError); NotSupportedError = PyErr_NewException("pg.NotSupportedError", DatabaseError, NULL); PyDict_SetItemString(dict, "NotSupportedError", NotSupportedError); /* Make the version available */ v = PyString_FromString(PyPgVersion); PyDict_SetItemString(dict, "version", v); PyDict_SetItemString(dict, "__version__", v); Py_DECREF(v); /* results type for queries */ PyDict_SetItemString(dict, "RESULT_EMPTY", PyInt_FromLong(RESULT_EMPTY)); PyDict_SetItemString(dict, "RESULT_DML", PyInt_FromLong(RESULT_DML)); PyDict_SetItemString(dict, "RESULT_DDL", PyInt_FromLong(RESULT_DDL)); PyDict_SetItemString(dict, "RESULT_DQL", PyInt_FromLong(RESULT_DQL)); /* transaction states */ PyDict_SetItemString(dict,"TRANS_IDLE",PyInt_FromLong(PQTRANS_IDLE)); PyDict_SetItemString(dict,"TRANS_ACTIVE",PyInt_FromLong(PQTRANS_ACTIVE)); PyDict_SetItemString(dict,"TRANS_INTRANS",PyInt_FromLong(PQTRANS_INTRANS)); PyDict_SetItemString(dict,"TRANS_INERROR",PyInt_FromLong(PQTRANS_INERROR)); PyDict_SetItemString(dict,"TRANS_UNKNOWN",PyInt_FromLong(PQTRANS_UNKNOWN)); #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"); } PyGreSQL-4.0/pgdb.py010064400017500000000000000422501112713757600132420ustar darcywheel#!/usr/bin/env python # # pgdb.py # # Written by D'Arcy J.M. Cain # # $Id: pgdb.py,v 1.54 2008/11/23 14:32:18 cito Exp $ # """pgdb - DB-API 2.0 compliant module for PygreSQL. (c) 1999, Pascal Andre . See package documentation for further information on copyright. Inline documentation is sparse. See DB-API 2.0 specification for usage information: http://www.python.org/peps/pep-0249.html Basic usage: pgdb.connect(connect_string) # open a connection # connect_string = 'host:database:user:password:opt:tty' # All parts are optional. You may also pass host through # password as keyword arguments. To pass a port, # pass it in the host keyword parameter: pgdb.connect(host='localhost:5432') connection.cursor() # open a cursor cursor.execute(query[, params]) # Execute a query, binding params (a dictionary) if they are # passed. The binding syntax is the same as the % operator # for dictionaries, and no quoting is done. cursor.executemany(query, list of params) # Execute a query many times, binding each param dictionary # from the list. cursor.fetchone() # fetch one row, [value, value, ...] cursor.fetchall() # fetch all rows, [[value, value, ...], ...] cursor.fetchmany([size]) # returns size or cursor.arraysize number of rows, # [[value, value, ...], ...] from result set. # Default cursor.arraysize is 1. cursor.description # returns information about the columns # [(column_name, type_name, display_size, # internal_size, precision, scale, null_ok), ...] # Note that precision, scale and null_ok are not implemented. cursor.rowcount # number of rows available in the result set # Available after a call to execute. connection.commit() # commit transaction connection.rollback() # or rollback transaction cursor.close() # close the cursor connection.close() # close the connection """ from _pg import * import time try: frozenset except NameError: # Python < 2.4 from sets import ImmutableSet as frozenset from datetime import datetime, timedelta try: # use Decimal if available from decimal import Decimal set_decimal(Decimal) except ImportError: # otherwise (Python < 2.4) Decimal = float # use float instead of Decimal ### Module Constants # compliant with DB SIG 2.0 apilevel = '2.0' # module may be shared, but not connections threadsafety = 1 # this module use extended python format codes paramstyle = 'pyformat' ### Internal Types Handling def decimal_type(decimal_type=None): """Get or set global type to be used for decimal values.""" global Decimal if decimal_type is not None: Decimal = decimal_type set_decimal(decimal_type) return Decimal def _cast_bool(value): return value[:1] in ['t', 'T'] def _cast_money(value): return Decimal(''.join(filter( lambda v: v in '0123456789.-', value))) _cast = {'bool': _cast_bool, 'int2': int, 'int4': int, 'serial': int, 'int8': long, 'oid': long, 'oid8': long, 'float4': float, 'float8': float, 'numeric': Decimal, 'money': _cast_money} class pgdbTypeCache(dict): """Cache for database types.""" def __init__(self, cnx): """Initialize type cache for connection.""" super(pgdbTypeCache, self).__init__() self._src = cnx.source() def typecast(typ, value): """Cast value to database type.""" if value is None: # for NULL values, no typecast is necessary return None cast = _cast.get(typ) if cast is None: # no typecast available or necessary return value else: return cast(value) typecast = staticmethod(typecast) def getdescr(self, oid): """Get name of database type with given oid.""" try: return self[oid] except KeyError: self._src.execute( "SELECT typname, typlen " "FROM pg_type WHERE oid=%s" % oid) res = self._src.fetch(1)[0] # The column name is omitted from the return value. # It will have to be prepended by the caller. res = (res[0], None, int(res[1]), None, None, None) self[oid] = res return res class _quotedict(dict): """Dictionary with auto quoting of its items. The quote attribute must be set to the desired quote function. """ def __getitem__(self, key): return self.quote(super(_quotedict, self).__getitem__(key)) ### Cursor Object class pgdbCursor(object): """Cursor Object.""" def __init__(self, dbcnx): """Create a cursor object for the database connection.""" self.connection = self._dbcnx = dbcnx self._cnx = dbcnx._cnx self._type_cache = dbcnx._type_cache self._src = self._cnx.source() self.description = None self.rowcount = -1 self.arraysize = 1 self.lastrowid = None def __iter__(self): """Return self to make cursors compatible to the iteration protocol.""" return self def _quote(self, val): """Quote value depending on its type.""" if isinstance(val, datetime): val = str(val) elif isinstance(val, unicode): val = val.encode( 'utf8' ) if isinstance(val, str): val = "'%s'" % self._cnx.escape_string(val) elif isinstance(val, (int, long, float)): pass elif val is None: val = 'NULL' elif isinstance(val, (list, tuple)): val = '(%s)' % ','.join(map(lambda v: str(self._quote(v)), val)) elif Decimal is not float and isinstance(val, Decimal): pass elif hasattr(val, '__pg_repr__'): val = val.__pg_repr__() else: raise InterfaceError( 'do not know how to handle type %s' % type(val)) return val def _quoteparams(self, string, params): """Quote parameters. This function works for both mappings and sequences. """ if isinstance(params, dict): params = _quotedict(params) params.quote = self._quote else: params = tuple(map(self._quote, params)) return string % params def row_factory(row): """Process rows before they are returned. You can overwrite this with a custom row factory, e.g. a dict factory: class myCursor(pgdb.pgdbCursor): def cursor.row_factory(self, row): d = {} for idx, col in enumerate(self.description): d[col[0]] = row[idx] return d cursor = myCursor(cnx) """ return row row_factory = staticmethod(row_factory) def close(self): """Close the cursor object.""" self._src.close() self.description = None self.rowcount = -1 self.lastrowid = None def execute(self, operation, params=None): """Prepare and execute a database operation (query or command).""" # The parameters may also be specified as list of # tuples to e.g. insert multiple rows in a single # operation, but this kind of usage is deprecated: if (params and isinstance(params, list) and isinstance(params[0], tuple)): self.executemany(operation, params) else: # not a list of tuples self.executemany(operation, (params,)) def executemany(self, operation, param_seq): """Prepare operation and execute it against a parameter sequence.""" if not param_seq: # don't do anything without parameters return self.description = None self.rowcount = -1 # first try to execute all queries totrows = 0 sql = "BEGIN" try: if not self._dbcnx._tnx: try: self._cnx.source().execute(sql) except Exception: raise OperationalError("can't start transaction") self._dbcnx._tnx = True for params in param_seq: if params: sql = self._quoteparams(operation, params) else: sql = operation rows = self._src.execute(sql) if rows: # true if not DML totrows += rows else: self.rowcount = -1 except Error, msg: raise DatabaseError("error '%s' in '%s'" % (msg, sql)) except Exception, err: raise OperationalError("internal error in '%s': %s" % (sql, err)) # then initialize result raw count and description if self._src.resulttype == RESULT_DQL: self.rowcount = self._src.ntuples getdescr = self._type_cache.getdescr coltypes = self._src.listinfo() self.description = [typ[1:2] + getdescr(typ[2]) for typ in coltypes] self.lastrowid = self._src.oidstatus() else: self.rowcount = totrows self.description = None self.lastrowid = self._src.oidstatus() def fetchone(self): """Fetch the next row of a query result set.""" res = self.fetchmany(1, False) try: return res[0] except IndexError: return None def fetchall(self): """Fetch all (remaining) rows of a query result.""" return self.fetchmany(-1, False) def fetchmany(self, size=None, keep=False): """Fetch the next set of rows of a query result. The number of rows to fetch per call is specified by the size parameter. If it is not given, the cursor's arraysize determines the number of rows to be fetched. If you set the keep parameter to true, this is kept as new arraysize. """ if size is None: size = self.arraysize if keep: self.arraysize = size try: result = self._src.fetch(size) except Error, err: raise DatabaseError(str(err)) row_factory = self.row_factory typecast = self._type_cache.typecast coltypes = [desc[1] for desc in self.description] return [row_factory([typecast(*args) for args in zip(coltypes, row)]) for row in result] def next(self): """Return the next row (support for the iteration protocol).""" res = self.fetchone() if res is None: raise StopIteration return res def nextset(): """Not supported.""" raise NotSupportedError("nextset() is not supported") nextset = staticmethod(nextset) def setinputsizes(sizes): """Not supported.""" pass setinputsizes = staticmethod(setinputsizes) def setoutputsize(size, column=0): """Not supported.""" pass setoutputsize = staticmethod(setoutputsize) ### Connection Objects class pgdbCnx(object): """Connection Object.""" # expose the exceptions as attributes on the connection object Error = Error Warning = Warning InterfaceError = InterfaceError DatabaseError = DatabaseError InternalError = InternalError OperationalError = OperationalError ProgrammingError = ProgrammingError IntegrityError = IntegrityError DataError = DataError NotSupportedError = NotSupportedError def __init__(self, cnx): """Create a database connection object.""" self._cnx = cnx # connection self._tnx = False # transaction state self._type_cache = pgdbTypeCache(cnx) try: self._cnx.source() except Exception: raise OperationalError("invalid connection") def close(self): """Close the connection object.""" if self._cnx: self._cnx.close() self._cnx = None else: raise OperationalError("connection has been closed") def commit(self): """Commit any pending transaction to the database.""" if self._cnx: if self._tnx: self._tnx = False try: self._cnx.source().execute("COMMIT") except Exception: raise OperationalError("can't commit") else: raise OperationalError("connection has been closed") def rollback(self): """Roll back to the start of any pending transaction.""" if self._cnx: if self._tnx: self._tnx = False try: self._cnx.source().execute("ROLLBACK") except Exception: raise OperationalError("can't rollback") else: raise OperationalError("connection has been closed") def cursor(self): """Return a new Cursor Object using the connection.""" if self._cnx: try: return pgdbCursor(self) except Exception: raise OperationalError("invalid connection") else: raise OperationalError("connection has been closed") ### Module Interface _connect_ = connect def connect(dsn=None, user=None, password=None, host=None, database=None): """Connects to a database.""" # first get params from DSN dbport = -1 dbhost = "" dbbase = "" dbuser = "" dbpasswd = "" dbopt = "" dbtty = "" try: params = dsn.split(":") dbhost = params[0] dbbase = params[1] dbuser = params[2] dbpasswd = params[3] dbopt = params[4] dbtty = params[5] except (AttributeError, IndexError, TypeError): pass # override if necessary if user is not None: dbuser = user if password is not None: dbpasswd = password if database is not None: dbbase = database if host is not None: try: params = host.split(":") dbhost = params[0] dbport = int(params[1]) except (AttributeError, IndexError, TypeError, ValueError): pass # empty host is localhost if dbhost == "": dbhost = None if dbuser == "": dbuser = None # open the connection cnx = _connect_(dbbase, dbhost, dbport, dbopt, dbtty, dbuser, dbpasswd) return pgdbCnx(cnx) ### Types Handling class pgdbType(frozenset): """Type class for a couple of PostgreSQL data types. PostgreSQL is object-oriented: types are dynamic. We must thus use type names as internal type codes. """ if frozenset.__module__ == '__builtin__': def __new__(cls, values): if isinstance(values, basestring): values = values.split() return super(pgdbType, cls).__new__(cls, values) else: # Python < 2.4 def __init__(self, values): if isinstance(values, basestring): values = values.split() super(pgdbType, self).__init__(values) def __eq__(self, other): if isinstance(other, basestring): return other in self else: return super(pgdbType, self).__eq__(other) def __ne__(self, other): if isinstance(other, basestring): return other not in self else: return super(pgdbType, self).__ne__(other) # Mandatory type objects defined by DB-API 2 specs: STRING = pgdbType('char bpchar name text varchar') BINARY = pgdbType('bytea') NUMBER = pgdbType('int2 int4 serial int8 float4 float8 numeric money') DATETIME = pgdbType('date time timetz timestamp timestamptz datetime abstime' ' interval tinterval timespan reltime') ROWID = pgdbType('oid oid8') # Additional type objects (more specific): BOOL = pgdbType('bool') SMALLINT = pgdbType('int2') INTEGER = pgdbType('int2 int4 int8 serial') LONG = pgdbType('int8') FLOAT = pgdbType('float4 float8') NUMERIC = pgdbType('numeric') MONEY = pgdbType('money') DATE = pgdbType('date') TIME = pgdbType('time timetz') TIMESTAMP = pgdbType('timestamp timestamptz datetime abstime') INTERVAL = pgdbType('interval tinterval timespan reltime') # Mandatory type helpers defined by DB-API 2 specs: def Date(year, month, day): """Construct an object holding a date value.""" return datetime(year, month, day) def Time(hour, minute, second): """Construct an object holding a time value.""" return timedelta(hour, minute, second) def Timestamp(year, month, day, hour, minute, second): """construct an object holding a time stamp value.""" return datetime(year, month, day, hour, minute, second) def DateFromTicks(ticks): """Construct an object holding a date value from the given ticks value.""" return Date(*time.localtime(ticks)[:3]) def TimeFromTicks(ticks): """construct an object holding a time value from the given ticks value.""" return Time(*time.localtime(ticks)[3:6]) def TimestampFromTicks(ticks): """construct an object holding a time stamp from the given ticks value.""" return Timestamp(*time.localtime(ticks)[:6]) def Binary(value): """construct an object capable of holding a binary (long) string value.""" return value # If run as script, print some information: if __name__ == '__main__': print 'PyGreSQL version', version print print __doc__ PyGreSQL-4.0/pg.py010064400017500000000000000635321112713757600127420ustar darcywheel#!/usr/bin/env python # # pg.py # # Written by D'Arcy J.M. Cain # Improved by Christoph Zwerschke # # $Id: pg.py,v 1.77 2008/12/30 16:40:00 darcy Exp $ # """PyGreSQL classic interface. This pg module implements some basic database management stuff. It includes the _pg module and builds on it, providing the higher level wrapper class named DB with addtional functionality. This is known as the "classic" ("old style") PyGreSQL interface. For a DB-API 2 compliant interface use the newer pgdb module. """ from _pg import * try: frozenset except NameError: # Python < 2.4 from sets import ImmutableSet as frozenset try: from decimal import Decimal set_decimal(Decimal) except ImportError: pass # Python < 2.4 # Auxiliary functions which are independent from a DB connection: def _is_quoted(s): """Check whether this string is a quoted identifier.""" s = s.replace('_', 'a') return not s.isalnum() or s[:1].isdigit() or s != s.lower() def _is_unquoted(s): """Check whether this string is an unquoted identifier.""" s = s.replace('_', 'a') return s.isalnum() and not s[:1].isdigit() def _split_first_part(s): """Split the first part of a dot separated string.""" s = s.lstrip() if s[:1] == '"': p = [] s = s.split('"', 3)[1:] p.append(s[0]) while len(s) == 3 and s[1] == '': p.append('"') s = s[2].split('"', 2) p.append(s[0]) p = [''.join(p)] s = '"'.join(s[1:]).lstrip() if s: if s[:0] == '.': p.append(s[1:]) else: s = _split_first_part(s) p[0] += s[0] if len(s) > 1: p.append(s[1]) else: p = s.split('.', 1) s = p[0].rstrip() if _is_unquoted(s): s = s.lower() p[0] = s return p def _split_parts(s): """Split all parts of a dot separated string.""" q = [] while s: s = _split_first_part(s) q.append(s[0]) if len(s) < 2: break s = s[1] return q def _join_parts(s): """Join all parts of a dot separated string.""" return '.'.join([_is_quoted(p) and '"%s"' % p or p for p in s]) def _oid_key(qcl): """Build oid key from qualified class name.""" return 'oid(%s)' % qcl # The PostGreSQL database connection interface: class DB(object): """Wrapper class for the _pg connection type.""" def __init__(self, *args, **kw): """Create a new connection. You can pass either the connection parameters or an existing _pg or pgdb connection. This allows you to use the methods of the classic pg interface with a DB-API 2 pgdb connection. """ if not args and len(kw) == 1: db = kw.get('db') elif not kw and len(args) == 1: db = args[0] else: db = None if db: if isinstance(db, DB): db = db.db else: try: db = db._cnx except AttributeError: pass if not db or not hasattr(db, 'db') or not hasattr(db, 'query'): db = connect(*args, **kw) self._closeable = 1 else: self._closeable = 0 self.db = db self.dbname = db.db self._attnames = {} self._pkeys = {} self._privileges = {} self._args = args, kw self.debug = None # For debugging scripts, this can be set # * to a string format specification (e.g. in CGI set to "%s
"), # * to a file object to write debug statements or # * to a callable object which takes a string argument. def __getattr__(self, name): # All undefined members are the same as in the underlying pg connection: if self.db: return getattr(self.db, name) else: raise InternalError('Connection is not valid') # Auxiliary methods def _do_debug(self, s): """Print a debug message.""" if self.debug: if isinstance(self.debug, basestring): print self.debug % s elif isinstance(self.debug, file): print >> self.debug, s elif callable(self.debug): self.debug(s) def _quote_text(self, d): """Quote text value.""" if not isinstance(d, basestring): d = str(d) return "'%s'" % self.escape_string(d) _bool_true = frozenset('t true 1 y yes on'.split()) def _quote_bool(self, d): """Quote boolean value.""" if isinstance(d, basestring): if not d: return 'NULL' d = d.lower() in self._bool_true else: d = bool(d) return ("'f'", "'t'")[d] _date_literals = frozenset('current_date current_time' ' current_timestamp localtime localtimestamp'.split()) def _quote_date(self, d): """Quote date value.""" if not d: return 'NULL' if isinstance(d, basestring) and d.lower() in self._date_literals: return d return self._quote_text(d) def _quote_num(self, d): """Quote numeric value.""" if not d and d != 0: return 'NULL' return str(d) def _quote_money(self, d): """Quote money value.""" if not d: return 'NULL' return "'%.2f'" % float(d) _quote_funcs = dict( # quote methods for each type text=_quote_text, bool=_quote_bool, date=_quote_date, int=_quote_num, num=_quote_num, float=_quote_num, money=_quote_money) def _quote(self, d, t): """Return quotes if needed.""" if d is None: return 'NULL' try: quote_func = self._quote_funcs[t] except KeyError: quote_func = self._quote_funcs['text'] return quote_func(self, d) def _split_schema(self, cl): """Return schema and name of object separately. This auxiliary function splits off the namespace (schema) belonging to the class with the name cl. If the class name is not qualified, the function is able to determine the schema of the class, taking into account the current search path. """ s = _split_parts(cl) if len(s) > 1: # name already qualfied? # should be database.schema.table or schema.table if len(s) > 3: raise ProgrammingError('Too many dots in class name %s' % cl) schema, cl = s[-2:] else: cl = s[0] # determine search path q = 'SELECT current_schemas(TRUE)' schemas = self.db.query(q).getresult()[0][0][1:-1].split(',') if schemas: # non-empty path # search schema for this object in the current search path q = ' UNION '.join( ["SELECT %d::integer AS n, '%s'::name AS nspname" % s for s in enumerate(schemas)]) q = ("SELECT nspname FROM pg_class" " JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid" " JOIN (%s) AS p USING (nspname)" " WHERE pg_class.relname = '%s'" " ORDER BY n LIMIT 1" % (q, cl)) schema = self.db.query(q).getresult() if schema: # schema found schema = schema[0][0] else: # object not found in current search path schema = 'public' else: # empty path schema = 'public' return schema, cl def _add_schema(self, cl): """Ensure that the class name is prefixed with a schema name.""" return _join_parts(self._split_schema(cl)) # Public methods # escape_string and escape_bytea exist as methods, # so we define unescape_bytea as a method as well unescape_bytea = staticmethod(unescape_bytea) def close(self): """Close the database connection.""" # Wraps shared library function so we can track state. if self._closeable: if self.db: self.db.close() self.db = None else: raise InternalError('Connection already closed') def reset(self): """Reset connection with current parameters. All derived queries and large objects derived from this connection will not be usable after this call. """ self.db.reset() def reopen(self): """Reopen connection to the database. Used in case we need another connection to the same database. Note that we can still reopen a database that we have closed. """ # There is no such shared library function. if self._closeable: db = connect(*self._args[0], **self._args[1]) if self.db: self.db.close() self.db = db def query(self, qstr): """Executes a SQL command string. This method simply sends a SQL query to the database. If the query is an insert statement that inserted exactly one row into a table that has OIDs, the return value is the OID of the newly inserted row. If the query is an update or delete statement, or an insert statement that did not insert exactly one row in a table with OIDs, then the numer of rows affected is returned as a string. If it is a statement that returns rows as a result (usually a select statement, but maybe also an "insert/update ... returning" statement), this method returns a pgqueryobject that can be accessed via getresult() or dictresult() or simply printed. Otherwise, it returns `None`. """ # Wraps shared library function for debugging. if not self.db: raise InternalError('Connection is not valid') self._do_debug(qstr) return self.db.query(qstr) def pkey(self, cl, newpkey=None): """This method gets or sets the primary key of a class. Composite primary keys are represented as frozensets. Note that this raises an exception if the table does not have a primary key. If newpkey is set and is not a dictionary then set that value as the primary key of the class. If it is a dictionary then replace the _pkeys dictionary with a copy of it. """ # First see if the caller is supplying a dictionary if isinstance(newpkey, dict): # make sure that all classes have a namespace self._pkeys = dict([ ('.' in cl and cl or 'public.' + cl, pkey) for cl, pkey in newpkey.iteritems()]) return self._pkeys qcl = self._add_schema(cl) # build fully qualified class name # Check if the caller is supplying a new primary key for the class if newpkey: self._pkeys[qcl] = newpkey return newpkey # Get all the primary keys at once if qcl not in self._pkeys: # if not found, check again in case it was added after we started self._pkeys = {} if self.server_version >= 80200: # the ANY syntax works correctly only with PostgreSQL >= 8.2 any_indkey = "= ANY (pg_index.indkey)" else: any_indkey = "IN (%s)" % ', '.join( ['pg_index.indkey[%d]' % i for i in range(16)]) for r in self.db.query( "SELECT pg_namespace.nspname, pg_class.relname," " pg_attribute.attname FROM pg_class" " JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace" " AND pg_namespace.nspname NOT LIKE 'pg_%'" " JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid" " AND pg_attribute.attisdropped = 'f'" " JOIN pg_index ON pg_index.indrelid = pg_class.oid" " AND pg_index.indisprimary = 't'" " AND pg_attribute.attnum " + any_indkey).getresult(): cl, pkey = _join_parts(r[:2]), r[2] self._pkeys.setdefault(cl, []).append(pkey) # (only) for composite primary keys, the values will be frozensets for cl, pkey in self._pkeys.iteritems(): self._pkeys[cl] = len(pkey) > 1 and frozenset(pkey) or pkey[0] self._do_debug(self._pkeys) # will raise an exception if primary key doesn't exist return self._pkeys[qcl] def get_databases(self): """Get list of databases in the system.""" return [s[0] for s in self.db.query('SELECT datname FROM pg_database').getresult()] def get_relations(self, kinds=None): """Get list of relations in connected database of specified kinds. If kinds is None or empty, all kinds of relations are returned. Otherwise kinds can be a string or sequence of type letters specifying which kind of relations you want to list. """ where = kinds and "pg_class.relkind IN (%s) AND" % ','.join( ["'%s'" % x for x in kinds]) or '' return map(_join_parts, self.db.query( "SELECT pg_namespace.nspname, pg_class.relname " "FROM pg_class " "JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace " "WHERE %s pg_class.relname !~ '^Inv' AND " "pg_class.relname !~ '^pg_' " "ORDER BY 1, 2" % where).getresult()) def get_tables(self): """Return list of tables in connected database.""" return self.get_relations('r') def get_attnames(self, cl, newattnames=None): """Given the name of a table, digs out the set of attribute names. Returns a dictionary of attribute names (the names are the keys, the values are the names of the attributes' types). If the optional newattnames exists, it must be a dictionary and will become the new attribute names dictionary. """ if isinstance(newattnames, dict): self._attnames = newattnames return elif newattnames: raise ProgrammingError( 'If supplied, newattnames must be a dictionary') cl = self._split_schema(cl) # split into schema and class qcl = _join_parts(cl) # build fully qualified name # May as well cache them: if qcl in self._attnames: return self._attnames[qcl] if qcl not in self.get_relations('rv'): raise ProgrammingError('Class %s does not exist' % qcl) t = {} for att, typ in self.db.query("SELECT pg_attribute.attname," " pg_type.typname FROM pg_class" " JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid" " JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid" " JOIN pg_type ON pg_type.oid = pg_attribute.atttypid" " WHERE pg_namespace.nspname = '%s' AND pg_class.relname = '%s'" " AND (pg_attribute.attnum > 0 or pg_attribute.attname = 'oid')" " AND pg_attribute.attisdropped = 'f'" % cl).getresult(): if typ.startswith('bool'): t[att] = 'bool' elif typ.startswith('abstime'): t[att] = 'date' elif typ.startswith('date'): t[att] = 'date' elif typ.startswith('interval'): t[att] = 'date' elif typ.startswith('timestamp'): t[att] = 'date' elif typ.startswith('oid'): t[att] = 'int' elif typ.startswith('int'): t[att] = 'int' elif typ.startswith('float'): t[att] = 'float' elif typ.startswith('numeric'): t[att] = 'num' elif typ.startswith('money'): t[att] = 'money' else: t[att] = 'text' self._attnames[qcl] = t # cache it return self._attnames[qcl] def has_table_privilege(self, cl, privilege='select'): """Check whether current user has specified table privilege.""" qcl = self._add_schema(cl) privilege = privilege.lower() try: return self._privileges[(qcl, privilege)] except KeyError: q = "SELECT has_table_privilege('%s', '%s')" % (qcl, privilege) ret = self.db.query(q).getresult()[0][0] == 't' self._privileges[(qcl, privilege)] = ret return ret def get(self, cl, arg, keyname=None): """Get a tuple from a database table or view. This method is the basic mechanism to get a single row. The keyname 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. For a composite key, keyname can also be a sequence of key names. The OID is also put into the dictionary if the table has one, but in order to allow the caller to work with multiple tables, it is munged as oid(schema.table). """ if cl.endswith('*'): # scan descendant tables? cl = cl[:-1].rstrip() # need parent table name # build qualified class name qcl = self._add_schema(cl) # To allow users to work with multiple tables, # we munge the name of the "oid" the key qoid = _oid_key(qcl) if not keyname: # use the primary key by default try: keyname = self.pkey(qcl) except KeyError: raise ProgrammingError('Class %s has no primary key' % qcl) # We want the oid for later updates if that isn't the key if keyname == 'oid': if isinstance(arg, dict): if qoid not in arg: raise ProgrammingError('%s not in arg' % qoid) else: arg = {qoid: arg} where = 'oid = %s' % arg[qoid] attnames = '*' else: attnames = self.get_attnames(qcl) if isinstance(keyname, basestring): keyname = (keyname,) if not isinstance(arg, dict): if len(keyname) > 1: raise ProgrammingError('Composite key needs dict as arg') arg = dict([(k, arg) for k in keyname]) where = ' AND '.join(['%s = %s' % (k, self._quote(arg[k], attnames[k])) for k in keyname]) attnames = ', '.join(attnames) q = 'SELECT %s FROM %s WHERE %s LIMIT 1' % (attnames, qcl, where) self._do_debug(q) res = self.db.query(q).dictresult() if not res: raise DatabaseError('No such record in %s where %s' % (qcl, where)) for att, value in res[0].iteritems(): arg[att == 'oid' and qoid or att] = value return arg def insert(self, cl, d=None, **kw): """Insert a tuple into a database table. This method inserts a row into a table. If a dictionary is supplied it starts with that. Otherwise it uses a blank dictionary. Either way the dictionary is updated from the keywords. The dictionary is then, if possible, reloaded with the values actually inserted in order to pick up values modified by rules, triggers, etc. Note: The method currently doesn't support insert into views although PostgreSQL does. """ qcl = self._add_schema(cl) qoid = _oid_key(qcl) if d is None: d = {} d.update(kw) attnames = self.get_attnames(qcl) names, values = [], [] for n in attnames: if n != 'oid' and n in d: names.append('"%s"' % n) values.append(self._quote(d[n], attnames[n])) names, values = ', '.join(names), ', '.join(values) selectable = self.has_table_privilege(qcl) if selectable and self.server_version >= 80200: ret = ' RETURNING %s*' % ('oid' in attnames and 'oid, ' or '') else: ret = '' q = 'INSERT INTO %s (%s) VALUES (%s)%s' % (qcl, names, values, ret) self._do_debug(q) res = self.db.query(q) if ret: res = res.dictresult() for att, value in res[0].iteritems(): d[att == 'oid' and qoid or att] = value elif isinstance(res, int): d[qoid] = res if selectable: self.get(qcl, d, 'oid') elif selectable: if qoid in d: self.get(qcl, d, 'oid') else: try: self.get(qcl, d) except ProgrammingError: pass # table has no primary key return d def update(self, cl, d=None, **kw): """Update an existing row in a database table. Similar to insert but updates an existing row. The update is based on the OID value as munged by get or passed as keyword, or on the primary key of the table. The dictionary is modified, if possible, to reflect any changes caused by the update due to triggers, rules, default values, etc. """ # Update always works on the oid which get returns if available, # otherwise use the primary key. Fail if neither. # Note that we only accept oid key from named args for safety qcl = self._add_schema(cl) qoid = _oid_key(qcl) if 'oid' in kw: kw[qoid] = kw['oid'] del kw['oid'] if d is None: d = {} d.update(kw) attnames = self.get_attnames(qcl) if qoid in d: where = 'oid = %s' % d[qoid] keyname = () else: try: keyname = self.pkey(qcl) except KeyError: raise ProgrammingError('Class %s has no primary key' % qcl) if isinstance(keyname, basestring): keyname = (keyname,) try: where = ' AND '.join(['%s = %s' % (k, self._quote(d[k], attnames[k])) for k in keyname]) except KeyError: raise ProgrammingError('Update needs primary key or oid.') values = [] for n in attnames: if n in d and n not in keyname: values.append('%s = %s' % (n, self._quote(d[n], attnames[n]))) if not values: return d values = ', '.join(values) selectable = self.has_table_privilege(qcl) if selectable and self.server_version >= 880200: ret = ' RETURNING %s*' % ('oid' in attnames and 'oid, ' or '') else: ret = '' q = 'UPDATE %s SET %s WHERE %s%s' % (qcl, values, where, ret) self._do_debug(q) res = self.db.query(q) if ret: res = self.db.query(q).dictresult() for att, value in res[0].iteritems(): d[att == 'oid' and qoid or att] = value else: self.db.query(q) if selectable: if qoid in d: self.get(qcl, d, 'oid') else: self.get(qcl, d) return d def clear(self, cl, a=None): """ This method clears all the attributes to values determined by the types. Numeric types are set to 0, Booleans are set to 'f', 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. """ # At some point we will need a way to get defaults from a table. qcl = self._add_schema(cl) if a is None: a = {} # empty if argument is not present attnames = self.get_attnames(qcl) for n, t in attnames.iteritems(): if n == 'oid': continue if t in ('int', 'float', 'num', 'money'): a[n] = 0 elif t == 'bool': a[n] = 'f' else: a[n] = '' return a def delete(self, cl, d=None, **kw): """Delete an existing row in a database table. This method deletes the row from a table. It deletes based on the OID value as munged by get or passed as keyword, or on the primary key of the table. The return value is the number of deleted rows (i.e. 0 if the row did not exist and 1 if the row was deleted). """ # 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). # Note that we only accept oid key from named args for safety qcl = self._add_schema(cl) qoid = _oid_key(qcl) if 'oid' in kw: kw[qoid] = kw['oid'] del kw['oid'] if d is None: d = {} d.update(kw) if qoid in d: where = 'oid = %s' % d[qoid] else: try: keyname = self.pkey(qcl) except KeyError: raise ProgrammingError('Class %s has no primary key' % qcl) if isinstance(keyname, basestring): keyname = (keyname,) attnames = self.get_attnames(qcl) try: where = ' AND '.join(['%s = %s' % (k, self._quote(d[k], attnames[k])) for k in keyname]) except KeyError: raise ProgrammingError('Delete needs primary key or oid.') q = 'DELETE FROM %s WHERE %s' % (qcl, where) self._do_debug(q) return int(self.db.query(q)) # if run as script, print some information if __name__ == '__main__': print 'PyGreSQL version', version print print __doc__