Developing Informix Applications in Python · Developing Informix Applications in Python 11 Looping...

Preview:

Citation preview

Developing InformixApplications in Python

Carsten HaeseUnique Systems, Inc.

Informix Forum 2006Washington, DC

December 8-9, 2006

2Developing Informix Applications in Python

OverviewOverview

● Python FeaturesPython Features● InformixDB FeaturesInformixDB Features● Installing InformixDBInstalling InformixDB● Interactive Tour of InformixDBInteractive Tour of InformixDB● ““Real World” ExampleReal World” Example● ConclusionConclusion

3Developing Informix Applications in Python

Python FeaturesPython Features

● Open SourceOpen Source

● Cross-PlatformCross-Platform

● Easy to learnEasy to learn● Dynamic LanguageDynamic Language

● Interactive ConsoleInteractive Console

● Clean SyntaxClean Syntax

● Helpful user communityHelpful user community

● VersatileVersatile● Small tasks or large applicationsSmall tasks or large applications

● Stand-alone or embeddedStand-alone or embedded

● Console, GUI, or web applicationsConsole, GUI, or web applications

● Rich standard library and 3Rich standard library and 3rdrd party extensionsparty extensions

● ExpressiveExpressiveDuck typing, Iterators, HOFs, OOP, Duck typing, Iterators, HOFs, OOP, Powerful data types, exception handling, Powerful data types, exception handling, memory managementmemory management

● Fosters Reusable CodeFosters Reusable Code

● AgileAgile

● Rapid Application Rapid Application DevelopmentDevelopment

● Standardized database APIStandardized database API

4Developing Informix Applications in Python

InformixDB FeaturesInformixDB Features

● DB-API 2.0 compliantDB-API 2.0 compliant

● Portable (POSIX and Win32 Platforms)Portable (POSIX and Win32 Platforms)

● Multiple connectionsMultiple connections

● Parametrized queriesParametrized queries

● Statement caching and prepared statementsStatement caching and prepared statements

● Access to sqlcode and sqlerrdAccess to sqlcode and sqlerrd

● Update, delete, and insert cursorsUpdate, delete, and insert cursors

● Scroll cursors and cursors with holdScroll cursors and cursors with hold

● Supports all IDS data types, including Smart Large Objects, and Supports all IDS data types, including Smart Large Objects, and UDTsUDTs

5Developing Informix Applications in Python

Installing InformixDBInstalling InformixDB

Prerequisites:Prerequisites:

Python 2.2+Python 2.2+

Informix Client SDKInformix Client SDK

Get InformixDB from informixdb.sourceforge.netGet InformixDB from informixdb.sourceforge.net

POSIX Installation: Compile from sourcePOSIX Installation: Compile from source

python setup.py build_extpython setup.py build_ext

python setup.py installpython setup.py install (as root)(as root)

Windows: Installers are provided for Python 2.4+Windows: Installers are provided for Python 2.4+

6Developing Informix Applications in Python

Establishing a ConnectionEstablishing a Connection

import informixdbimport informixdbconn = informixdb.connect(”stores_demo”, ”informix”, ”pw”)conn = informixdb.connect(”stores_demo”, ”informix”, ”pw”)

7Developing Informix Applications in Python

Static SQL QueryStatic SQL Query

cur = conn.cursor()cur = conn.cursor()cur.execute(“select * from customer”)cur.execute(“select * from customer”)

8Developing Informix Applications in Python

Fetching Data RowsFetching Data Rows

cur.execute(“select * from customer”)cur.execute(“select * from customer”)print cur.fetchone()print cur.fetchone()print cur.fetchmany(5)print cur.fetchmany(5)print cur.fetchall()print cur.fetchall()

9Developing Informix Applications in Python

SQL Query with ParametersSQL Query with Parameters

start_date = raw_input(“Start Date: “)start_date = raw_input(“Start Date: “)end_date = raw_input(“End Date: “)end_date = raw_input(“End Date: “)

Question mark placeholders:Question mark placeholders:

cur.execute(“””cur.execute(“”” select * from orders where order_date between ? and ?select * from orders where order_date between ? and ?“””“””, (start_date, end_date) ), (start_date, end_date) )

Numeric placeholders:Numeric placeholders:

cur.execute(“””cur.execute(“”” select * from orders where order_date between :1 and :2select * from orders where order_date between :1 and :2“””“””, (start_date, end_date) ), (start_date, end_date) )

10Developing Informix Applications in Python

SQL Query with ParametersSQL Query with ParametersContinuedContinued

Named placeholders:Named placeholders:

cur.execute(“””cur.execute(“”” select * from orders where order_date between :start_date and :end_dateselect * from orders where order_date between :start_date and :end_date“””“””, {'start_date':start_date, 'end_date':end_date} ), {'start_date':start_date, 'end_date':end_date} )

Using Using locals()locals()makes named parameters look like host variables:makes named parameters look like host variables:

cur.execute(“””cur.execute(“”” select * from orders where order_date between :start_date and :end_dateselect * from orders where order_date between :start_date and :end_date“””“””, locals() ), locals() )

11Developing Informix Applications in Python

Looping through Data RowsLooping through Data Rows

Doing it the hard way:Doing it the hard way:

cur.execute("select * from orders where customer_num=104")cur.execute("select * from orders where customer_num=104")row = cur.fetchone()row = cur.fetchone()while row != None:while row != None: print "Order %s was placed on %s." % (row[0], row[1])print "Order %s was placed on %s." % (row[0], row[1]) row = cur.fetchone()row = cur.fetchone()

Using cursors as iterators is much easier:Using cursors as iterators is much easier:

cur.execute("select * from orders where customer_num=104")cur.execute("select * from orders where customer_num=104")for row in cur:for row in cur: print "Order %s was placed on %s." % (row[0], row[1])print "Order %s was placed on %s." % (row[0], row[1])

12Developing Informix Applications in Python

Column Access AlternativesColumn Access Alternatives

By default, rows are returned as tuples according to DB-API standard, which By default, rows are returned as tuples according to DB-API standard, which requires column access by number. Dictionary cursors return rows as requires column access by number. Dictionary cursors return rows as dictionaries that allow column access by name:dictionaries that allow column access by name:

dictcur = conn.cursor(rowformat=informixdb.ROW_AS_DICT)dictcur = conn.cursor(rowformat=informixdb.ROW_AS_DICT)dictcur.execute("select * from orders where customer_num=104")dictcur.execute("select * from orders where customer_num=104")for row in dictcur:for row in dictcur: print "Order %s was placed on %s.”,(row['order_num'],print "Order %s was placed on %s.”,(row['order_num'],

row['order_date']) row['order_date'])

Object cursors offer a more concise way of accessing columns by name:Object cursors offer a more concise way of accessing columns by name:

objcur = conn.cursor(rowformat=informixdb.ROW_AS_OBJECT)objcur = conn.cursor(rowformat=informixdb.ROW_AS_OBJECT)objcur.execute("select * from orders where customer_num=104")objcur.execute("select * from orders where customer_num=104")for row in objcur:for row in objcur: print "Order %s was placed on %s." % (row.order_num, print "Order %s was placed on %s." % (row.order_num,

row.order_date) row.order_date)

13Developing Informix Applications in Python

Named CursorNamed Cursor

Explicitly named cursors are useful as update or delete cursors:Explicitly named cursors are useful as update or delete cursors:

price_increase = {'SMT':4, 'HSK':5, 'SHM':3}price_increase = {'SMT':4, 'HSK':5, 'SHM':3}updcur = conn.cursor(name="updcur", rowformat=informixdb.ROW_AS_OBJECT)updcur = conn.cursor(name="updcur", rowformat=informixdb.ROW_AS_OBJECT)updcur.execute("select * from stock for update of unit_price")updcur.execute("select * from stock for update of unit_price")for row in updcur:for row in updcur: increase = price_increase.get(row.manu_code, None)increase = price_increase.get(row.manu_code, None) if increase != None:if increase != None: newprice = row.unit_price * (1.0+increase/100.0)newprice = row.unit_price * (1.0+increase/100.0) cur.execute("update stock set unit_price=? where current of updcur", cur.execute("update stock set unit_price=? where current of updcur",

[newprice] ) [newprice] )

14Developing Informix Applications in Python

Bulk ExecutionBulk Execution

names = [names = [ (“Jonathan”, “Leffler”),(“Jonathan”, “Leffler”), (“Darryl”, “Priest”),(“Darryl”, “Priest”), (“Carsten”, “Haese”)(“Carsten”, “Haese”)]]

cur.executemany(“””cur.executemany(“”” insert into customer(fname,lname) values(?,?)insert into customer(fname,lname) values(?,?)“””“””, names ), names )

For insert statements, executemany employs an insert cursor “under the hood” when For insert statements, executemany employs an insert cursor “under the hood” when possible.possible.

15Developing Informix Applications in Python

Prepared StatementsPrepared Statements

get_manu_name = conn.cursor()get_manu_name = conn.cursor()get_manu_name.prepare(“””get_manu_name.prepare(“”” select manu_name from manufact where manu_code=?select manu_name from manufact where manu_code=?“””“””))

while True:while True: manu_code = raw_input(“Enter a manufacturer code: “)manu_code = raw_input(“Enter a manufacturer code: “) if manu_code==””: breakif manu_code==””: break get_manu_name.execute(None, [manu_code] )get_manu_name.execute(None, [manu_code] ) manu_name = get_manu_name.fetchone()[0]manu_name = get_manu_name.fetchone()[0] print manu_nameprint manu_name

16Developing Informix Applications in Python

TransactionsTransactions

By default, a connection is always in a transaction. Committing or rolling By default, a connection is always in a transaction. Committing or rolling back the current transaction begins a new transaction.back the current transaction begins a new transaction.

conn.commit()conn.commit()conn.rollback()conn.rollback()

Sometimes it's useful to work outside of a transaction:Sometimes it's useful to work outside of a transaction:

conn.autocommit = Trueconn.autocommit = True

17Developing Informix Applications in Python

Scroll CursorScroll Cursor

scrollcurs = conn.cursor(scroll=True)scrollcurs = conn.cursor(scroll=True)scrollcurs.execute(“select * from customer order by customer_num”)scrollcurs.execute(“select * from customer order by customer_num”)scrollcurs.scroll(10,”absolute”)scrollcurs.scroll(10,”absolute”)print scrollcurs.fetchone()print scrollcurs.fetchone()scrollcurs.scroll(-5,”relative”)scrollcurs.scroll(-5,”relative”)print scrollcurs.fetchone()print scrollcurs.fetchone()

18Developing Informix Applications in Python

Date and Datetime Columns/Date and Datetime Columns/Accessing SQLERRDAccessing SQLERRD

InformixDB uses Python’s datetime module for handling date and InformixDB uses Python’s datetime module for handling date and datetime values.datetime values.

import datetimeimport datetimetoday = datetime.date.today()today = datetime.date.today()cur.execute(“””cur.execute(“”” insert into orders(customer_num,order_date)insert into orders(customer_num,order_date) values(?,?)values(?,?)“””“””, [104,today] ), [104,today] )

# Read out sqlerrd to get the serial number of the newly inserted order# Read out sqlerrd to get the serial number of the newly inserted orderprint cur.sqlerrdprint cur.sqlerrd

19Developing Informix Applications in Python

Error HandlingError Handling

By default, error conditions raise Python exceptions:By default, error conditions raise Python exceptions:

my_sqlcode = 0my_sqlcode = 0try:try: cur.execute("sleect * from systables")cur.execute("sleect * from systables")except informixdb.Error, e:except informixdb.Error, e: my_sqlcode = e.sqlcodemy_sqlcode = e.sqlcodeprint my_sqlcodeprint my_sqlcode

An An errorhandlererrorhandler callback can be set up to customize error handling. callback can be set up to customize error handling.

20Developing Informix Applications in Python

Describing a QueryDescribing a Query

If a query produces a result set (e.g. SELECT), the cursor’s If a query produces a result set (e.g. SELECT), the cursor’s description description attribute describes the result columns. If a query attribute describes the result columns. If a query doesn’t produce a result set, doesn’t produce a result set, descriptiondescription is is NoneNone..

cur.execute("select * from systables")cur.execute("select * from systables")print cur.descriptionprint cur.description

21Developing Informix Applications in Python

Features Not CoveredFeatures Not Covered

● Interval types, date arithmetic, interval arithmeticInterval types, date arithmetic, interval arithmetic● Working with simple large objectsWorking with simple large objects● Working with opaque typesWorking with opaque types● Using smart large objects as file-like objectsUsing smart large objects as file-like objects

22Developing Informix Applications in Python

““Real World” ExampleReal World” Exampleimport informixdbimport informixdbfrom itertools import groupbyfrom itertools import groupbyfrom operator import attrgetterfrom operator import attrgetter

conn = informixdb.connect("stores_demo")conn = informixdb.connect("stores_demo")

def report_rows(start_date, end_date):def report_rows(start_date, end_date): main_cur = conn.cursor(rowformat=informixdb.ROW_AS_OBJECT)main_cur = conn.cursor(rowformat=informixdb.ROW_AS_OBJECT) main_cur.execute("""main_cur.execute(""" select customer.*, orders.*, items.*select customer.*, orders.*, items.* from customer, orders, itemsfrom customer, orders, items where items.order_num = orders.order_numwhere items.order_num = orders.order_num and customer.customer_num = orders.customer_numand customer.customer_num = orders.customer_num and orders.order_date between :start_date and :end_dateand orders.order_date between :start_date and :end_date order by customer.customer_num, orders.order_numorder by customer.customer_num, orders.order_num """, locals() )""", locals() ) for row in main_cur:for row in main_cur: row.unit_price = row.total_price / row.quantityrow.unit_price = row.total_price / row.quantity yield rowyield row

start_date = raw_input("Start Date: ")start_date = raw_input("Start Date: ")end_date = raw_input("End Date: ")end_date = raw_input("End Date: ")

cust_grouping = groupby(report_rows(start_date, end_date),cust_grouping = groupby(report_rows(start_date, end_date), attrgetter("customer_num", "fname", "lname"))attrgetter("customer_num", "fname", "lname"))for (cust_code, fname, lname), cust_rows in cust_grouping:for (cust_code, fname, lname), cust_rows in cust_grouping: cust_total = 0cust_total = 0 print "Customer: %s - %s %s" % (cust_code, fname, lname)print "Customer: %s - %s %s" % (cust_code, fname, lname) printprint ord_grouping = groupby(cust_rows, attrgetter("order_num", "order_date"))ord_grouping = groupby(cust_rows, attrgetter("order_num", "order_date")) for (order_num, order_date), ord_rows in ord_grouping:for (order_num, order_date), ord_rows in ord_grouping: print " Order: %-10s Order Date: %s" % (order_num,print " Order: %-10s Order Date: %s" % (order_num, order_date.strftime("%m/%d/%Y"))order_date.strftime("%m/%d/%Y")) printprint for row in ord_rows:for row in ord_rows: print " %-10s %5d %7s %7s" % (print " %-10s %5d %7s %7s" % ( row.manu_code+str(row.stock_num), row.quantity, row.unit_price,row.manu_code+str(row.stock_num), row.quantity, row.unit_price, row.total_price)row.total_price) cust_total += row.total_pricecust_total += row.total_price printprint print "Customer Total: %s" % cust_totalprint "Customer Total: %s" % cust_total print "-"*80print "-"*80 printprint

23Developing Informix Applications in Python

ConclusionConclusion

Thanks to its versatility and agility, Python is an attractive and productive programming language for developing Informix applications - and Python is fun!

24Developing Informix Applications in Python

LinksLinks

● Python’s Home PagePython’s Home Pagehttp://www.python.org/http://www.python.org/

● Information on DB Programming in PythonInformation on DB Programming in Pythonhttp://www.python.org/topics/database/http://www.python.org/topics/database/

● Python DB-API specification, version 2.0Python DB-API specification, version 2.0http://www.python.org/peps/pep-0249.htmlhttp://www.python.org/peps/pep-0249.html

● InformixDB on SourceforgeInformixDB on Sourceforgehttp://informixdb.sourceforge.net/http://informixdb.sourceforge.net/

25Developing Informix Applications in Python

AcknowledgmentsAcknowledgments

● Guido van Rossum and many othersGuido van Rossum and many othersfor creating Python,for creating Python,

● Greg Stein, Michael Lorton, Bertil Reinhammar, and Greg Stein, Michael Lorton, Bertil Reinhammar, and Stephen J. Turner,Stephen J. Turner,for originally developing and maintaining for originally developing and maintaining InformixDBInformixDB

● Daniel Smertnig,Daniel Smertnig,for contributing improvements to InformixDBfor contributing improvements to InformixDB

26Developing Informix Applications in Python

Developing InformixApplications in Python

Carsten HaeseUnique Systems, Inc.

Informix Forum 2006Washington, DC

December 8-9, 2006

Recommended