diff --git a/doc/src/sgml/datetime.sgml b/doc/src/sgml/datetime.sgml index f237bdea253d59ee939f959594b679a0bd5bf9ea..d7a76dab2331d17e123f6bdce2c30bb5d1b03a44 100644 --- a/doc/src/sgml/datetime.sgml +++ b/doc/src/sgml/datetime.sgml @@ -1,5 +1,5 @@ @@ -210,7 +210,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/datetime.sgml,v 2.30 2003/03/13 01:30:27 pe - + Date/Time Key Words diff --git a/doc/src/sgml/jdbc.sgml b/doc/src/sgml/jdbc.sgml index 817a0a7e2f66158a152e495df29fd96c588b1c86..525cc07175ded378fccc1a4e70149f6bfbe5b62f 100644 --- a/doc/src/sgml/jdbc.sgml +++ b/doc/src/sgml/jdbc.sgml @@ -1,18 +1,10 @@ <acronym>JDBC</acronym> Interface - - Author - - Originally written by Peter T. Mount (peter@retep.org.uk), - the original author of the JDBC driver. - - - JDBC is a core API of Java 1.1 and later. It provides a standard set of @@ -21,7 +13,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/Attic/jdbc.sgml,v 1.43 2003/03/25 16:15:37 PostgreSQL provides a type - 4 JDBC Driver. Type 4 indicates + 4 JDBC driver. Type 4 indicates that the driver is written in Pure Java, and communicates in the database system's own network protocol. Because of this, the driver is platform independent; once compiled, the driver can be used on @@ -33,13 +25,17 @@ $Header: /cvsroot/pgsql/doc/src/sgml/Attic/jdbc.sgml,v 1.43 2003/03/25 16:15:37 JDBC programming, but should help to get you started. For more information refer to the standard JDBC API documentation. - Also, take a look at the examples included with the source. The - basic example is used here. + Also, take a look at the examples included with the source. Setting up the <acronym>JDBC</acronym> Driver + + This section describes the steps you need to take before you can + write or run programs that use the JDBC interface. + + Getting the Driver @@ -71,8 +67,8 @@ $Header: /cvsroot/pgsql/doc/src/sgml/Attic/jdbc.sgml,v 1.43 2003/03/25 16:15:37 To use the driver, the JAR archive (named postgresql.jar if you built from source, otherwise - it will likely be named jdbc7.2-1.1.jar or - jdbc7.2-1.2.jar for the JDBC 1 and JDBC 2 versions + it will likely be named jdbc&majorversion;-1.1.jar or + jdbc&majorversion;-1.2.jar for the JDBC 1 and JDBC 2 versions respectively) needs to be included in the class path, either by putting it in the CLASSPATH @@ -80,26 +76,18 @@ $Header: /cvsroot/pgsql/doc/src/sgml/Attic/jdbc.sgml,v 1.43 2003/03/25 16:15:37 java command line. - - For instance, I have an application that uses the - JDBC driver to access a large database - containing astronomical objects. I have the application and the - JDBC driver installed in the /usr/local/lib directory, and the - Java JDK installed in - /usr/local/jdk1.3.1. To run the - application, I would use: + + For instance, assume we have an application that uses the + JDBC driver to access a database, and that + application is installed as + /usr/local/lib/myapp.jar. The PostgreSQL + JDBC driver installed as + /usr/local/pgsql/share/java/postgresql.jar. To run + the application, we would use: -export CLASSPATH=/usr/local/lib/finder.jar:/usr/local/pgsql/share/java/postgresql.jar:. -java Finder +export CLASSPATH=/usr/local/lib/myapp.jar:/usr/local/pgsql/share/java/postgresql.jar:. +java MyApp - - - - finder.jar contains the Finder application. - - - @@ -109,7 +97,7 @@ java Finder - Preparing the Database for <acronym>JDBC</acronym> + Preparing the Database Server for <acronym>JDBC</acronym> Because Java only uses TCP/IP connections, the @@ -125,14 +113,19 @@ java Finder Also, the client authentication setup in the pg_hba.conf file may need to be configured. Refer to for details. The - JDBC Driver supports the trust, ident, + JDBC driver supports the trust, ident, password, md5, and crypt authentication methods. - Using the Driver + Initializing the Driver + + + This section describes how to load and initialize the JDBC driver + in your programs. + Importing <acronym>JDBC</acronym> @@ -144,15 +137,15 @@ java Finder import java.sql.*; + - + Do not import the org.postgresql package. If you do, your source will not compile, as javac will get confused. - - + @@ -175,6 +168,7 @@ Class.forName("org.postgresql.Driver"); This will load the driver, and while loading, the driver will automatically register itself with JDBC. + @@ -183,7 +177,6 @@ Class.forName("org.postgresql.Driver"); not available. - This is the most common method to use, but restricts your code to @@ -250,7 +243,7 @@ jdbc:postgresql://host: - where: + The parametes have the following meanings: @@ -292,7 +285,7 @@ jdbc:postgresql://host: To connect, you need to get a Connection instance from JDBC. To do this, - you would use the DriverManager.getConnection() method: + you use the DriverManager.getConnection() method: Connection db = DriverManager.getConnection(url, username, password); @@ -336,8 +329,8 @@ db.close(); column of each row using a Statement. Statement st = db.createStatement(); -ResultSet rs = st.executeQuery("SELECT * FROM mytable where columnfoo = 500"); -while(rs.next()) { +ResultSet rs = st.executeQuery("SELECT * FROM mytable WHERE columnfoo = 500"); +while (rs.next()) { System.out.print("Column 1 returned "); System.out.println(rs.getString(1)); } @@ -347,15 +340,15 @@ st.close(); - This example will issue the same query as before using + This example issues the same query as before but uses a PreparedStatement and a bind value in the query. int foovalue = 500; -PreparedStatement st = db.prepareStatement("SELECT * FROM mytable where columnfoo = ?"); +PreparedStatement st = db.prepareStatement("SELECT * FROM mytable WHERE columnfoo = ?"); st.setInt(1, foovalue); ResultSet rs = st.executeQuery(); -while(rs.next()) { +while (rs.next()) { System.out.print("Column 1 returned "); System.out.println(rs.getString(1)); } @@ -366,8 +359,7 @@ st.close(); - Using the <classname>Statement</classname> or <classname> - PreparedStatement</classname> Interface + Using the <classname>Statement</classname> or <classname>PreparedStatement</classname> Interface The following must be considered when using the @@ -463,10 +455,10 @@ st.close(); ResultSet is currently read only. You can not update data through the ResultSet. - If you want to update data you need to do it the old fashioned way - by issuing a SQL update statement. This is + If you want to update data you need to do it the normal way + by using the SQL statement UPDATE. This is in conformance with the JDBC specification - which does not require drivers to provide this functionality. + which does not require drivers to provide updatable result sets. @@ -478,23 +470,25 @@ st.close(); Performing Updates - To change data (perform an insert, update, or delete) - you use the executeUpdate() method. - executeUpdate() is similar to the - executeQuery() used to issue a select, - however it doesn't return a ResultSet, - instead it returns the number of records affected by the insert, - update, or delete statement. + To change data (perform an INSERT, + UPDATE, or DELETE) you use + the executeUpdate() method. This method is + similar to the method executeQuery() used to + issue a SELECT statement, but it doesn't return + a ResultSet; instead it returns the number + of rows affected by the INSERT, + UPDATE, or DELETE statement. + illustrates the usage. - Simple Delete Example + Deleting Rows in <acronym>JDBC</acronym> - This example will issue a simple delete and print out the number - of rows deleted. + This example will issue a simple DELETE + statement and print out the number of rows deleted. int foovalue = 500; -PreparedStatement st = db.prepareStatement("DELETE FROM mytable where columnfoo = ?"); +PreparedStatement st = db.prepareStatement("DELETE FROM mytable WHERE columnfoo = ?"); st.setInt(1, foovalue); int rowsDeleted = st.executeUpdate(); System.out.println(rowsDeleted + " rows deleted"); @@ -509,19 +503,19 @@ st.close(); To create, modify or drop a database object like a table or view - you use the execute() method. - execute is similar to the - executeQuery() used to issue a select, - however it doesn't return a result. + you use the execute() method. This method is + similar to the method executeQuery(), but it + doesn't return a result. + illustrates the usage. - Drop Table Example + Dropping a Table in JDBC This example will drop a table. Statement st = db.createStatement(); -ResultSet rs = st.executeQuery("DROP TABLE mytable"); +st.execute("DROP TABLE mytable"); st.close(); @@ -534,11 +528,10 @@ st.close(); PostgreSQL provides two distinct ways to store binary data. Binary data can be stored in a table using - PostgreSQL's binary data type - bytea, or by using the Large Object + the data type bytea or by using the Large Object feature which stores the binary data in a separate table in a special - format, and refers to that table by storing a value of type - OID in your table. + format and refers to that table by storing a value of type + oid in your table. @@ -547,32 +540,32 @@ st.close(); bytea data type is not well suited for storing very large amounts of binary data. While a column of type bytea can hold up to 1 GB of binary data, it would - require a huge amount of memory (RAM) to + require a huge amount of memory to process such a large value. The Large Object method for storing binary data is better suited to storing very large values, but it has its own limitations. Specifically deleting a row - that contains a Large Object does not delete the Large Object. + that contains a Large Object reference does not delete the Large Object. Deleting the Large Object is a separate operation that needs to be performed. Large Objects also have some security - issues since anyone connected to the database case view + issues since anyone connected to the database cann view and/or modify any Large Object, even if they don't have - permissions to view/update the row containing the Large Object. + permissions to view/update the row containing the Large Object reference. - 7.2 is the first release of the JDBC Driver + Version 7.2 was the first release of the JDBC driver that supports the bytea data type. The introduction of this functionality in 7.2 has introduced a change in behavior - as compared to previous releases. In 7.2 the methods + as compared to previous releases. Since 7.2, the methods getBytes(), setBytes(), getBinaryStream(), and setBinaryStream() operate on - the bytea data type. In 7.1 these methods operated - on the OID data type associated with Large Objects. + the bytea data type. In 7.1 and earlier, these methods operated + on the oid data type associated with Large Objects. It is possible to revert the driver back to the old 7.1 behavior - by setting the compatible property on - the Connection to a value of - 7.1 + by setting the property compatible on + the Connection object to the value + 7.1. @@ -584,39 +577,44 @@ st.close(); To use the Large Object functionality you can use either the - LargeObject API + LargeObject class provided by the PostgreSQL - JDBC Driver, or by using the + JDBC driver, or by using the getBLOB() and setBLOB() methods. - For PostgreSQL, you must access Large - Objects within an SQL transaction. You would - open a transaction by using the - setAutoCommit() method with an input - parameter of false. + You must access Large Objects within an SQL + transaction block. You can start a transaction block by calling + setAutoCommit(false). - In a future release of the - JDBC Driver, the getBLOB() + + + In a future release of the + JDBC driver, the getBLOB() and setBLOB() methods may no longer - interact with Large Objects and will instead work on - bytea data types. So it is recommended that you + interact with Large Objects and will instead work on the data type + bytea. So it is recommended that you use the LargeObject API if you intend to use Large Objects. - + + + + contains some examples on + how to process binary data using the PostgreSQL JDBC driver. + - Binary Data Examples + Processing Binary Data in JDBC - For example, suppose you have a table containing the file name of - an image and you also want to store the image in a bytea + For example, suppose you have a table containing the file names of + images and you also want to store the image in a bytea column: CREATE TABLE images (imgname text, img bytea); @@ -649,13 +647,13 @@ fis.close(); Statement class can equally be used.) -PreparedStatement ps = con.prepareStatement("SELECT img FROM images WHERE imgname=?"); +PreparedStatement ps = con.prepareStatement("SELECT img FROM images WHERE imgname = ?"); ps.setString(1, "myimage.gif"); ResultSet rs = ps.executeQuery(); if (rs != null) { - while(rs.next()) { + while (rs.next()) { byte[] imgBytes = rs.getBytes(1); - // use the stream in some way here + // use the data in some way here } rs.close(); } @@ -674,76 +672,76 @@ ps.close(); the LargeObject API to store the file: -CREATE TABLE imagesLO (imgname text, imgOID OID); +CREATE TABLE imageslo (imgname text, imgoid oid); To insert an image, you would use: -// All LargeObject API calls must be within a transaction +// All LargeObject API calls must be within a transaction block conn.setAutoCommit(false); // Get the Large Object Manager to perform operations with LargeObjectManager lobj = ((org.postgresql.PGConnection)conn).getLargeObjectAPI(); -//create a new large object +// Create a new large object int oid = lobj.create(LargeObjectManager.READ | LargeObjectManager.WRITE); -//open the large object for write +// Open the large object for writing LargeObject obj = lobj.open(oid, LargeObjectManager.WRITE); // Now open the file File file = new File("myimage.gif"); FileInputStream fis = new FileInputStream(file); -// copy the data from the file to the large object +// Copy the data from the file to the large object byte buf[] = new byte[2048]; int s, tl = 0; -while ((s = fis.read(buf, 0, 2048)) > 0) -{ - obj.write(buf, 0, s); - tl += s; +while ((s = fis.read(buf, 0, 2048)) > 0) { + obj.write(buf, 0, s); + tl += s; } // Close the large object obj.close(); -//Now insert the row into imagesLO -PreparedStatement ps = conn.prepareStatement("INSERT INTO imagesLO VALUES (?, ?)"); +// Now insert the row into imageslo +PreparedStatement ps = conn.prepareStatement("INSERT INTO imageslo VALUES (?, ?)"); ps.setString(1, file.getName()); ps.setInt(2, oid); ps.executeUpdate(); ps.close(); fis.close(); - + + Retrieving the image from the Large Object: -// All LargeObject API calls must be within a transaction +// All LargeObject API calls must be within a transaction block conn.setAutoCommit(false); // Get the Large Object Manager to perform operations with LargeObjectManager lobj = ((org.postgresql.PGConnection)conn).getLargeObjectAPI(); -PreparedStatement ps = con.prepareStatement("SELECT imgOID FROM imagesLO WHERE imgname=?"); +PreparedStatement ps = con.prepareStatement("SELECT imgoid FROM imageslo WHERE imgname = ?"); ps.setString(1, "myimage.gif"); ResultSet rs = ps.executeQuery(); if (rs != null) { - while(rs.next()) { - //open the large object for reading - int oid = rs.getInt(1); - LargeObject obj = lobj.open(oid, LargeObjectManager.READ); - - //read the data - byte buf[] = new byte[obj.size()]; - obj.read(buf, 0, obj.size()); - //do something with the data read here - - // Close the object - obj.close(); + while (rs.next()) { + // Open the large object for reading + int oid = rs.getInt(1); + LargeObject obj = lobj.open(oid, LargeObjectManager.READ); + + // Read the data + byte buf[] = new byte[obj.size()]; + obj.read(buf, 0, obj.size()); + // Do something with the data read here + + // Close the object + obj.close(); } rs.close(); } @@ -2441,93 +2439,78 @@ public void unlink(int oid) throws SQLException - Using the driver in a multithreaded or a servlet environment + Using the Driver in a Multithreaded or a Servlet Environment A problem with many JDBC drivers is that only one thread can use a Connection at any one - time -- otherwise a thread could send a query while another one is - receiving results, and this would be a bad thing for the database - engine. + time --- otherwise a thread could send a query while another one is + receiving results, and this could cause severe confusion. - The PostgreSQL JDBC Driver + The PostgreSQL JDBC driver is thread safe. Consequently, if your application uses multiple threads then you do - not have to worry about complex algorithms to ensure that only one - uses the database at any time. + not have to worry about complex algorithms to ensure that only one thread + uses the database at a time. If a thread attempts to use the connection while another one is using it, it will wait until the other thread has finished its - current operation. If it is a regular SQL + current operation. If the operation is a regular SQL statement, then the operation consists of sending the statement and retrieving any ResultSet (in full). If it - is a Fastpath call (e.g., reading a block - from a LargeObject) then it is the time to - send and retrieve that block. + is a fast-path call (e.g., reading a block + from a large object) then it consists of + sending and retrieving the respective data. This is fine for applications and applets but can cause a - performance problem with servlets. With servlets you can have a - heavy load on the connection. If you have several threads - performing queries then each but one will pause, which may not be what - you are after. - - - - To solve this, you would be advised to create a pool of - connections. When ever a thread needs to use the database, it asks - a manager class for a Connection. The - manager hands a free connection to the thread and marks it as - busy. If a free connection is not available, it opens one. Once - the thread has finished with it, it returns it to the manager who - can then either close it or add it to the pool. The manager would - also check that the connection is still alive and remove it from - the pool if it is dead. - - - - So, with servlets, it is up to you to use either a single - connection, or a pool. The plus side for a pool is that threads - will not be hit by the bottle neck caused by a single network - connection. The down side is that it increases the load on the - server, as a backend process is created for each - Connection. It is up to you and your - applications requirements. + performance problem with servlets. If you have several threads + performing queries then each but one will pause. + To solve this, you are advised to create a pool of connections. + When ever a thread needs to use the database, it asks a manager + class for a Connection object. The manager + hands a free connection to the thread and marks it as busy. If a + free connection is not available, it opens one. Once the thread + has finished using the connection, it returns it to the manager + which can then either close it or add it to the pool. The manager + would also check that the connection is still alive and remove it + from the pool if it is dead. The down side of a connection pool is + that it increases the load on the server because a new session is + created for each Connection object. It is + up to you and your applications' requirements. - Connection Pools And DataSources - - - JDBC, JDK Version Support + Connection Pools and Data Sources - + JDBC 2 introduced standard connection pooling features in an add-on API known as the JDBC 2.0 Optional Package (also known as the JDBC 2.0 Standard Extension). These features have since been included in the core JDBC 3 API. The PostgreSQL - JDBC drivers support these features with + JDBC drivers support these features if it has been compiled with JDK 1.3.x in combination with the JDBC 2.0 Optional Package - (JDBC 2), or with JDK 1.4+ + (JDBC 2), or with JDK 1.4 or higher (JDBC 3). Most application servers include the JDBC 2.0 Optional Package, but it is also available separately from the Sun JDBC download site. - - JDBC Connection Pooling API - The JDBC API provides a client + Overview + + + The JDBC API provides a client and a server interface for connection pooling. The client interface is javax.sql.DataSource, which is what application code will typically use to @@ -2535,16 +2518,22 @@ public void unlink(int oid) throws SQLException is javax.sql.ConnectionPoolDataSource, which is how most application servers will interface with the PostgreSQL JDBC - driver. - In an application server environment, the + driver. + + + + In an application server environment, the application server configuration will typically refer to the PostgreSQL ConnectionPoolDataSource implementation, while the application component code will typically acquire a DataSource implementation provided by the application server (not by - PostgreSQL). - In an environment without an application server, + PostgreSQL). + + + + For an environment without an application server, PostgreSQL provides two implementations of DataSource which an application can use directly. One implementation performs connection pooling, @@ -2553,19 +2542,23 @@ public void unlink(int oid) throws SQLException any pooling. Again, these implementations should not be used in an application server environment unless the application server does not support the - ConnectionPoolDataSource interface. + ConnectionPoolDataSource interface. + - Application Servers: ConnectionPoolDataSource - PostgreSQL includes one - implementation of ConnectionPoolDataSource - for JDBC 2, and one for - JDBC 3: + Application Servers: <classname>ConnectionPoolDataSource</classname> + + + PostgreSQL includes one implementation + of ConnectionPoolDataSource for + JDBC 2 and one for JDBC 3, + as shown in . + - - ConnectionPoolDataSource Implementations +
+ <classname>ConnectionPoolDataSource</classname> Implementations @@ -2587,15 +2580,18 @@ public void unlink(int oid) throws SQLException -
+ - Both implementations use the same configuration scheme. - JDBC requires that a - ConnectionPoolDataSource be configured via - JavaBean properties, so there are get and set methods for each of - these properties: - - ConnectionPoolDataSource Configuration Properties + + Both implementations use the same configuration scheme. + JDBC requires that a + ConnectionPoolDataSource be configured via + JavaBean properties, shown in , + so there are get and set methods for each of these properties. + + +
+ <classname>ConnectionPoolDataSource</> Configuration Properties @@ -2608,97 +2604,98 @@ public void unlink(int oid) throws SQLException - serverName - String + serverName + String PostgreSQL database server - hostname + host name - databaseName - String + databaseName + String PostgreSQL database name - portNumber - int - TCP/IP port which the - PostgreSQL database server is - listening on (or 0 to use the default port) + portNumber + int + + TCP port which the PostgreSQL + database server is listening on (or 0 to use the default port) + - user - String + user + String User used to make database connections - password - String + password + String Password used to make database connections - defaultAutoCommit - boolean - Whether connections should have autoCommit - enabled or disabled when they are supplied to the - caller. The default is false, to - disable autoCommit. + defaultAutoCommit + boolean + + Whether connections should have autocommit enabled or disabled + when they are supplied to the caller. The default is + false, to disable autocommit. + -
- - Many application servers use a properties-style syntax to - configure these properties, so it would not be unusual to - enter properties as a block of text. + - - <literal>ConnectionPoolDataSource</literal> Configuration Example - - - If the application server provides a single area to enter all - the properties, they might be listed like this: + + Many application servers use a properties-style syntax to + configure these properties, so it would not be unusual to enter + properties as a block of text. If the application server provides + a single area to enter all the properties, they might be listed + like this: serverName=localhost databaseName=test user=testuser password=testpassword - Or, separated by semicolons instead of newlines, like this: + Or, if semicolons are used as separators instead of newlines, it + could look like this: serverName=localhost;databaseName=test;user=testuser;password=testpassword - - +
- Applications: DataSource + Applications: <classname>DataSource</> + PostgreSQL includes two implementations of DataSource - for JDBC 2, and two for JDBC - 3. The pooling implementations do not actually close connections + for JDBC 2 and two for JDBC + 3, as shown in . + The pooling implementations do not actually close connections when the client calls the close method, but instead return the connections to a pool of available connections for other clients to use. This avoids any overhead of repeatedly opening and closing connections, and allows a large number of clients to share a small number of database connections. - The pooling datasource implementation provided here is not + The pooling data-source implementation provided here is not the most feature-rich in the world. Among other things, connections are never closed until the pool itself is closed; there is no way to shrink the pool. As well, connections requested for users other than the default configured user are not pooled. Many application servers - provide more advanced pooling features, and use the + provide more advanced pooling features and use the ConnectionPoolDataSource implementation instead. - - DataSource Implementations + +
+ <classname>DataSource</> Implementations @@ -2738,14 +2735,16 @@ serverName=localhost;databaseName=test;user=testuser;password=testpassword
- All the implementations use the same configuration scheme. + + All the implementations use the same configuration scheme. JDBC requires that a - DataSource be configured via - JavaBean properties, so there are get and set methods for each of - these properties. + DataSource be configured via JavaBean + properties, shown in , so there + are get and set methods for each of these properties. + - - DataSource Configuration Properties +
+ <classname>DataSource</> Configuration Properties @@ -2758,35 +2757,35 @@ serverName=localhost;databaseName=test;user=testuser;password=testpassword - serverName - String + serverName + String PostgreSQL database server - hostname + host name - databaseName - String + databaseName + String PostgreSQL database name - portNumber - int - TCP/IP port which the + portNumber + int + TCP port which the PostgreSQL database server is listening on (or 0 to use the default port) - user - String + user + String User used to make database connections - password - String + password + String Password used to make database connections @@ -2794,10 +2793,10 @@ serverName=localhost;databaseName=test;user=testuser;password=testpassword
The pooling implementations require some additional - configuration properties: + configuration properties, which are shown in .
- - Additional Pooling DataSource Configuration Properties +
+ Additional Pooling <classname>DataSource</> Configuration Properties @@ -2810,22 +2809,22 @@ serverName=localhost;databaseName=test;user=testuser;password=testpassword - dataSourceName - String + dataSourceName + String Every pooling DataSource must have a - unique name + unique name. - initialConnections - int + initialConnections + int The number of database connections to be created when the pool is initialized. - maxConnections - int + maxConnections + int The maximum number of open database connections to allow. When more connections are requested, the caller will hang until a connection is returned to the pool. @@ -2834,14 +2833,14 @@ serverName=localhost;databaseName=test;user=testuser;password=testpassword
- Here's an example of typical application code using a - pooling DataSource: + shows an example of typical application code using a + pooling DataSource. - + <literal>DataSource</literal> Code Example - Code to initialize a pooling DataSource might look like this: + Code to initialize a pooling DataSource might look like this: Jdbc3PoolingDataSource source = new Jdbc3PoolingDataSource(); source.setDataSourceName("A Data Source"); @@ -2853,18 +2852,18 @@ source.setMaxConnections(10); Then code to use a connection from the pool might look like this. Note that it is critical that the connections - are closed, or else the pool will "leak" connections, and - eventually lock all the clients out. + are eventually closed. Else the pool will leak connections and + will eventually lock all the clients out. Connection con = null; try { con = source.getConnection(); // use connection -} catch(SQLException e) { +} catch (SQLException e) { // log error } finally { - if(con != null) { - try {con.close();}catch(SQLException e) {} + if (con != null) { + try { con.close(); } catch (SQLException e) {} } } @@ -2873,34 +2872,44 @@ try {
- DataSources and <acronym>JNDI</acronym> - All the ConnectionPoolDataSource and + Data Sources and <acronym>JNDI</acronym> + + + All the ConnectionPoolDataSource and DataSource implementations can be stored - in JNDI. In the case of the non-pooling + in JNDI. In the case of the nonpooling implementations, a new instance will be created every time the object is retrieved from JNDI, with the - same settings as the instance which was stored. For the + same settings as the instance that was stored. For the pooling implementations, the same instance will be retrieved - as long as it is available (e.g. not a different + as long as it is available (e.g., not a different JVM retrieving the pool from JNDI), or a new instance with the same - settings created otherwise. - In the application server environment, typically the + settings created otherwise. + + + + In the application server environment, typically the application server's DataSource instance will be stored in JNDI, instead of the PostgreSQL ConnectionPoolDataSource implementation. - - In an application environment, the application may store + + + + In an application environment, the application may store the DataSource in JNDI so that it doesn't have to make a reference to the DataSource available to all application - components that may need to use it: + components that may need to use it. An example of this is + shown in . + + - <literal>DataSource</literal> <acronym>JNDI</acronym> Code Example + <classname>DataSource</classname> <acronym>JNDI</acronym> Code Example - Application code to initialize a pooling DataSource and add + Application code to initialize a pooling DataSource and add it to JNDI might look like this: Jdbc3PoolingDataSource source = new Jdbc3PoolingDataSource(); @@ -2920,13 +2929,13 @@ try { DataSource source = (DataSource)new InitialContext().lookup("DataSource"); con = source.getConnection(); // use connection -} catch(SQLException e) { +} catch (SQLException e) { // log error -} catch(NamingException e) { +} catch (NamingException e) { // DataSource wasn't found in JNDI } finally { - if(con != null) { - try {con.close();}catch(SQLException e) {} + if (con != null) { + try { con.close(); } catch (SQLException e) {} } } @@ -2934,30 +2943,25 @@ try { - - Specific Application Server Configurations - Configuration examples for specific application servers - will be included here. -
Further Reading - If you have not yet read it, I'd advise you read the + If you have not yet read it, you are advised you read the JDBC API Documentation - (supplied with Sun's JDK), and the + (supplied with Sun's JDK) and the JDBC Specification. Both are available from http://java.sun.com/products/jdbc/index.html. + url="http://java.sun.com/products/jdbc/index.html">. http://jdbc.postgresql.org - contains updated information not included in this document, and - also includes precompiled drivers. + url="http://jdbc.postgresql.org"> + contains updated information not included in this chapter and + also offers precompiled drivers. diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml index b17892a4f669ab8ccdacf14ce625d76d8447591e..02d38bc53403442a5e665a54fe46ef02f8655835 100644 --- a/doc/src/sgml/plperl.sgml +++ b/doc/src/sgml/plperl.sgml @@ -1,5 +1,5 @@ @@ -34,8 +34,8 @@ $Header: /cvsroot/pgsql/doc/src/sgml/plperl.sgml,v 2.18 2002/09/21 18:32:53 pete Users of source packages must specially enable the build of - PL/Perl during the installation process (refer to the installation - instructions for more information). Users of binary packages + PL/Perl during the installation process. (Refer to the installation + instructions for more information.) Users of binary packages might find PL/Perl in a separate subpackage. @@ -57,8 +57,12 @@ CREATE FUNCTION funcname (argument-types Arguments and results are handled as in any other Perl subroutine: Arguments are passed in @_, and a result value is returned with return or as the last expression - evaluated in the function. For example, a function returning the - greater of two integer values could be defined as: + evaluated in the function. + + + + For example, a function returning the greater of two integer values + could be defined as: CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS ' @@ -145,7 +149,7 @@ SELECT name, empcomp(employee) FROM employee; Data Values in PL/Perl - The argument values supplied to a PL/Perl function's script are + The argument values supplied to a PL/Perl function's code are simply the input arguments converted to text form (just as if they had been displayed by a SELECT statement). Conversely, the return command will accept any string @@ -206,8 +210,8 @@ SELECT name, empcomp(employee) FROM employee; environment. This includes file handle operations, require, and use (for external modules). There is no way to access internals of the - database backend process or to gain OS-level access with the - permissions of the PostgreSQL user ID, + database server process or to gain OS-level access with the + permissions of the server process, as a C function can do. Thus, any unprivileged database user may be permitted to use this language. @@ -227,7 +231,7 @@ CREATE FUNCTION badfunc() RETURNS integer AS ' Sometimes it is desirable to write Perl functions that are not - restricted --- for example, one might want a Perl function that + restricted. For example, one might want a Perl function that sends mail. To handle these cases, PL/Perl can also be installed as an untrusted language (usually called PL/PerlU). In this case the full Perl language is @@ -255,7 +259,7 @@ CREATE FUNCTION badfunc() RETURNS integer AS ' The following features are currently missing from PL/Perl, but they - would make welcome contributions: + would make welcome contributions. diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index 84862b041d11c126026c72965c982a7446189035..38306e100b2d15b09d31a1e27418a9066eae7789 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,5 +1,5 @@ @@ -10,22 +10,11 @@ $Header: /cvsroot/pgsql/doc/src/sgml/plpgsql.sgml,v 1.16 2003/03/25 16:15:37 pet - PL/pgSQL is a loadable procedural language for the - PostgreSQL database system. - - - - This package was originally written by Jan Wieck. This - documentation was in part written - by Roberto Mello (rmello@fslc.usu.edu). - + PL/pgSQL is a loadable procedural + language for the PostgreSQL database + system. The design goals of PL/pgSQL were to create + a loadable procedural language that - - Overview - - - The design goals of PL/pgSQL were to create a loadable procedural - language that @@ -44,7 +33,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/plpgsql.sgml,v 1.16 2003/03/25 16:15:37 pet - inherits all user defined types, functions and operators, + inherits all user-defined types, functions, and operators, @@ -59,57 +48,61 @@ $Header: /cvsroot/pgsql/doc/src/sgml/plpgsql.sgml,v 1.16 2003/03/25 16:15:37 pet + + + Overview + The PL/pgSQL call handler parses the function's source text and produces an internal binary instruction tree the first time the - function is called (within any one backend process). The instruction tree + function is called (within each session). The instruction tree fully translates the PL/pgSQL statement structure, but individual - SQL expressions and SQL queries + SQL expressions and SQL commands used in the function are not translated immediately. - As each expression and SQL query is first used + As each expression and SQL command is first used in the function, the PL/pgSQL interpreter creates a prepared execution plan (using the SPI manager's SPI_prepare and SPI_saveplan functions). Subsequent visits - to that expression or query re-use the prepared plan. Thus, a + to that expression or command reuse the prepared plan. Thus, a function with conditional code that contains many statements for which execution plans might be required will only prepare and save those plans that are really used during the lifetime of the database connection. This can substantially reduce the total - amount of time required to parse, and generate query plans for the - statements in a procedural language function. A disadvantage is - that errors in a specific expression or query may not be detected + amount of time required to parse, and generate execution plans for the + statements in a PL/pgSQL function. A disadvantage is + that errors in a specific expression or command may not be detected until that part of the function is reached in execution. - Once PL/pgSQL has made a query plan for a particular - query in a function, it will re-use that plan for the life of the + Once PL/pgSQL has made an execution plan for a particular + command in a function, it will reuse that plan for the life of the database connection. This is usually a win for performance, but it can cause some problems if you dynamically alter your database schema. For example: -CREATE FUNCTION populate() RETURNS INTEGER AS ' +CREATE FUNCTION populate() RETURNS integer AS ' DECLARE - -- Declarations + -- declarations BEGIN PERFORM my_function(); END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; If you execute the above function, it will reference the OID for - my_function() in the query plan produced for + my_function() in the execution plan produced for the PERFORM statement. Later, if you - drop and re-create my_function(), then + drop and recreate my_function(), then populate() will not be able to find my_function() anymore. You would then have to - re-create populate(), or at least start a new + recreate populate(), or at least start a new database session so that it will be compiled afresh. Another way to avoid this problem is to use CREATE OR REPLACE FUNCTION when updating the definition of @@ -119,13 +112,13 @@ END; Because PL/pgSQL saves execution plans - in this way, queries that appear directly in a + in this way, SQL commands that appear directly in a PL/pgSQL function must refer to the - same tables and fields on every execution; that is, you cannot use - a parameter as the name of a table or field in a query. To get - around this restriction, you can construct dynamic queries using + same tables and columns on every execution; that is, you cannot use + a parameter as the name of a table or column in an SQL command. To get + around this restriction, you can construct dynamic commands using the PL/pgSQL EXECUTE - statement --- at the price of constructing a new query plan on + statement --- at the price of constructing a new execution plan on every execution. @@ -134,7 +127,7 @@ END; The PL/pgSQL EXECUTE statement is not related to the EXECUTE statement supported by the - PostgreSQL backend. The backend + PostgreSQL server. The server's EXECUTE statement cannot be used within PL/pgSQL functions (and is not needed). @@ -142,50 +135,27 @@ END; Except for input/output conversion and calculation functions - for user defined types, anything that can be defined in C language - functions can also be done with PL/pgSQL. It is possible to + for user-defined types, anything that can be defined in C language + functions can also be done with PL/pgSQL. For example, it is possible to create complex conditional computation functions and later use them to define operators or use them in functional indexes. + Advantages of Using <application>PL/pgSQL</application> - - - - Better performance (see ) - - - - - - SQL support (see ) - - - - - - Portability (see ) - - - - - - Better Performance - - SQL is the language - PostgreSQL (and most other relational databases) - use as query language. It's portable and easy to learn. But - every SQL statement must be executed - individually by the database server. + SQL is the language PostgreSQL + (and most other relational databases) use as query language. It's + portable and easy to learn. But every SQL + statement must be executed individually by the database server. That means that your client application must send each query to the database server, wait for it to process it, receive the results, do some computation, then send other queries to the - server. All this incurs inter-process communication and may also + server. All this incurs interprocess communication and may also incur network overhead if your client is on a different machine than the database server. @@ -199,51 +169,29 @@ END; communication overhead. This can make for a considerable performance increase. - - - - SQL Support - - - PL/pgSQL adds the power of a procedural language to the - flexibility and ease of SQL. With - PL/pgSQL you can use all the data types, columns, operators - and functions of SQL. - - - - - Portability - Because PL/pgSQL functions run inside - PostgreSQL, these functions will run on any - platform where PostgreSQL runs. Thus you can - reuse code and reduce development costs. + Also, with PL/pgSQL you can use all + the data types, operators and functions of SQL. - Developing in <application>PL/pgSQL</application> - Developing in PL/pgSQL is pretty - straight forward, especially if you have developed in other - database procedural languages, such as Oracle's - PL/SQL. One good way to develop in + One good way to develop in PL/pgSQL is to simply use the text editor of your choice to create your functions, and in another window, use - psql (PostgreSQL's interactive - monitor) to load those functions. If you are doing it this way, it + psql to load those functions. If you are doing it this way, it is a good idea to write the function using CREATE OR REPLACE FUNCTION. That way you can reload the file to update the function definition. For example: -CREATE OR REPLACE FUNCTION testfunc(INTEGER) RETURNS INTEGER AS ' +CREATE OR REPLACE FUNCTION testfunc(integer) RETURNS integer AS ' .... end; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; @@ -251,7 +199,7 @@ end; While running psql, you can load or reload such a function definition file with - \i filename.sql +\i filename.sql and then immediately issue SQL commands to test the function. @@ -261,22 +209,123 @@ end; GUI database access tool that facilitates development in a procedural language. One example of such as a tool is PgAccess, although others exist. These tools often - provide convenient features such as escaping single-quotes, and + provide convenient features such as escaping single quotes and making it easier to recreate and debug functions.
+ + Handling of Quotations Marks + + + Since the code of any procedural language function is specified + CREATE FUNCTION as a string literal, single + quotes inside the function body must be escaped. This can lead to + rather complicated code at times, especially if you are writing a + function that generates other functions, as in the example in . The list below gives + you an overview over the needed levels of quotation marks in + various situations. Keep this chart handy. + + + + + 1 quotation mark + + + To begin/end function bodies, for example: + +CREATE FUNCTION foo() RETURNS integer AS '...' + LANGUAGE plpgsql; + + + + + + + 2 quotation marks + + + For string literals inside the function body, for example: + +a_output := ''Blah''; +SELECT * FROM users WHERE f_name=''foobar''; + + The second line is interpreted as + +SELECT * FROM users WHERE f_name='foobar'; + + + + + + + 4 quotation marks + + + When you need a single quote in a string inside the function + body, for example: + +a_output := a_output || '' AND name LIKE ''''foobar'''' AND xyz'' + + The value of a_output would then be: + AND name LIKE 'foobar' AND xyz. + + + + + + 6 quotation marks + + + When a single quote in a string inside the function body is + adjacent to the end of that string constant, for example: + +a_output := a_output || '' AND name LIKE ''''foobar'''''' + + The value of a_output would then be: + AND name LIKE 'foobar'. + + + + + + 10 quotation marks + + + When you want two single quotes in a string constant (which + accounts for 8 quotes) and this is adjacent to the end of that + string constant (2 more). You will probably only need that if + you are writing a function that generates other functions. For + example: + +a_output := a_output || '' if v_'' || + referrer_keys.kind || '' like '''''''''' + || referrer_keys.key_string || '''''''''' + then return '''''' || referrer_keys.referrer_type + || ''''''; end if;''; + + The value of a_output would then be: + +if v_... like ''...'' then return ''...''; end if; + + + + + + + Structure of <application>PL/pgSQL</application> - - PL/pgSQL is a block - structured language. The complete text of a function - definition must be a block. A block is defined as: + + PL/pgSQL is a block-structured language. + The complete text of a function definition must be a + block. A block is defined as: - <<label>> + <<label>> DECLARE declarations BEGIN @@ -286,8 +335,28 @@ END; - Any statement in the statement section of a block - can be a sub-block. Sub-blocks can be used for + Each declaration and each statement within a block is terminated + by a semicolon. + + + + All key words and identifiers can be written in mixed upper and + lower case. Identifiers are implicitly converted to lower-case + unless double-quoted. + + + + There are two types of comments in PL/pgSQL. A double dash (--) + starts a comment that extends to the end of the line. A /* + starts a block comment that extends to the next occurrence of */. + Block comments cannot be nested, but double dash comments can be + enclosed into a block comment and a double dash can hide + the block comment delimiters /* and */. + + + + Any statement in the statement section of a block + can be a subblock. Subblocks can be used for logical grouping or to localize variables to a small group of statements. @@ -297,89 +366,65 @@ END; block are initialized to their default values every time the block is entered, not only once per function call. For example: -CREATE FUNCTION somefunc() RETURNS INTEGER AS ' +CREATE FUNCTION somefunc() RETURNS integer AS ' DECLARE - quantity INTEGER := 30; + quantity integer := 30; BEGIN - RAISE NOTICE ''Quantity here is %'',quantity; -- Quantity here is 30 - quantity := 50; - -- - -- Create a sub-block - -- - DECLARE - quantity INTEGER := 80; - BEGIN - RAISE NOTICE ''Quantity here is %'',quantity; -- Quantity here is 80 - END; - - RAISE NOTICE ''Quantity here is %'',quantity; -- Quantity here is 50 - - RETURN quantity; + RAISE NOTICE ''Quantity here is %'', quantity; -- Quantity here is 30 + quantity := 50; + -- + -- Create a subblock + -- + DECLARE + quantity integer := 80; + BEGIN + RAISE NOTICE ''Quantity here is %'', quantity; -- Quantity here is 80 + END; + + RAISE NOTICE ''Quantity here is %'', quantity; -- Quantity here is 50 + + RETURN quantity; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; - It is important not to confuse the use of BEGIN/END for + It is important not to confuse the use of BEGIN/END for grouping statements in PL/pgSQL with the database commands for - transaction control. PL/pgSQL's BEGIN/END are only for grouping; + transaction control. PL/pgSQL's BEGIN/END are only for grouping; they do not start or end a transaction. Functions and trigger procedures are always executed within a transaction established by an outer query --- they cannot start or commit transactions, since PostgreSQL does not have nested transactions. - - - Lexical Details - - - Each statement and declaration within a block is terminated - by a semicolon. - - - - All keywords and identifiers can be written in mixed upper- and - lower-case. Identifiers are implicitly converted to lower-case - unless double-quoted. - - - - There are two types of comments in PL/pgSQL. A double dash -- - starts a comment that extends to the end of the line. A /* - starts a block comment that extends to the next occurrence of */. - Block comments cannot be nested, but double dash comments can be - enclosed into a block comment and a double dash can hide - the block comment delimiters /* and */. - - Declarations - All variables, rows and records used in a block must be declared in the + All variables used in a block must be declared in the declarations section of the block. - (The only exception is that the loop variable of a FOR loop iterating + (The only exception is that the loop variable of a FOR loop iterating over a range of integer values is automatically declared as an integer variable.) PL/pgSQL variables can have any SQL data type, such as - INTEGER, VARCHAR and - CHAR. + integer, varchar, and + char. Here are some examples of variable declarations: -user_id INTEGER; -quantity NUMERIC(5); -url VARCHAR; +user_id integer; +quantity numeric(5); +url varchar; myrow tablename%ROWTYPE; -myfield tablename.fieldname%TYPE; +myfield tablename.columnname%TYPE; arow RECORD; @@ -389,104 +434,125 @@ arow RECORD; name CONSTANT type NOT NULL { DEFAULT | := } expression ; - - - - The DEFAULT clause, if given, specifies the initial value assigned - to the variable when the block is entered. If the DEFAULT clause + The DEFAULT clause, if given, specifies the initial value assigned + to the variable when the block is entered. If the DEFAULT clause is not given then the variable is initialized to the - SQL NULL value. - - - - The CONSTANT option prevents the variable from being assigned to, + SQL null value. + The CONSTANT option prevents the variable from being assigned to, so that its value remains constant for the duration of the block. - If NOT NULL - is specified, an assignment of a NULL value results in a run-time - error. All variables declared as NOT NULL - must have a non-NULL default value specified. + If NOT NULL + is specified, an assignment of a null value results in a run-time + error. All variables declared as NOT NULL + must have a nonnull default value specified. The default value is evaluated every time the block is entered. So, - for example, assigning 'now' to a variable of type + for example, assigning 'now' to a variable of type timestamp causes the variable to have the - time of the current function call, not when the function was + time of the current function call, not the time when the function was precompiled. Examples: -quantity INTEGER DEFAULT 32; +quantity integer DEFAULT 32; url varchar := ''http://mysite.com''; -user_id CONSTANT INTEGER := 10; +user_id CONSTANT integer := 10; Aliases for Function Parameters - -name ALIAS FOR $n; +name ALIAS FOR $n; - Parameters passed to functions are named with the identifiers $1, $2, - etc. Optionally, aliases can be declared for $n + etc. Optionally, aliases can be declared for $n parameter names for increased readability. Either the alias or the numeric identifier can then be used to refer to the parameter value. Some examples: -CREATE FUNCTION sales_tax(REAL) RETURNS REAL AS ' +CREATE FUNCTION sales_tax(real) RETURNS real AS ' DECLARE subtotal ALIAS FOR $1; BEGIN - return subtotal * 0.06; + RETURN subtotal * 0.06; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; -CREATE FUNCTION instr(VARCHAR,INTEGER) RETURNS INTEGER AS ' +CREATE FUNCTION instr(varchar, integer) RETURNS integer AS ' DECLARE v_string ALIAS FOR $1; index ALIAS FOR $2; BEGIN - -- Some computations here + -- some computations here END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; -CREATE FUNCTION use_many_fields(tablename) RETURNS TEXT AS ' +CREATE FUNCTION use_many_fields(tablename) RETURNS text AS ' DECLARE in_t ALIAS FOR $1; BEGIN RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; + + Copying Types + + +variable%TYPE + + + + %TYPE provides the data type of a variable or + table column. You can use this to declare variables that will hold + database values. For example, let's say you have a column named + user_id in your users + table. To declare a variable with the same data type as + users.user_id you write: + +user_id users.user_id%TYPE; + + + + + By using %TYPE you don't need to know the data + type of the structure you are referencing, and most importantly, + if the data type of the referenced item changes in the future (for + instance: you change the type of user_id + from integer to real), you may not need + to change your function definition. + + + Row Types - name tablename%ROWTYPE; - A variable of a composite type is called a row - variable (or row-type variable). Such a variable can hold a - whole row of a SELECT or FOR - query result, so long as that query's column set matches the declared - type of the variable. The individual fields of the row value are - accessed using the usual dot notation, for example + variable (or row-type variable). Such a variable + can hold a whole row of a SELECT or FOR + query result, so long as that query's column set matches the + declared type of the variable. + tablename must be an existing table or + view name in the database. The individual fields of the row value + are accessed using the usual dot notation, for example rowvar.field. @@ -500,18 +566,22 @@ END; Parameters to a function can be composite types (complete table rows). In that case, the - corresponding identifier $n will be a row variable, and fields can + corresponding identifier $n will be a row variable, and fields can be selected from it, for example $1.user_id. - Only the user-defined attributes of a table row are accessible in a - row-type variable, not OID or other system attributes (because the + Only the user-defined columns of a table row are accessible in a + row-type variable, not the OID or other system columns (because the row could be from a view). The fields of the row type inherit the table's field size or precision for data types such as - char(n). + char(n). + + + + Here is an example of using composite types: -CREATE FUNCTION use_two_tables(tablename) RETURNS TEXT AS ' +CREATE FUNCTION use_two_tables(tablename) RETURNS text AS ' DECLARE in_t ALIAS FOR $1; use_t table2name%ROWTYPE; @@ -519,13 +589,13 @@ BEGIN SELECT * INTO use_t FROM table2name WHERE ... ; RETURN in_t.f1 || use_t.f3 || in_t.f5 || use_t.f7; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; - Records + Record Types @@ -536,10 +606,10 @@ END; Record variables are similar to row-type variables, but they have no predefined structure. They take on the actual row structure of the - row they are assigned during a SELECT or FOR command. The substructure + row they are assigned during a SELECT or FOR command. The substructure of a record variable can change each time it is assigned to. A consequence of this is that until a record variable is first assigned - to, it has no substructure, and any attempt to access a + to, it has no substructure, and any attempt to access a field in it will draw a run-time error. @@ -548,89 +618,8 @@ END; - - Attributes - - - Using the %TYPE and %ROWTYPE - attributes, you can declare variables with the same - data type or structure as another database item (e.g: a - table field). - - - - - - variable%TYPE - - - - %TYPE provides the data type of a - variable or database column. You can use this to - declare variables that will hold database - values. For example, let's say you have a column - named user_id in your - users table. To declare a variable with - the same data type as users.user_id you write: - -user_id users.user_id%TYPE; - - - - - By using %TYPE you do not need to know - the data type of the structure you are referencing, - and most important, if the data type of the - referenced item changes in the future (e.g: you - change your table definition of user_id from INTEGER to - REAL), you may not need to change your function - definition. - - - - - - - table%ROWTYPE - - - - %ROWTYPE provides the composite data type corresponding - to a whole row of the specified table. - table must be an existing - table or view name of the database. - - - -DECLARE - users_rec users%ROWTYPE; - user_id users.user_id%TYPE; -BEGIN - user_id := users_rec.user_id; - ... - -CREATE FUNCTION does_view_exist(INTEGER) RETURNS bool AS ' - DECLARE - key ALIAS FOR $1; - table_data cs_materialized_views%ROWTYPE; - BEGIN - SELECT INTO table_data * FROM cs_materialized_views - WHERE sort_key=key; - - IF NOT FOUND THEN - RETURN false; - END IF; - RETURN true; - END; -' LANGUAGE 'plpgsql'; - - - - - - - RENAME + <literal>RENAME</> @@ -669,20 +658,20 @@ RENAME this_var TO that_var; statements are processed using the server's regular SQL executor. Expressions that appear to contain constants may in fact require run-time evaluation - (e.g. 'now' for the timestamp + (e.g., 'now' for the timestamp type) so it is impossible for the PL/pgSQL parser to identify real - constant values other than the NULL keyword. All expressions are + constant values other than the key word NULL. All expressions are evaluated internally by executing a query SELECT expression - using the SPI manager. In the expression, + using the SPI manager. For evaluation, occurrences of PL/pgSQL variable - identifiers are replaced by parameters and the actual values from + identifiers are replaced by parameters, and the actual values from the variables are passed to the executor in the parameter array. This allows the query plan for the SELECT to - be prepared just once and then re-used for subsequent + be prepared just once and then reused for subsequent evaluations. @@ -693,20 +682,20 @@ SELECT expression is a difference between what these two functions do: -CREATE FUNCTION logfunc1 (TEXT) RETURNS TIMESTAMP AS ' +CREATE FUNCTION logfunc1(text) RETURNS timestamp AS ' DECLARE logtxt ALIAS FOR $1; BEGIN INSERT INTO logtable VALUES (logtxt, ''now''); RETURN ''now''; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; and -CREATE FUNCTION logfunc2 (TEXT) RETURNS TIMESTAMP AS ' +CREATE FUNCTION logfunc2(text) RETURNS timestamp AS ' DECLARE logtxt ALIAS FOR $1; curtime timestamp; @@ -715,31 +704,33 @@ CREATE FUNCTION logfunc2 (TEXT) RETURNS TIMESTAMP AS ' INSERT INTO logtable VALUES (logtxt, curtime); RETURN curtime; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; + - In the case of logfunc1(), the + + In the case of logfunc1, the PostgreSQL main parser knows when - preparing the plan for the INSERT, that the string + preparing the plan for the INSERT, that the string 'now' should be interpreted as - timestamp because the target field of logtable + timestamp because the target column of logtable is of that type. Thus, it will make a constant from it at this time and this constant value is then used in all invocations of - logfunc1() during the lifetime of the - backend. Needless to say that this isn't what the + logfunc1 during the lifetime of the + session. Needless to say that this isn't what the programmer wanted. - In the case of logfunc2(), the + In the case of logfunc2, the PostgreSQL main parser does not know what type 'now' should become and therefore it returns a data value of type text containing the string - 'now'. During the ensuing assignment + now. During the ensuing assignment to the local variable curtime, the PL/pgSQL interpreter casts this string to the timestamp type by calling the - text_out() and timestamp_in() + text_out and timestamp_in functions for the conversion. So, the computed time stamp is updated on each execution as the programmer expects. @@ -765,12 +756,12 @@ CREATE FUNCTION logfunc2 (TEXT) RETURNS TIMESTAMP AS ' types that are explicitly understood by PL/pgSQL. Anything not recognized as one of these statement types is presumed - to be an SQL query, and is sent to the main database engine to execute - (after substitution for any PL/pgSQL variables + to be an SQL command and is sent to the main database engine to execute + (after substitution of any PL/pgSQL variables used in the statement). Thus, - for example, SQL INSERT, UPDATE, and - DELETE commands may be considered to be statements of - PL/pgSQL. But they are not specifically + for example, the SQL commands INSERT, UPDATE, and + DELETE may be considered to be statements of + PL/pgSQL, but they are not specifically listed here. @@ -809,10 +800,10 @@ tax := subtotal * 0.06; - SELECT INTO + <command>SELECT INTO</command> - The result of a SELECT command yielding multiple columns (but + The result of a SELECT command yielding multiple columns (but only one row) can be assigned to a record variable, row-type variable, or list of scalar variables. This is done by: @@ -822,60 +813,67 @@ SELECT INTO target expressionstarget can be a record variable, a row variable, or a comma-separated list of simple variables and - record/row fields. Note that this is quite different from - PostgreSQL's normal interpretation of SELECT INTO, which is that the - INTO target is a newly created table. (If you want to create a - table from a SELECT result inside a PL/pgSQL function, use the - syntax CREATE TABLE ... AS SELECT.) + record/row fields. + + + + Note that this is quite different from + PostgreSQL's normal interpretation of + SELECT INTO, where the INTO target + is a newly created table. If you want to create a table from a + SELECT result inside a + PL/pgSQL function, use the syntax + CREATE TABLE ... AS SELECT. If a row or a variable list is used as target, the selected values - must exactly match the structure of the target(s), or a run-time error + must exactly match the structure of the target, or a run-time error occurs. When a record variable is the target, it automatically configures itself to the row type of the query result columns. - Except for the INTO clause, the SELECT statement is the same as a normal - SQL SELECT query and can use the full power of SELECT. + Except for the INTO clause, the SELECT + statement is the same as a normal SQL SELECT command + and can use its full power. - If the SELECT query returns zero rows, null values are assigned to the - target(s). If the SELECT query returns multiple rows, the first + If the query returns zero rows, null values are assigned to the + target(s). If the query returns multiple rows, the first row is assigned to the target(s) and the rest are discarded. (Note that the first row is not well-defined unless you've - used ORDER BY.) + used ORDER BY.) - At present, the INTO clause can appear almost anywhere in the SELECT - query, but it is recommended to place it immediately after the SELECT - keyword as depicted above. Future versions of + At present, the INTO clause can appear almost anywhere in the SELECT + statement, but it is recommended to place it immediately after the SELECT + key word as depicted above. Future versions of PL/pgSQL may be less forgiving about - placement of the INTO clause. + placement of the INTO clause. - You can use FOUND immediately after a SELECT - INTO statement to determine whether the assignment was successful - (that is, at least one row was was returned by the SELECT - statement). For example: + You can use FOUND immediately after a SELECT + INTO statement to determine whether the assignment was successful + (that is, at least one row was was returned by the query). For example: -SELECT INTO myrec * FROM EMP WHERE empname = myname; +SELECT INTO myrec * FROM emp WHERE empname = myname; IF NOT FOUND THEN RAISE EXCEPTION ''employee % not found'', myname; END IF; - - Alternatively, you can use the IS NULL (or ISNULL) conditional to - test for whether a RECORD/ROW result is null. Note that there is no - way to tell whether any additional rows might have been discarded. + To test for whether a record/row result is null, you can use the + IS NULL conditional. There is, however, no + way to tell whether any additional rows might have been + discarded. Here is an example that handles the case where no + rows have been returned: DECLARE users_rec RECORD; @@ -885,7 +883,6 @@ BEGIN IF users_rec.homepage IS NULL THEN -- user entered no homepage, return "http://" - RETURN ''http://''; END IF; END; @@ -894,7 +891,7 @@ END; - Executing an expression or query with no result + Executing an Expression or Query With No Result Sometimes one wishes to evaluate an expression or query but @@ -907,21 +904,22 @@ END; PERFORM query; - This executes a SELECT - query and discards the + This executes query, which must be a + SELECT statement, and discards the result. PL/pgSQL variables are substituted in the query as usual. Also, the special variable FOUND is set to true if the query produced at - least one row, or false if it produced no rows. + least one row or false if it produced no rows. - - One might expect that SELECT with no INTO - clause would accomplish this result, but at present the only - accepted way to do it is PERFORM. - - + + One might expect that SELECT with no + INTO clause would accomplish this result, but at + present the only accepted way to do it is + PERFORM. + + An example: @@ -931,109 +929,103 @@ PERFORM create_mv(''cs_session_page_requests_mv'', my_query); - - Executing dynamic queries + + Executing Dynamic Commands - Oftentimes you will want to generate dynamic queries inside your - PL/pgSQL functions, that is, queries + Oftentimes you will want to generate dynamic commands inside your + PL/pgSQL functions, that is, commands that will involve different tables or different data types each time they are executed. PL/pgSQL's - normal attempts to cache plans for queries will not work in such + normal attempts to cache plans for commands will not work in such scenarios. To handle this sort of problem, the EXECUTE statement is provided: -EXECUTE query-string; +EXECUTE command-string; - where query-string is an expression + where command-string is an expression yielding a string (of type - text) containing the query + text) containing the command to be executed. This string is fed literally to the SQL engine. Note in particular that no substitution of PL/pgSQL - variables is done on the query string. The values of variables must - be inserted in the query string as it is constructed. + variables is done on the command string. The values of variables must + be inserted in the command string as it is constructed. - When working with dynamic queries you will have to face + When working with dynamic commands you will have to face escaping of single quotes in PL/pgSQL. Please refer to the - table in - for a detailed explanation that will save you some effort. + overview in , + which can save you some effort. - Unlike all other queries in PL/pgSQL, a - query run by an - EXECUTE statement is not prepared and saved - just once during the life of the server. Instead, the - query is prepared each time the - statement is run. The query-string can - be dynamically created within the procedure to perform actions on - variable tables and fields. + Unlike all other commands in PL/pgSQL, a command + run by an EXECUTE statement is not prepared + and saved just once during the life of the session. Instead, the + command is prepared each time the statement is run. The command + string can be dynamically created within the function to perform + actions on variable tables and columns. - The results from SELECT queries are discarded + The results from SELECT commands are discarded by EXECUTE, and SELECT INTO is not currently supported within EXECUTE. So, the only way to extract a result from a dynamically-created - SELECT is to use the FOR-IN-EXECUTE form + SELECT is to use the FOR-IN-EXECUTE form described later. An example: - EXECUTE ''UPDATE tbl SET '' - || quote_ident(fieldname) + || quote_ident(colname) || '' = '' || quote_literal(newvalue) || '' WHERE ...''; - This example shows use of the functions - quote_ident(TEXT) and - quote_literal(TEXT). - Variables containing field and table identifiers should be - passed to function quote_ident(). - Variables containing literal elements of the dynamic query + quote_ident(text) and + quote_literal(text). + Variables containing column and table identifiers should be + passed to function quote_ident. + Variables containing values that act as value literals in the constructed command string should be passed to - quote_literal(). Both take the + quote_literal. Both take the appropriate steps to return the input text enclosed in single or double quotes and with any embedded special characters properly escaped. - Here is a much larger example of a dynamic query and + Here is a much larger example of a dynamic command and EXECUTE: -CREATE FUNCTION cs_update_referrer_type_proc() RETURNS INTEGER AS ' +CREATE FUNCTION cs_update_referrer_type_proc() RETURNS integer AS ' DECLARE - referrer_keys RECORD; -- Declare a generic record to be used in a FOR + referrer_keys RECORD; -- declare a generic record to be used in a FOR a_output varchar(4000); BEGIN - a_output := ''CREATE FUNCTION cs_find_referrer_type(varchar,varchar,varchar) - RETURNS VARCHAR AS '''' + a_output := ''CREATE FUNCTION cs_find_referrer_type(varchar, varchar, varchar) + RETURNS varchar AS '''' DECLARE v_host ALIAS FOR $1; v_domain ALIAS FOR $2; v_url ALIAS FOR $3; BEGIN ''; - -- -- Notice how we scan through the results of a query in a FOR loop -- using the FOR <record> construct. - -- FOR referrer_keys IN SELECT * FROM cs_referrer_keys ORDER BY try_order LOOP a_output := a_output || '' IF v_'' || referrer_keys.kind || '' LIKE '''''''''' @@ -1041,24 +1033,21 @@ BEGIN || referrer_keys.referrer_type || ''''''; END IF;''; END LOOP; - a_output := a_output || '' RETURN NULL; END; '''' LANGUAGE ''''plpgsql'''';''; + a_output := a_output || '' RETURN NULL; END; '''' LANGUAGE plpgsql;''; - -- This works because we are not substituting any variables - -- Otherwise it would fail. Look at PERFORM for another way to run functions - EXECUTE a_output; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; - Obtaining result status + Obtaining the Result Status There are several ways to determine the effect of a command. The - first method is to use the GET DIAGNOSTICS, + first method is to use the GET DIAGNOSTICS, which has the form: @@ -1066,26 +1055,27 @@ GET DIAGNOSTICS variable = item This command allows retrieval of system status indicators. Each - item is a keyword identifying a state + item is a key word identifying a state value to be assigned to the specified variable (which should be of the right data type to receive it). The currently available status items are ROW_COUNT, the number of rows - processed by the last SQL query sent down to - the SQL engine; and RESULT_OID, + processed by the last SQL command sent down to + the SQL engine, and RESULT_OID, the OID of the last row inserted by the most recent - SQL query. Note that RESULT_OID - is only useful after an INSERT query. + SQL command. Note that RESULT_OID + is only useful after an INSERT command. + - + An example: - GET DIAGNOSTICS var_integer = ROW_COUNT; +GET DIAGNOSTICS var_integer = ROW_COUNT; - - There is a special variable named FOUND of + The second method to determine the effects of a command is the + special variable named FOUND of type boolean. FOUND starts out false within each PL/pgSQL function. It is set by each of the following types of statements: @@ -1152,7 +1142,7 @@ GET DIAGNOSTICS variable = item - Returning from a function + Returning From a Function There are two commands available that allow you to return data @@ -1163,23 +1153,24 @@ GET DIAGNOSTICS variable = item <command>RETURN</> - RETURN expression; - RETURN with an expression is used to return - from a PL/pgSQL function that does not return a - set. The function terminates and the value of - expression is returned to the caller. + + RETURN with an expression terminates the + function and returns the value of + expression to the caller. This form + is to be used for PL/pgSQL functions that does + not return a set. - To return a composite (row) value, you must write a record or row - variable as the expression. When - returning a scalar type, any expression can be used. - The expression's result will be automatically cast into the - function's return type as described for assignments. + When returning a scalar type, any expression can be used. The + expression's result will be automatically cast into the + function's return type as described for assignments. To return a + composite (row) value, you must write a record or row variable + as the expression. @@ -1188,8 +1179,8 @@ RETURN expression; without hitting a RETURN statement, a run-time error will occur. Note that if you have declared the function to return void, a RETURN statement - must still be specified; however, the expression following - RETURN is optional, and will be ignored in + must still be specified; the expression following + RETURN is, however, optional and will be ignored in any case. @@ -1203,7 +1194,7 @@ RETURN NEXT expression; When a PL/pgSQL function is declared to return - SETOF sometype, the procedure + SETOF sometype, the procedure to follow is slightly different. In that case, the individual items to return are specified in RETURN NEXT commands, and then a final RETURN command @@ -1211,6 +1202,9 @@ RETURN NEXT expression; finished executing. RETURN NEXT can be used with both scalar and composite data types; in the later case, an entire table of results will be returned. + + + Functions that use RETURN NEXT should be called in the following fashion: @@ -1218,7 +1212,7 @@ RETURN NEXT expression; SELECT * FROM some_func(); - That is, the function is used as a table source in a FROM + That is, the function is used as a table source in a FROM clause. @@ -1233,11 +1227,6 @@ SELECT * FROM some_func(); have no argument, causes control to exit the function. - - For more information on using set-returning functions in - PostgreSQL, refer to XXX. - The current implementation of RETURN NEXT @@ -1251,7 +1240,7 @@ SELECT * FROM some_func(); allow users to allow users to define set-returning functions that do not have this limitation. Currently, the point at which data begins being written to disk is controlled by the - SORT_MEM configuration variable. Administrators + sort_mem configuration variable. Administrators who have sufficient memory to store larger result sets in memory should consider increasing this parameter. @@ -1274,7 +1263,7 @@ SELECT * FROM some_func(); IF ... THEN ... ELSE - IF ... THEN ... ELSE IF and + IF ... THEN ... ELSE IF IF ... THEN ... ELSIF ... THEN ... ELSE @@ -1285,19 +1274,22 @@ SELECT * FROM some_func(); <literal>IF-THEN</> - IF boolean-expression THEN statements END IF; + IF-THEN statements are the simplest form of IF. The statements between THEN and END IF will be executed if the condition is true. Otherwise, they are skipped. + + + Example: IF v_user_id <> 0 THEN UPDATE users SET email = v_email WHERE user_id = v_user_id; @@ -1309,7 +1301,6 @@ END IF; <literal>IF-THEN-ELSE</> - IF boolean-expression THEN statements @@ -1318,25 +1309,30 @@ ELSE END IF; + IF-THEN-ELSE statements add to IF-THEN by letting you specify an alternative set of statements that should be executed if the - condition evaluates to FALSE. + condition evaluates to false. + + + Examples: -IF parentid IS NULL or parentid = '''' -THEN - return fullname; +IF parentid IS NULL OR parentid = '''' +THEN + RETURN fullname; ELSE - return hp_true_filename(parentid) || ''/'' || fullname; + RETURN hp_true_filename(parentid) || ''/'' || fullname; END IF; + - + IF v_count > 0 THEN - INSERT INTO users_count(count) VALUES(v_count); - return ''t''; -ELSE - return ''f''; + INSERT INTO users_count (count) VALUES (v_count); + RETURN ''t''; +ELSE + RETURN ''f''; END IF; @@ -1351,11 +1347,11 @@ END IF; IF demo_row.sex = ''m'' THEN - pretty_sex := ''man''; + pretty_sex := ''man''; ELSE - IF demo_row.sex = ''f'' THEN - pretty_sex := ''woman''; - END IF; + IF demo_row.sex = ''f'' THEN + pretty_sex := ''woman''; + END IF; END IF; @@ -1365,16 +1361,16 @@ END IF; IF statement inside the ELSE part of an outer IF statement. Thus you need one END IF - statement for each nested IF and one for the parent + statement for each nested IF and one for the parent IF-ELSE. This is workable but grows tedious when there are many alternatives to be checked. + Hence the next form. <literal>IF-THEN-ELSIF-ELSE</> - IF boolean-expression THEN statements @@ -1390,6 +1386,7 @@ IF boolean-expression THEN END IF; + IF-THEN-ELSIF-ELSE provides a more convenient method of checking many alternatives in one statement. Formally it is equivalent to nested @@ -1408,16 +1405,11 @@ ELSIF number > 0 THEN ELSIF number < 0 THEN result := ''negative''; ELSE - -- hmm, the only other possibility is that number IS NULL + -- hmm, the only other possibility is that number is null result := ''NULL''; END IF; - - - The final ELSE statement is optional. - - @@ -1425,51 +1417,52 @@ END IF; Simple Loops - With the LOOP, EXIT, WHILE and FOR statements, you can arrange - for your PL/pgSQL function to repeat - a series of commands. + With the LOOP, EXIT, WHILE, + and FOR statements, you can arrange for your + PL/pgSQL function to repeat a series + of commands. - LOOP + <literal>LOOP</> - -<<label>> +<<label>> LOOP statements END LOOP; - LOOP defines an unconditional loop that is repeated indefinitely - until terminated by an EXIT or RETURN - statement. The optional label can be used by EXIT statements in + + LOOP defines an unconditional loop that is repeated indefinitely + until terminated by an EXIT or RETURN + statement. The optional label can be used by EXIT statements in nested loops to specify which level of nesting should be terminated. - EXIT + <literal>EXIT</> - EXIT label WHEN expression ; + If no label is given, the innermost loop is terminated and the - statement following END LOOP is executed next. + statement following END LOOP is executed next. If label is given, it must be the label of the current or some outer level of nested loop or block. Then the named loop or block is terminated and control continues with the statement after the loop's/block's corresponding - END. + END. - If WHEN is present, loop exit occurs only if the specified condition - is true, otherwise control passes to the statement after EXIT. + If WHEN is present, loop exit occurs only if the specified condition + is true, otherwise control passes to the statement after EXIT. @@ -1490,7 +1483,7 @@ END LOOP; BEGIN -- some computations IF stocks > 100000 THEN - EXIT; -- illegal. Can't use EXIT outside of a LOOP + EXIT; -- invalid; cannot use EXIT outside of LOOP END IF; END; @@ -1498,17 +1491,17 @@ END; - WHILE + <literal>WHILE</> - -<<label>> +<<label>> WHILE expression LOOP statements END LOOP; - The WHILE statement repeats a + + The WHILE statement repeats a sequence of statements so long as the condition expression evaluates to true. The condition is checked just before each entry to the loop body. @@ -1529,32 +1522,31 @@ END LOOP; - FOR (integer for-loop) + <literal>FOR</> (integer variant) - -<<label>> +<<label>> FOR name IN REVERSE expression .. expression LOOP statements END LOOP; - This form of FOR creates a loop that iterates over a range of integer + + This form of FOR creates a loop that iterates over a range of integer values. The variable name is automatically defined as type - integer and exists only inside the loop. The two expressions giving + integer and exists only inside the loop. The two expressions giving the lower and upper bound of the range are evaluated once when entering - the loop. The iteration step is normally 1, but is -1 when REVERSE is + the loop. The iteration step is normally 1, but is -1 when REVERSE is specified. - Some examples of integer FOR loops: + Some examples of integer FOR loops: FOR i IN 1..10 LOOP - -- some expressions here - - RAISE NOTICE ''i is %'',i; + -- some expressions here + RAISE NOTICE ''i is %'', i; END LOOP; FOR i IN REVERSE 10..1 LOOP @@ -1569,73 +1561,70 @@ END LOOP; Looping Through Query Results - Using a different type of FOR loop, you can iterate through + Using a different type of FOR loop, you can iterate through the results of a query and manipulate that data accordingly. The syntax is: -<<label>> -FOR record | row IN select_query LOOP +<<label>> +FOR record_or_row IN query LOOP statements END LOOP; - The record or row variable is successively assigned all the rows - resulting from the SELECT query and the loop + The record or row variable is successively assigned each row + resulting from the query (a SELECT command) and the loop body is executed for each row. Here is an example: - - - -CREATE FUNCTION cs_refresh_mviews () RETURNS INTEGER AS ' +CREATE FUNCTION cs_refresh_mviews() RETURNS integer AS ' DECLARE - mviews RECORD; + mviews RECORD; BEGIN - PERFORM cs_log(''Refreshing materialized views...''); + PERFORM cs_log(''Refreshing materialized views...''); - FOR mviews IN SELECT * FROM cs_materialized_views ORDER BY sort_key LOOP + FOR mviews IN SELECT * FROM cs_materialized_views ORDER BY sort_key LOOP - -- Now "mviews" has one record from cs_materialized_views + -- Now "mviews" has one record from cs_materialized_views - PERFORM cs_log(''Refreshing materialized view '' || quote_ident(mviews.mv_name) || ''...''); - EXECUTE ''TRUNCATE TABLE '' || quote_ident(mviews.mv_name); - EXECUTE ''INSERT INTO '' || quote_ident(mviews.mv_name) || '' '' || mviews.mv_query; - END LOOP; + PERFORM cs_log(''Refreshing materialized view '' || quote_ident(mviews.mv_name) || ''...''); + EXECUTE ''TRUNCATE TABLE '' || quote_ident(mviews.mv_name); + EXECUTE ''INSERT INTO '' || quote_ident(mviews.mv_name) || '' '' || mviews.mv_query; + END LOOP; - PERFORM cs_log(''Done refreshing materialized views.''); - RETURN 1; -end; -' LANGUAGE 'plpgsql'; + PERFORM cs_log(''Done refreshing materialized views.''); + RETURN 1; +END; +' LANGUAGE plpgsql; - If the loop is terminated by an EXIT statement, the last + If the loop is terminated by an EXIT statement, the last assigned row value is still accessible after the loop. - The FOR-IN-EXECUTE statement is another way to iterate over + The FOR-IN-EXECUTE statement is another way to iterate over records: -<<label>> -FOR record | row IN EXECUTE text_expression LOOP +<<label>> +FOR record_or_row IN EXECUTE text_expression LOOP statements END LOOP; This is like the previous form, except that the source SELECT statement is specified as a string - expression, which is evaluated and re-planned on each entry to - the FOR loop. This allows the programmer to choose the speed of - a pre-planned query or the flexibility of a dynamic query, just + expression, which is evaluated and replanned on each entry to + the FOR loop. This allows the programmer to choose the speed of + a preplanned query or the flexibility of a dynamic query, just as with a plain EXECUTE statement. The PL/pgSQL parser presently distinguishes the - two kinds of FOR loops (integer or record-returning) by checking - whether the target variable mentioned just after FOR has been - declared as a record/row variable. If not, it's presumed to be - an integer FOR loop. This can cause rather nonintuitive error + two kinds of FOR loops (integer or query result) by checking + whether the target variable mentioned just after FOR has been + declared as a record or row variable. If not, it's presumed to be + an integer FOR loop. This can cause rather nonintuitive error messages when the true problem is, say, that one has - misspelled the FOR variable name. + misspelled the variable name after the FOR. @@ -1650,7 +1639,7 @@ END LOOP; the query result a few rows at a time. One reason for doing this is to avoid memory overrun when the result contains a large number of rows. (However, PL/pgSQL users do not normally need - to worry about that, since FOR loops automatically use a cursor + to worry about that, since FOR loops automatically use a cursor internally to avoid memory problems.) A more interesting usage is to return a reference to a cursor that it has created, allowing the caller to read the rows. This provides an efficient way to return @@ -1668,13 +1657,13 @@ END LOOP; Another way is to use the cursor declaration syntax, which in general is: -name CURSOR ( arguments ) FOR select_query ; +name CURSOR ( arguments ) FOR query ; (FOR may be replaced by IS for Oracle compatibility.) - arguments, if any, are a - comma-separated list of name - datatype pairs that define names to be + arguments, if specified, is a + comma-separated list of pairs name + datatype that define names to be replaced by parameter values in the given query. The actual values to substitute for these names will be specified later, when the cursor is opened. @@ -1684,8 +1673,8 @@ END LOOP; DECLARE curs1 refcursor; - curs2 CURSOR FOR SELECT * from tenk1; - curs3 CURSOR (key int) IS SELECT * from tenk1 where unique1 = key; + curs2 CURSOR FOR SELECT * FROM tenk1; + curs3 CURSOR (key integer) IS SELECT * FROM tenk1 WHERE unique1 = key; All three of these variables have the data type refcursor, but the first may be used with any query, while the second has @@ -1705,18 +1694,18 @@ DECLARE Before a cursor can be used to retrieve rows, it must be opened. (This is the equivalent action to the SQL command DECLARE CURSOR.) PL/pgSQL has - four forms of the OPEN statement, two of which use unbound cursor - variables and the other two use bound cursor variables. + three forms of the OPEN statement, two of which use unbound cursor + variables and the other uses a bound cursor variable. - OPEN FOR SELECT + <command>OPEN FOR SELECT</command> - OPEN unbound-cursor FOR SELECT ...; + The cursor variable is opened and given the specified query to execute. The cursor cannot be open already, and it must have been declared as an unbound cursor (that is, as a simple @@ -1724,8 +1713,11 @@ OPEN unbound-cursor FOR SELECT ...; is treated in the same way as other SELECT statements in PL/pgSQL: PL/pgSQL variable names are substituted, and the query plan is cached for - possible re-use. + possible reuse. + + + An example: OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey; @@ -1733,13 +1725,13 @@ OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey; - OPEN FOR EXECUTE + <command>OPEN FOR EXECUTE</command> - OPEN unbound-cursor FOR EXECUTE query-string; + The cursor variable is opened and given the specified query to execute. The cursor cannot be open already, and it must have been declared as an unbound cursor (that is, as a simple @@ -1747,7 +1739,10 @@ OPEN unbound-cursor FOR EXECUTE Inside the format string, % is replaced by the - next optional argument's external representation. Write + next optional argument's string representation. Write %% to emit a literal %. Note that the optional arguments must presently be simple variables, not expressions, and the format must be a simple string literal. @@ -1949,38 +1951,33 @@ RAISE level ' - Exceptions - PostgreSQL does not have a very smart exception handling model. Whenever the parser, planner/optimizer or executor decide that a statement cannot be processed any longer, the whole transaction gets aborted and the system jumps back - into the main loop to get the next query from the client application. + into the main loop to get the next command from the client application. It is possible to hook into the error mechanism to notice that this happens. But currently it is impossible to tell what really - caused the abort (input/output conversion error, floating-point - error, parse error). And it is possible that the database backend + caused the abort (data type format error, floating-point + error, parse error, etc.). And it is possible that the database server is in an inconsistent state at this point so returning to the upper executor or issuing more commands might corrupt the whole database. @@ -1993,7 +1990,6 @@ RAISE EXCEPTION ''Inexistent ID --> %'',user_id; function and where (line number and type of statement) this happened. The error always stops execution of the function. - @@ -2021,8 +2017,8 @@ RAISE EXCEPTION ''Inexistent ID --> %'',user_id; Data type RECORD; variable holding the new - database row for INSERT/UPDATE operations in ROW level - triggers. This variable is NULL in STATEMENT level triggers. + database row for INSERT/UPDATE operations in row-level + triggers. This variable is null in statement-level triggers. @@ -2032,8 +2028,8 @@ RAISE EXCEPTION ''Inexistent ID --> %'',user_id; Data type RECORD; variable holding the old - database row for UPDATE/DELETE operations in ROW level - triggers. This variable is NULL in STATEMENT level triggers. + database row for UPDATE/DELETE operations in row-level + triggers. This variable is null in statement-level triggers. @@ -2075,9 +2071,9 @@ RAISE EXCEPTION ''Inexistent ID --> %'',user_id; Data type text; a string of - INSERT, UPDATE or + INSERT, UPDATE, or DELETE telling for which operation the - trigger is fired. + trigger was fired. @@ -2118,8 +2114,8 @@ RAISE EXCEPTION ''Inexistent ID --> %'',user_id; Data type array of text; the arguments from the CREATE TRIGGER statement. - The index counts from 0 and can be given as an expression. Invalid - indices (< 0 or >= tg_nargs) result in a null value. + The index counts from 0. Invalid + indices (less than 0 or greater than or equal to tg_nargs) result in a null value. @@ -2127,34 +2123,40 @@ RAISE EXCEPTION ''Inexistent ID --> %'',user_id; - A trigger function must return either NULL or a record/row value + A trigger function must return either null or a record/row value having exactly the structure of the table the trigger was fired - for. The return value of a BEFORE or AFTER STATEMENT level - trigger, or an AFTER ROW level trigger is ignored; it may as well - return NULL. However, any of these types of triggers can still + for. The return value of a BEFORE or AFTER statement-level + trigger or an AFTER row-level trigger is ignored; it may as well + be null. However, any of these types of triggers can still abort the entire trigger operation by raising an error. - ROW level triggers fired BEFORE may return NULL to signal the + Row-level triggers fired BEFORE may return null to signal the trigger manager to skip the rest of the operation for this row - (ie, subsequent triggers are not fired, and the - INSERT/UPDATE/DELETE does not occur for this row). If a non-NULL + (i.e., subsequent triggers are not fired, and the + INSERT/UPDATE/DELETE does not occur for this row). If a nonnull value is returned then the operation proceeds with that row value. - Note that returning a row value different from the original value - of NEW alters the row that will be inserted or updated. It is - possible to replace single values directly in NEW and return that, + Returning a row value different from the original value + of NEW alters the row that will be inserted or updated. It is + possible to replace single values directly in NEW and return NEW, or to build a complete new record/row to return. - - A <application>PL/pgSQL</application> Trigger Procedure Example + + shows an example of a + trigger procedure in PL/pgSQL. + + + + A <application>PL/pgSQL</application> Trigger Procedure This example trigger ensures that any time a row is inserted or updated in the table, the current user name and time are stamped into the row. And it ensures that an employee's name is given and that the salary is a positive value. + CREATE TABLE emp ( @@ -2164,18 +2166,18 @@ CREATE TABLE emp ( last_user text ); -CREATE FUNCTION emp_stamp () RETURNS TRIGGER AS ' +CREATE FUNCTION emp_stamp() RETURNS trigger AS ' BEGIN -- Check that empname and salary are given - IF NEW.empname ISNULL THEN - RAISE EXCEPTION ''empname cannot be NULL value''; + IF NEW.empname IS NULL THEN + RAISE EXCEPTION ''empname cannot be null''; END IF; - IF NEW.salary ISNULL THEN - RAISE EXCEPTION ''% cannot have NULL salary'', NEW.empname; + IF NEW.salary IS NULL THEN + RAISE EXCEPTION ''% cannot have null salary'', NEW.empname; END IF; -- Who works for us when she must pay for it? - IF NEW.salary < 0 THEN + IF NEW.salary < 0 THEN RAISE EXCEPTION ''% cannot have a negative salary'', NEW.empname; END IF; @@ -2184,106 +2186,10 @@ CREATE FUNCTION emp_stamp () RETURNS TRIGGER AS ' NEW.last_user := current_user; RETURN NEW; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp FOR EACH ROW EXECUTE PROCEDURE emp_stamp(); - - - - - - - - - Examples - - - Here are only a few functions to demonstrate how easy it is to - write PL/pgSQL - functions. For more complex examples the programmer - might look at the regression test for PL/pgSQL. - - - - One painful detail in writing functions in - PL/pgSQL is the handling of single - quotes. The function's source text in CREATE FUNCTION - must be a literal string. Single quotes inside of literal strings - must be either doubled or quoted with a backslash. We are still - looking for an elegant alternative. In the meantime, doubling the - single quotes as in the examples below should be used. Any - solution for this in future versions of - PostgreSQL will be forward compatible. - - - - For a detailed explanation and examples of how to escape single - quotes in different situations, please see . - - - - A Simple <application>PL/pgSQL</application> Function to Increment an Integer - - - The following two PL/pgSQL functions are identical to their - counterparts from the C language function discussion. This - function receives an integer and increments it by - one, returning the incremented value. - - - -CREATE FUNCTION add_one (integer) RETURNS INTEGER AS ' - BEGIN - RETURN $1 + 1; - END; -' LANGUAGE 'plpgsql'; - - - - - A Simple <application>PL/pgSQL</application> Function to Concatenate Text - - - This function receives two text parameters and - returns the result of concatenating them. - - - -CREATE FUNCTION concat_text (TEXT, TEXT) RETURNS TEXT AS ' - BEGIN - RETURN $1 || $2; - END; -' LANGUAGE 'plpgsql'; - - - - - A <application>PL/pgSQL</application> Function on Composite Type - - - In this example, we take EMP (a table) and an - integer as arguments to our function, which returns - a boolean. If the salary field of the EMP table is - NULL, we return f. Otherwise we compare with - that field with the integer passed to the function - and return the boolean result of the comparison (t - or f). This is the PL/pgSQL equivalent to the example from the C - functions. - - - -CREATE FUNCTION c_overpaid (EMP, INTEGER) RETURNS BOOLEAN AS ' - DECLARE - emprec ALIAS FOR $1; - sallim ALIAS FOR $2; - BEGIN - IF emprec.salary ISNULL THEN - RETURN ''f''; - END IF; - RETURN emprec.salary > sallim; - END; -' LANGUAGE 'plpgsql'; @@ -2291,31 +2197,6 @@ CREATE FUNCTION c_overpaid (EMP, INTEGER) RETURNS BOOLEAN AS ' - - - - February 2001 - - - Roberto - Mello - -
- rmello@fslc.usu.edu -
-
-
- -
- Porting from <productname>Oracle</productname> PL/SQL @@ -2326,43 +2207,26 @@ CREATE FUNCTION c_overpaid (EMP, INTEGER) RETURNS BOOLEAN AS ' PL/SQL - - Author - - Roberto Mello (rmello@fslc.usu.edu) - - - - This section explains differences between Oracle's PL/SQL and - PostgreSQL's PL/pgSQL languages in the hopes of helping developers - port applications from Oracle to PostgreSQL. Most of the code here - is from the ArsDigita - Clickstream - module that I ported to PostgreSQL when I took an - internship with OpenForce - Inc. in the Summer of 2000. + This section explains differences between + PostgreSQL's PL/pgSQL + language and Oracle's PL/SQL language, + to help developers that port applications from Oracle to + PostgreSQL. - PL/pgSQL is similar to PL/SQL in many aspects. It is a block - structured, imperative language (all variables have to be - declared). PL/SQL has many more features than its PostgreSQL - counterpart, but PL/pgSQL allows for a great deal of functionality - and it is being improved constantly. - - - - Main Differences - - - Some things you should keep in mind when porting from - Oracle to PostgreSQL: + PL/pgSQL is similar to PL/SQL in many + aspects. It is a block-structured, imperative language, and all + variables have to be declared. Assignments, loops, conditionals + are similar. The main differences you should keep in mind when + porting from PL/SQL to + PL/pgSQL are: - No default parameters in PostgreSQL. + There are no default values for parameters in PostgreSQL. @@ -2375,146 +2239,41 @@ CREATE FUNCTION c_overpaid (EMP, INTEGER) RETURNS BOOLEAN AS ' - Assignments, loops and conditionals are similar. + No need for cursors in PL/pgSQL, just put the + query in the FOR statement. (See .) - No need for cursors in PostgreSQL, just put the query in the FOR - statement (see example below) + In PostgreSQL you need to escape single + quotes in the function body. See . - In PostgreSQL you need to escape single - quotes. See . + Instead of packages, use schemas to organize your functions + into groups. - - Quote Me on That: Escaping Single Quotes - - - In PostgreSQL you need to escape single quotes inside your - function definition. This can lead to quite amusing code at - times, especially if you are creating a function that generates - other function(s), as in - . - One thing to keep in mind - when escaping lots of single quotes is that, except for the - beginning/ending quotes, all the others will come in even - quantity. - - - - gives the scoop. (You'll - love this little chart.) - - - - Single Quotes Escaping Chart - - - - - No. of Quotes - Usage - Example - Result - - - - - - 1 - To begin/terminate function bodies - -CREATE FUNCTION foo() RETURNS INTEGER AS '...' -LANGUAGE 'plpgsql'; - - as is - - - - 2 - In assignments, SELECT statements, to delimit strings, etc. - -a_output := ''Blah''; -SELECT * FROM users WHERE f_name=''foobar''; - - SELECT * FROM users WHERE f_name='foobar'; - - - - 4 - - When you need two single quotes in your resulting string - without terminating that string. - - -a_output := a_output || '' AND name - LIKE ''''foobar'''' AND ...'' - - AND name LIKE 'foobar' AND ... - - - - 6 - - When you want double quotes in your resulting string - and terminate that string. - - -a_output := a_output || '' AND name - LIKE ''''foobar'''''' - - - AND name LIKE 'foobar' - - - - - 10 - - When you want two single quotes in the resulting string - (which accounts for 8 quotes) and - terminate that string (2 more). You will probably only need - that if you were using a function to generate other functions - (like in ). - - -a_output := a_output || '' if v_'' || - referrer_keys.kind || '' like '''''''''' - || referrer_keys.key_string || '''''''''' - then return '''''' || referrer_keys.referrer_type - || ''''''; end if;''; - - - if v_<...> like ''<...>'' then return ''<...>''; end if; - - - - -
-
-
+ + Porting Examples - - - Porting Functions - + + shows how to port a simple + function from PL/SQL to PL/pgSQL. + - - - A Simple Function - + + Porting a Simple Function from <application>PL/SQL</> to <application>PL/pgSQL</> - Here is an Oracle function: + Here is an Oracle PL/SQL function: CREATE OR REPLACE FUNCTION cs_fmt_browser_version(v_name IN varchar, v_version IN varchar) RETURN varchar IS @@ -2525,7 +2284,7 @@ BEGIN RETURN v_name || '/' || v_version; END; / -SHOW ERRORS; +show errors; @@ -2545,11 +2304,11 @@ SHOW ERRORS; Oracle can have IN, OUT, and - INOUT parameters passed to functions. The + INOUT parameters passed to functions. INOUT, for example, means that the parameter will receive a value and return - another. PostgreSQL only has IN - parameters and functions can return only a single value. + another. PostgreSQL only has IN + parameters. @@ -2557,16 +2316,15 @@ SHOW ERRORS; The RETURN key word in the function prototype (not the function body) becomes - RETURNS in PostgreSQL. + RETURNS in PostgreSQL. - On PostgreSQL functions are created using single quotes as - delimiters, so you have to escape single quotes inside your - functions (which can be quite annoying at times; see ). + In PostgreSQL, functions are created using + single quotes as the delimiters of the function body, so you + have to escape single quotes inside the function body. @@ -2580,12 +2338,12 @@ SHOW ERRORS; - So let's see how this function would look when ported to + This is how this function would look when ported to PostgreSQL: -CREATE OR REPLACE FUNCTION cs_fmt_browser_version(VARCHAR, VARCHAR) -RETURNS VARCHAR AS ' +CREATE OR REPLACE FUNCTION cs_fmt_browser_version(varchar, varchar) +RETURNS varchar AS ' DECLARE v_name ALIAS FOR $1; v_version ALIAS FOR $2; @@ -2595,24 +2353,30 @@ BEGIN END IF; RETURN v_name || ''/'' || v_version; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; + + + shows how to port a + function that creates another function and how to handle to + ensuing quoting problems. + - - - A Function that Creates Another Function - + + Porting a Function that Creates Another Function from <application>PL/SQL</> to <application>PL/pgSQL</> The following procedure grabs rows from a SELECT statement and builds a large function with the results in IF statements, for the - sake of efficiency. Notice particularly the differences in - cursors, FOR loops, and the need to escape - single quotes in PostgreSQL. + sake of efficiency. Notice particularly the differences in the + cursor and the FOR loop, + + + This is the Oracle version: CREATE OR REPLACE PROCEDURE cs_update_referrer_type_proc IS CURSOR referrer_keys IS @@ -2634,7 +2398,7 @@ referrer_key.key_string || ''' THEN RETURN ''' || referrer_key.referrer_type || EXECUTE IMMEDIATE a_output; END; / -show errors +show errors; @@ -2642,23 +2406,21 @@ show errors Here is how this function would end up in PostgreSQL: -CREATE FUNCTION cs_update_referrer_type_proc() RETURNS INTEGER AS ' +CREATE FUNCTION cs_update_referrer_type_proc() RETURNS integer AS ' DECLARE referrer_keys RECORD; -- Declare a generic record to be used in a FOR a_output varchar(4000); BEGIN - a_output := ''CREATE FUNCTION cs_find_referrer_type(VARCHAR,VARCHAR,VARCHAR) - RETURNS VARCHAR AS '''' + a_output := ''CREATE FUNCTION cs_find_referrer_type(varchar, varchar, varchar) + RETURNS varchar AS '''' DECLARE v_host ALIAS FOR $1; v_domain ALIAS FOR $2; v_url ALIAS FOR $3; BEGIN ''; - -- -- Notice how we scan through the results of a query in a FOR loop -- using the FOR <record> construct. - -- FOR referrer_keys IN SELECT * FROM cs_referrer_keys ORDER BY try_order LOOP a_output := a_output || '' IF v_'' || referrer_keys.kind || '' LIKE '''''''''' @@ -2666,47 +2428,60 @@ BEGIN || referrer_keys.referrer_type || ''''''; END IF;''; END LOOP; - a_output := a_output || '' RETURN NULL; END; '''' LANGUAGE ''''plpgsql'''';''; + a_output := a_output || '' RETURN NULL; END; '''' LANGUAGE plpgsql;''; - -- This works because we are not substituting any variables - -- Otherwise it would fail. Look at PERFORM for another way to run functions + -- EXECUTE will work because we are not substituting any variables. + -- Otherwise it would fail. Look at PERFORM for another way to run functions. EXECUTE a_output; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; - - - A Procedure with a lot of String Manipulation and <literal>OUT</> Parameters - + + shows how to port a function + with OUT parameters and string manipulation. + PostgreSQL does not have an + instr function, but you can work around it + using a combination of other functions. In there is a + PL/pgSQL implementation of + instr that you can use to make your porting + easier. + + + + Porting a Procedure With String Manipulation and + <literal>OUT</> Parameters from <application>PL/SQL</> to + <application>PL/pgSQL</> The following Oracle PL/SQL procedure is used to parse a URL and - return several elements (host, path and query). It is an - procedure because in PL/pgSQL functions only one value can be returned - (see ). In + return several elements (host, path, and query). + PL/pgSQL functions can return only one value. In PostgreSQL, one way to work around this is to split the procedure in three different functions: one to return the host, another for - the path and another for the query. + the path, and another for the query. + + This is the Oracle version: CREATE OR REPLACE PROCEDURE cs_parse_url( v_url IN VARCHAR, v_host OUT VARCHAR, -- This will be passed back v_path OUT VARCHAR, -- This one too v_query OUT VARCHAR) -- And this one -is +IS a_pos1 INTEGER; a_pos2 INTEGER; -begin +BEGIN v_host := NULL; v_path := NULL; v_query := NULL; - a_pos1 := instr(v_url, '//'); -- PostgreSQL doesn't have an instr function + a_pos1 := instr(v_url, '//'); IF a_pos1 = 0 THEN RETURN; @@ -2732,28 +2507,30 @@ END; / show errors; + - Here is how this procedure could be translated for PostgreSQL: + Here is how the PL/pgSQL function that returns + the host part could look like: -CREATE OR REPLACE FUNCTION cs_parse_url_host(VARCHAR) RETURNS VARCHAR AS ' -DECLARE - v_url ALIAS FOR $1; - v_host VARCHAR; - v_path VARCHAR; - a_pos1 INTEGER; - a_pos2 INTEGER; - a_pos3 INTEGER; +CREATE OR REPLACE FUNCTION cs_parse_url_host(varchar) RETURNS varchar AS ' +DECLARE + v_url ALIAS FOR $1; + v_host varchar; + v_path varchar; + a_pos1 integer; + a_pos2 integer; + a_pos3 integer; BEGIN v_host := NULL; - a_pos1 := instr(v_url,''//''); + a_pos1 := instr(v_url, ''//''); IF a_pos1 = 0 THEN RETURN ''''; -- Return a blank END IF; - a_pos2 := instr(v_url,''/'',a_pos1 + 2); + a_pos2 := instr(v_url,''/'',a_pos1 + 2); IF a_pos2 = 0 THEN v_host := substr(v_url, a_pos1 + 2); v_path := ''/''; @@ -2763,37 +2540,21 @@ BEGIN v_host := substr(v_url, a_pos1 + 2, a_pos2 - a_pos1 - 2 ); RETURN v_host; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; - - - PostgreSQL does not have an instr function, - so you can work around it using a combination of other functions. - I got tired of doing this and created my own - instr functions that behave exactly like - Oracle's (it makes life easier). See the for the code. - - - - - - - Procedures - - - Oracle procedures give a little more - flexibility to the developer because nothing needs to be - explicitly returned, but it can be through the use of - INOUT or OUT parameters. + shows how to port a procedure + that uses numerous features that are specific to Oracle. - - An example: + + Porting a Procedure from <application>PL/SQL</> to <application>PL/pgSQL</> + + + The Oracle version: CREATE OR REPLACE PROCEDURE cs_create_job(v_job_id IN INTEGER) IS @@ -2802,9 +2563,7 @@ CREATE OR REPLACE PROCEDURE cs_create_job(v_job_id IN INTEGER) IS BEGIN LOCK TABLE cs_jobs IN EXCLUSIVE MODE; - SELECT count(*) INTO a_running_job_count - FROM cs_jobs - WHERE end_stamp IS NULL; + SELECT count(*) INTO a_running_job_count FROM cs_jobs WHERE end_stamp IS NULL; IF a_running_job_count > 0 THEN COMMIT; -- free lock @@ -2827,13 +2586,13 @@ show errors Procedures like this can be easily converted into PostgreSQL - functions returning an INTEGER. This procedure in + functions returning an integer. This procedure in particular is interesting because it can teach us some things: - There is no pragma statement in PostgreSQL. + There is no PRAGMA statement in PostgreSQL. @@ -2846,11 +2605,10 @@ show errors - You also cannot have transactions in PL/pgSQL procedures. The + You also cannot have transactions in PL/pgSQL functions. The entire function (and other functions called from therein) is - executed in a transaction and PostgreSQL rolls back the results if - something goes wrong. Therefore only one - BEGIN statement is allowed. + executed in one transaction and PostgreSQL rolls back the transaction if + something goes wrong. @@ -2864,36 +2622,30 @@ show errors - So let's see one of the ways we could port this procedure to PL/pgSQL: + This is how we could port this procedure to PL/pgSQL: -CREATE OR REPLACE FUNCTION cs_create_job(INTEGER) RETURNS INTEGER AS ' +CREATE OR REPLACE FUNCTION cs_create_job(integer) RETURNS integer AS ' DECLARE v_job_id ALIAS FOR $1; - a_running_job_count INTEGER; - a_num INTEGER; - -- PRAGMA AUTONOMOUS_TRANSACTION; + a_running_job_count integer; + a_num integer; BEGIN LOCK TABLE cs_jobs IN EXCLUSIVE MODE; - SELECT count(*) INTO a_running_job_count - FROM cs_jobs - WHERE end_stamp IS NULL; + SELECT count(*) INTO a_running_job_count FROM cs_jobs WHERE end_stamp IS NULL; IF a_running_job_count > 0 THEN - -- COMMIT; -- free lock RAISE EXCEPTION ''Unable to create a new job: a job is currently running.''; END IF; DELETE FROM cs_active_job; INSERT INTO cs_active_job(job_id) VALUES (v_job_id); - SELECT count(*) into a_num - FROM cs_jobs - WHERE job_id=v_job_id; + SELECT count(*) INTO a_num FROM cs_jobs WHERE job_id=v_job_id; IF NOT FOUND THEN -- If nothing was returned in the last query -- This job is not in the table so lets insert it. - INSERT INTO cs_jobs(job_id, start_stamp) VALUES (v_job_id, sysdate()); + INSERT INTO cs_jobs(job_id, start_stamp) VALUES (v_job_id, current_timestamp); RETURN 1; ELSE RAISE NOTICE ''Job already running.''; @@ -2901,7 +2653,7 @@ BEGIN RETURN 0; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; @@ -2912,102 +2664,29 @@ END; + - - - Packages - - - - - I haven't done much with packages myself, so if there are - mistakes here, please let me know. - - - - - Packages are a way Oracle gives you to - encapsulate PL/SQL statements and functions into one entity, like - Java classes, where you define methods and objects. You can access - these objects/methods with a . - (dot). Here is an example of an Oracle - package from ACS 4 (the ArsDigita Community - System): - - -CREATE OR REPLACE PACKAGE BODY acs -AS - FUNCTION add_user ( - user_id IN users.user_id%TYPE DEFAULT NULL, - object_type IN acs_objects.object_type%TYPE DEFAULT 'user', - creation_date IN acs_objects.creation_date%TYPE DEFAULT sysdate, - creation_user IN acs_objects.creation_user%TYPE DEFAULT NULL, - creation_ip IN acs_objects.creation_ip%TYPE DEFAULT NULL, - ... - ) RETURN users.user_id%TYPE - IS - v_user_id users.user_id%TYPE; - v_rel_id membership_rels.rel_id%TYPE; - BEGIN - v_user_id := acs_user.new (user_id, object_type, creation_date, - creation_user, creation_ip, email, ... - RETURN v_user_id; - END; -END acs; -/ -show errors - - + + Other Things to Watch For - We port this to PostgreSQL by creating the - different objects of the Oracle package - as functions with a standard naming convention. We have to pay - attention to some other details, like the lack of default - parameters in PostgreSQL functions. The above - package would become something like this: - - -CREATE FUNCTION acs__add_user(INTEGER,INTEGER,VARCHAR,TIMESTAMP,INTEGER,INTEGER,...) -RETURNS INTEGER AS ' -DECLARE - user_id ALIAS FOR $1; - object_type ALIAS FOR $2; - creation_date ALIAS FOR $3; - creation_user ALIAS FOR $4; - creation_ip ALIAS FOR $5; - ... - v_user_id users.user_id%TYPE; - v_rel_id membership_rels.rel_id%TYPE; -BEGIN - v_user_id := acs_user__new(user_id,object_type,creation_date,creation_user,creation_ip, ...); - ... - - RETURN v_user_id; -END; -' LANGUAGE 'plpgsql'; - + This section explains a few other things to watch for when porting + Oracle PL/SQL functions to PostgreSQL. - - - - - Other Things to Watch For - - EXECUTE + <command>EXECUTE</command> - The PostgreSQL version of EXECUTE works - nicely, but you have to remember to use - quote_literal(TEXT) and - quote_string(TEXT) as described in . Constructs of the type - EXECUTE ''SELECT * from $1''; will not work - unless you use these functions. + The PL/pgSQL version of + EXECUTE works similar to the + PL/SQL version, but you have to remember to use + quote_literal(text) and + quote_string(text) as described in . Constructs of the + type EXECUTE ''SELECT * FROM $1''; will not + work unless you use these functions. @@ -3015,125 +2694,112 @@ END; Optimizing <application>PL/pgSQL</application> Functions - PostgreSQL gives you two function creation modifiers to optimize - execution: iscachable (function always returns - the same result when given the same arguments) and - isstrict (function returns NULL if any - argument is NULL). Consult the CREATE - FUNCTION reference for details. + PostgreSQL gives you two function creation + modifiers to optimize execution: the volatility (whether the + function always returns the same result when given the same + arguments) and the strictness (whether the + function returns null if any argument is null). Consult the description of + CREATE FUNCTION for details. - To make use of these optimization attributes, you have to use the - WITH modifier in your CREATE - FUNCTION statement. Something like: + To make use of these optimization attributes, your + CREATE FUNCTION statement could look something + like this: -CREATE FUNCTION foo(...) RETURNS INTEGER AS ' +CREATE FUNCTION foo(...) RETURNS integer AS ' ... -' LANGUAGE 'plpgsql' -WITH (isstrict, iscachable); +' LANGUAGE plpgsql STRICT IMMUTABLE; - - Appendix - - - - - Code for my <function>instr</function> functions - + Appendix - - This function should probably be integrated into the core. - + + This section contains the code for an Oracle-compatible + instr function that you can use to simplify + your porting efforts. + -- -- instr functions that mimic Oracle's counterpart --- Syntax: instr(string1,string2,[n],[m]) where [] denotes optional params. +-- Syntax: instr(string1, string2, [n], [m]) where [] denotes optional parameters. -- --- Searches string1 beginning at the nth character for the mth --- occurrence of string2. If n is negative, search backwards. If m is --- not passed, assume 1 (search starts at first character). --- --- by Roberto Mello (rmello@fslc.usu.edu) --- modified by Robert Gaszewski (graszew@poland.com) --- Licensed under the GPL v2 or later. +-- Searches string1 beginning at the nth character for the mth occurrence +-- of string2. If n is negative, search backwards. If m is not passed, +-- assume 1 (search starts at first character). -- -CREATE FUNCTION instr(VARCHAR,VARCHAR) RETURNS INTEGER AS ' +CREATE FUNCTION instr(varchar, varchar) RETURNS integer AS ' DECLARE pos integer; BEGIN - pos:= instr($1,$2,1); + pos:= instr($1, $2, 1); RETURN pos; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; -CREATE FUNCTION instr(VARCHAR,VARCHAR,INTEGER) RETURNS INTEGER AS ' +CREATE FUNCTION instr(varchar, varchar, varchar) RETURNS integer AS ' DECLARE string ALIAS FOR $1; string_to_search ALIAS FOR $2; beg_index ALIAS FOR $3; pos integer NOT NULL DEFAULT 0; - temp_str VARCHAR; - beg INTEGER; - length INTEGER; - ss_length INTEGER; + temp_str varchar; + beg integer; + length integer; + ss_length integer; BEGIN IF beg_index > 0 THEN + temp_str := substring(string FROM beg_index); + pos := position(string_to_search IN temp_str); - temp_str := substring(string FROM beg_index); - pos := position(string_to_search IN temp_str); - - IF pos = 0 THEN - RETURN 0; - ELSE - RETURN pos + beg_index - 1; - END IF; + IF pos = 0 THEN + RETURN 0; + ELSE + RETURN pos + beg_index - 1; + END IF; ELSE - ss_length := char_length(string_to_search); - length := char_length(string); - beg := length + beg_index - ss_length + 2; + ss_length := char_length(string_to_search); + length := char_length(string); + beg := length + beg_index - ss_length + 2; - WHILE beg > 0 LOOP - temp_str := substring(string FROM beg FOR ss_length); - pos := position(string_to_search IN temp_str); + WHILE beg > 0 LOOP + temp_str := substring(string FROM beg FOR ss_length); + pos := position(string_to_search IN temp_str); + + IF pos > 0 THEN + RETURN beg; + END IF; - IF pos > 0 THEN - RETURN beg; - END IF; + beg := beg - 1; + END LOOP; - beg := beg - 1; - END LOOP; - RETURN 0; + RETURN 0; END IF; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; --- --- Written by Robert Gaszewski (graszew@poland.com) --- Licensed under the GPL v2 or later. --- -CREATE FUNCTION instr(VARCHAR,VARCHAR,INTEGER,INTEGER) RETURNS INTEGER AS ' + +CREATE FUNCTION instr(varchar, varchar, integer, integer) RETURNS integer AS ' DECLARE string ALIAS FOR $1; string_to_search ALIAS FOR $2; beg_index ALIAS FOR $3; occur_index ALIAS FOR $4; pos integer NOT NULL DEFAULT 0; - occur_number INTEGER NOT NULL DEFAULT 0; - temp_str VARCHAR; - beg INTEGER; - i INTEGER; - length INTEGER; - ss_length INTEGER; + occur_number integer NOT NULL DEFAULT 0; + temp_str varchar; + beg integer; + i integer; + length integer; + ss_length integer; BEGIN IF beg_index > 0 THEN beg := beg_index; @@ -3179,9 +2845,8 @@ BEGIN RETURN 0; END IF; END; -' LANGUAGE 'plpgsql'; +' LANGUAGE plpgsql; -
diff --git a/doc/src/sgml/plpython.sgml b/doc/src/sgml/plpython.sgml index 7a36c074dca29a46fb62336649b8500b5cab28ad..6ec19c6e8eaf7a4a84fb89e9b86465c5f181fc9f 100644 --- a/doc/src/sgml/plpython.sgml +++ b/doc/src/sgml/plpython.sgml @@ -1,4 +1,4 @@ - + PL/Python - Python Procedural Language @@ -17,11 +17,18 @@ createlang plpython dbname. + + + If a language is installed into template1, all subsequently + created databases will have the language installed automatically. + + + Users of source packages must specially enable the build of - PL/Python during the installation process (refer to the - installation instructions for more information). Users of binary + PL/Python during the installation process. (Refer to the + installation instructions for more information.) Users of binary packages might find PL/Python in a separate subpackage. @@ -30,11 +37,11 @@ PL/Python Functions - The Python code you write gets transformed into a function. E.g., + The Python code you write gets transformed into a Python function. E.g., CREATE FUNCTION myfunc(text) RETURNS text AS 'return args[0]' - LANGUAGE 'plpython'; + LANGUAGE plpython; gets transformed into @@ -49,7 +56,7 @@ def __plpython_procedure_myfunc_23456(): If you do not provide a return value, Python returns the default - None which may or may not be what you want. The + None. The language module translates Python's None into the SQL null value. @@ -60,8 +67,8 @@ def __plpython_procedure_myfunc_23456(): myfunc example, args[0] contains whatever was passed in as the text argument. For myfunc2(text, integer), args[0] - would contain the text variable and - args[1] the integer variable. + would contain the text argument and + args[1] the integer argument. @@ -95,14 +102,14 @@ def __plpython_procedure_myfunc_23456(): TD["level"] contains one of ROW, STATEMENT, and UNKNOWN. TD["name"] contains the trigger name, and - TD["relid"] contains the relation ID of the table on + TD["relid"] contains the OID of the table on which the trigger occurred. If the trigger was called with arguments they are available in TD["args"][0] to TD["args"][(n-1)]. - If the TD["when"] is BEFORE, you may + If TD["when"] is BEFORE, you may return None or "OK" from the Python function to indicate the row is unmodified, "SKIP" to abort the event, or "MODIFY" to @@ -147,10 +154,10 @@ def __plpython_procedure_myfunc_23456(): optional limit argument causes that query to be run and the result to be returned in a result object. The result object emulates a list or dictionary object. The result object can be accessed by - row number and field name. It has these additional methods: - nrows() which returns the number of rows + row number and column name. It has these additional methods: + nrows which returns the number of rows returned by the query, and status which is the - SPI_exec return variable. The result object + SPI_exec() return value. The result object can be modified. @@ -161,27 +168,27 @@ rv = plpy.execute("SELECT * FROM my_table", 5) returns up to 5 rows from my_table. If my_table has a column - my_field, it would be accessed as + my_column, it would be accessed as -foo = rv[i]["my_field"] +foo = rv[i]["my_column"] - The second function plpy.prepare is called - with a query string and a list of argument types if you have bind - variables in the query. For example: + The second function, plpy.prepare, prepares the + execution plan for a query. It is called with a query string and a + list of parameter types, if you have parameter references in the + query. For example: plan = plpy.prepare("SELECT last_name FROM my_users WHERE first_name = $1", [ "text" ]) text is the type of the variable you will be - passing as $1. After preparing a statement, you + passing for $1. After preparing a statement, you use the function plpy.execute to run it: rv = plpy.execute(plan, [ "name" ], 5) - The limit argument is optional in the call to - plpy.execute. + The third argument is the limit and is optional. @@ -190,7 +197,7 @@ rv = plpy.execute(plan, [ "name" ], 5) in the immediate termination of that function by the server; it is not possible to trap error conditions using Python try ... catch constructs. For example, a syntax error in an - SQL statement passed to the plpy.execute() call + SQL statement passed to the plpy.execute call will terminate the function. This behavior may be changed in a future release. @@ -199,22 +206,19 @@ rv = plpy.execute(plan, [ "name" ], 5) When you prepare a plan using the PL/Python module it is automatically saved. Read the SPI documentation () for a description of what this means. - - - In order to make effective use of this across function calls one needs to use one of the persistent storage dictionaries - SD or GD, see - . For example: + SD or GD (see + ). For example: -CREATE FUNCTION usesavedplan ( ) RETURNS TRIGGER AS ' - if SD.has_key("plan"): - plan = SD["plan"] - else: - plan = plpy.prepare("SELECT 1") - SD["plan"] = plan - # rest of function -' LANGUAGE 'plpython'; +CREATE FUNCTION usesavedplan() RETURNS trigger AS ' + if SD.has_key("plan"): + plan = SD["plan"] + else: + plan = plpy.prepare("SELECT 1") + SD["plan"] = plan + # rest of function +' LANGUAGE plpython; diff --git a/doc/src/sgml/pltcl.sgml b/doc/src/sgml/pltcl.sgml index ed724153bb405b14b4a1cdfccd98046474d5efad..f3b85952dbffad468cfc31c0cdb4dabc19c96291 100644 --- a/doc/src/sgml/pltcl.sgml +++ b/doc/src/sgml/pltcl.sgml @@ -1,5 +1,5 @@ @@ -20,10 +20,6 @@ $Header: /cvsroot/pgsql/doc/src/sgml/pltcl.sgml,v 2.22 2002/09/21 18:32:53 peter trigger procedures. - - This package was originally written by Jan Wieck. - - @@ -38,19 +34,19 @@ $Header: /cvsroot/pgsql/doc/src/sgml/pltcl.sgml,v 2.22 2002/09/21 18:32:53 peter Tcl interpreter. In addition to the limited command set of safe Tcl, only a few commands are available to access the database via SPI and to raise messages via elog(). There is no way to access internals of the - database backend or to gain OS-level access under the permissions of the - PostgreSQL user ID, as a C function can do. + database server or to gain OS-level access under the permissions of the + PostgreSQL server process, as a C function can do. Thus, any unprivileged database user may be permitted to use this language. - The other, implementation restriction is that Tcl procedures cannot + The other, implementation restriction is that Tcl functions cannot be used to create input/output functions for new data types. Sometimes it is desirable to write Tcl functions that are not restricted - to safe Tcl --- for example, one might want a Tcl function that sends - mail. To handle these cases, there is a variant of PL/Tcl called PL/TclU + to safe Tcl. For example, one might want a Tcl function that sends + email. To handle these cases, there is a variant of PL/Tcl called PL/TclU (for untrusted Tcl). This is the exact same language except that a full Tcl interpreter is used. If PL/TclU is used, it must be installed as an untrusted procedural language so that only @@ -66,7 +62,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/pltcl.sgml,v 2.22 2002/09/21 18:32:53 peter library directory if Tcl/Tk support is specified in the configuration step of the installation procedure. To install PL/Tcl and/or PL/TclU in a particular database, use the - createlang script, for example + createlang program, for example createlang pltcl dbname or createlang pltclu dbname. @@ -74,58 +70,59 @@ $Header: /cvsroot/pgsql/doc/src/sgml/pltcl.sgml,v 2.22 2002/09/21 18:32:53 peter - - Description - - + PL/Tcl Functions and Arguments - To create a function in the PL/Tcl language, use the standard syntax + To create a function in the PL/Tcl language, use the standard syntax: - + CREATE FUNCTION funcname (argument-types) RETURNS return-type AS ' # PL/Tcl function body -' LANGUAGE 'pltcl'; - +' LANGUAGE pltcl; + - PL/TclU is the same, except that the language should be specified as + PL/TclU is the same, except that the language has to be specified as pltclu. The body of the function is simply a piece of Tcl script. When the function is called, the argument values are passed as - variables $1 ... $n to the + variables $1 ... $n to the Tcl script. The result is returned from the Tcl code in the usual way, with a return - statement. For example, a function + statement. + + + + For example, a function returning the greater of two integer values could be defined as: - -CREATE FUNCTION tcl_max (integer, integer) RETURNS integer AS ' + +CREATE FUNCTION tcl_max(integer, integer) RETURNS integer AS ' if {$1 > $2} {return $1} return $2 -' LANGUAGE 'pltcl' WITH (isStrict); - +' LANGUAGE pltcl STRICT; + - Note the clause WITH (isStrict), which saves us from - having to think about NULL input values: if a NULL is passed, the - function will not be called at all, but will just return a NULL + Note the clause STRICT, which saves us from + having to think about null input values: if a null value is passed, the + function will not be called at all, but will just return a null result automatically. - In a non-strict function, - if the actual value of an argument is NULL, the corresponding - $n variable will be set to an empty string. - To detect whether a particular argument is NULL, use the function + In a nonstrict function, + if the actual value of an argument is null, the corresponding + $n variable will be set to an empty string. + To detect whether a particular argument is null, use the function argisnull. For example, suppose that we wanted tcl_max - with one null and one non-null argument to return the non-null - argument, rather than NULL: + with one null and one nonnull argument to return the nonnull + argument, rather than null: - -CREATE FUNCTION tcl_max (integer, integer) RETURNS integer AS ' + +CREATE FUNCTION tcl_max(integer, integer) RETURNS integer AS ' if {[argisnull 1]} { if {[argisnull 2]} { return_null } return $2 @@ -133,8 +130,8 @@ CREATE FUNCTION tcl_max (integer, integer) RETURNS integer AS ' if {[argisnull 2]} { return $1 } if {$1 > $2} {return $1} return $2 -' LANGUAGE 'pltcl'; - +' LANGUAGE pltcl; + @@ -145,15 +142,19 @@ CREATE FUNCTION tcl_max (integer, integer) RETURNS integer AS ' - Composite-type arguments are passed to the procedure as Tcl arrays. - The element names of the array are the attribute names of the composite - type. If an attribute in the passed row - has the NULL value, it will not appear in the array! Here is - an example that defines the overpaid_2 function (as found in the - older PostgreSQL documentation) in PL/Tcl: - - -CREATE FUNCTION overpaid_2 (EMP) RETURNS bool AS ' + Composite-type arguments are passed to the function as Tcl + arrays. The element names of the array are the attribute names + of the composite type. If an attribute in the passed row has the + null value, it will not appear in the array. Here is an example: + + +CREATE TABLE employee ( + name text, + salary integer, + age integer +); + +CREATE FUNCTION overpaid(employee) RETURNS boolean AS ' if {200000.0 < $1(salary)} { return "t" } @@ -161,110 +162,109 @@ CREATE FUNCTION overpaid_2 (EMP) RETURNS bool AS ' return "t" } return "f" -' LANGUAGE 'pltcl'; - +' LANGUAGE pltcl; + - There is not currently any support for returning a composite-type + There is currently no support for returning a composite-type result value. - + - + Data Values in PL/Tcl - The argument values supplied to a PL/Tcl function's script are simply + The argument values supplied to a PL/Tcl function's code are simply the input arguments converted to text form (just as if they had been - displayed by a SELECT statement). Conversely, the return + displayed by a SELECT statement). Conversely, the return command will accept any string that is acceptable input format for the function's declared return type. So, the PL/Tcl programmer can manipulate data values as if they were just text. - + - + Global Data in PL/Tcl Sometimes it - is useful to have some global status data that is held between two - calls to a procedure or is shared between different procedures. + is useful to have some global data that is held between two + calls to a function or is shared between different functions. This is easily done since - all PL/Tcl procedures executed in one backend share the same + all PL/Tcl functions executed in one session share the same safe Tcl interpreter. So, any global Tcl variable is accessible to - all PL/Tcl procedure calls, and will persist for the duration of the - SQL client connection. (Note that PL/TclU functions likewise share + all PL/Tcl function calls and will persist for the duration of the + SQL session. (Note that PL/TclU functions likewise share global data, but they are in a different Tcl interpreter and cannot communicate with PL/Tcl functions.) - To help protect PL/Tcl procedures from unintentionally interfering + To help protect PL/Tcl functions from unintentionally interfering with each other, a global - array is made available to each procedure via the upvar - command. The global name of this variable is the procedure's internal - name and the local name is GD. It is recommended that + array is made available to each function via the upvar + command. The global name of this variable is the function's internal + name, and the local name is GD. It is recommended that GD be used - for private status data of a procedure. Use regular Tcl global variables + for private data of a function. Use regular Tcl global variables only for values that you specifically intend to be shared among multiple - procedures. + functions. An example of using GD appears in the spi_execp example below. - + - + Database Access from PL/Tcl The following commands are available to access the database from - the body of a PL/Tcl procedure: - + the body of a PL/Tcl function: - spi_exec ?-count n? ?-array name? query ?loop-body? + spi_exec ?-count n? ?-array name? command ?loop-body? - Execute an SQL query given as a string. An error in the query - causes an error to be raised. Otherwise, the command's return value + Executes an SQL command given as a string. An error in the command + causes an error to be raised. Otherwise, the return value of spi_exec is the number of rows processed (selected, inserted, updated, or - deleted) by the query, or zero if the query is a utility - statement. In addition, if the query is a SELECT statement, the + deleted) by the command, or zero if the command is a utility + statement. In addition, if the command is a SELECT statement, the values of the selected columns are placed in Tcl variables as described below. The optional -count value tells spi_exec the maximum number of rows - to process in the query. The effect of this is comparable to - setting up the query as a cursor and then saying FETCH n. + to process in the command. The effect of this is comparable to + setting up a query as a cursor and then saying FETCH n. - If the query is a SELECT statement, the values of the statement's + If the command is a SELECT statement, the values of the result columns are placed into Tcl variables named after the columns. If the -array option is given, the column values are - instead stored into the named associative array, with the SELECT + instead stored into the named associative array, with the column names used as array indexes. - If the query is a SELECT statement and no loop-body + If the command is a SELECT statement and no loop-body script is given, then only the first row of results are stored into - Tcl variables; remaining rows, if any, are ignored. No store occurs + Tcl variables; remaining rows, if any, are ignored. No storing occurs if the - SELECT returns no rows (this case can be detected by checking the - result of spi_exec). For example, + query returns no rows. (This case can be detected by checking the + result of spi_exec.) For example, - + spi_exec "SELECT count(*) AS cnt FROM pg_proc" - + will set the Tcl variable $cnt to the number of rows in the pg_proc system catalog. @@ -272,23 +272,23 @@ spi_exec "SELECT count(*) AS cnt FROM pg_proc" If the optional loop-body argument is given, it is a piece of Tcl script that is executed once for each row in the - SELECT result (note: loop-body is ignored if the given - query is not a SELECT). The values of the current row's fields + query result. (loop-body is ignored if the given + command is not a SELECT.) The values of the current row's columns are stored into Tcl variables before each iteration. For example, - + spi_exec -array C "SELECT * FROM pg_class" { elog DEBUG "have table $C(relname)" } - + - will print a DEBUG log message for every row of pg_class. This + will print a log message for every row of pg_class. This feature works similarly to other Tcl looping constructs; in particular continue and break work in the usual way inside the loop body. - If a field of a SELECT result is NULL, the target + If a column of a query result is null, the target variable for it is unset rather than being set. @@ -299,18 +299,18 @@ spi_exec -array C "SELECT * FROM pg_class" { Prepares and saves a query plan for later execution. The saved plan - will be retained for the life of the current backend. + will be retained for the life of the current session. - The query may use arguments, which are placeholders for + The query may use parameters, that is, placeholders for values to be supplied whenever the plan is actually executed. - In the query string, refer to arguments - by the symbols $1 ... $n. - If the query uses arguments, the names of the argument types + In the query string, refer to parameters + by the symbols $1 ... $n. + If the query uses parameters, the names of the parameter types must be given as a Tcl list. (Write an empty list for - typelist if no arguments are used.) - Presently, the argument types must be identified by the internal - type names shown in pg_type; for example int4 not + typelist if no parameters are used.) + Presently, the parameter types must be identified by the internal + type names shown in the system table pg_type; for example int4 not integer. @@ -325,24 +325,24 @@ spi_exec -array C "SELECT * FROM pg_class" { spi_execp ?-count n? ?-array name? ?-nulls string? queryid ?value-list? ?loop-body? - Execute a query previously prepared with spi_prepare. + Executes a query previously prepared with spi_prepare. queryid is the ID returned by - spi_prepare. If the query references arguments, - a value-list must be supplied: this - is a Tcl list of actual values for the arguments. This must be - the same length as the argument type list previously given to + spi_prepare. If the query references parameters, + a value-list must be supplied. This + is a Tcl list of actual values for the parameters. The list must be + the same length as the parameter type list previously given to spi_prepare. Omit value-list - if the query has no arguments. + if the query has no parameters. The optional value for -nulls is a string of spaces and 'n' characters telling spi_execp - which of the arguments are null values. If given, it must have exactly the + which of the parameters are null values. If given, it must have exactly the same length as the value-list. If it - is not given, all the argument values are non-NULL. + is not given, all the parameter values are nonnull. - Except for the way in which the query and its arguments are specified, + Except for the way in which the query and its parameters are specified, spi_execp works just like spi_exec. The -count, -array, and loop-body options are the same, @@ -351,7 +351,7 @@ spi_exec -array C "SELECT * FROM pg_class" { Here's an example of a PL/Tcl function using a prepared plan: - + CREATE FUNCTION t1_count(integer, integer) RETURNS integer AS ' if {![ info exists GD(plan) ]} { # prepare the saved plan on the first call @@ -361,14 +361,14 @@ CREATE FUNCTION t1_count(integer, integer) RETURNS integer AS ' } spi_execp -count 1 $GD(plan) [ list $1 $2 ] return $cnt -' LANGUAGE 'pltcl'; - +' LANGUAGE pltcl; + Note that each backslash that Tcl should see must be doubled when we type in the function, since the main parser processes - backslashes too in CREATE FUNCTION. We need backslashes inside + backslashes, too, in CREATE FUNCTION. We need backslashes inside the query string given to spi_prepare to ensure that - the $n markers will be passed through to + the $n markers will be passed through to spi_prepare as-is, and not replaced by Tcl variable substitution. @@ -383,8 +383,8 @@ CREATE FUNCTION t1_count(integer, integer) RETURNS integer AS ' Returns the OID of the row inserted by the last - spi_exec'd or spi_execp'd query, - if that query was a single-row INSERT. (If not, you get zero.) + spi_exec or spi_execp, + if the command was a single-row INSERT. (If not, you get zero.) @@ -395,18 +395,18 @@ CREATE FUNCTION t1_count(integer, integer) RETURNS integer AS ' Duplicates all occurrences of single quote and backslash characters in the given string. This may be used to safely quote strings - that are to be inserted into SQL queries given + that are to be inserted into SQL commands given to spi_exec or spi_prepare. - For example, think about a query string like + For example, think about an SQL command string like "SELECT '$val' AS ret" - where the Tcl variable val actually contains + where the Tcl variable val actually contains doesn't. This would result - in the final query string + in the final command string SELECT 'doesn't' AS ret @@ -415,21 +415,21 @@ SELECT 'doesn't' AS ret which would cause a parse error during spi_exec or spi_prepare. - The submitted query should contain + The submitted command should contain SELECT 'doesn''t' AS ret - which can be formed in PL/Tcl as + which can be formed in PL/Tcl using "SELECT '[ quote $val ]' AS ret" One advantage of spi_execp is that you don't - have to quote argument values like this, since the arguments are never - parsed as part of an SQL query string. + have to quote parameter values like this, since the parameters are never + parsed as part of an SQL command string. @@ -441,26 +441,27 @@ SELECT 'doesn''t' AS ret elog level msg - Emit a log or error message. Possible levels are + Emits a log or error message. Possible levels are DEBUG, LOG, INFO, NOTICE, WARNING, ERROR, and FATAL. Most simply emit the given message just like - the elog backend C function. ERROR + the elog C function. ERROR raises an error condition: further execution of the function is abandoned, and the current transaction is aborted. FATAL aborts the transaction and causes the current - backend to shut down (there is probably no good reason to use + session to shut down. (There is probably no good reason to use this error level in PL/Tcl functions, but it's provided for - completeness). + completeness.) + - + - + Trigger Procedures in PL/Tcl @@ -469,8 +470,8 @@ SELECT 'doesn''t' AS ret - Trigger procedures can be written in PL/Tcl. As is customary in - PostgreSQL, a procedure that's to be called + Trigger procedures can be written in PL/Tcl. + PostgreSQL requires that a procedure that is to be called as a trigger must be declared as a function with no arguments and a return type of trigger. @@ -481,16 +482,16 @@ SELECT 'doesn''t' AS ret - $TG_name + $TG_name - The name of the trigger from the CREATE TRIGGER statement. + The name of the trigger from the CREATE TRIGGER statement. - $TG_relid + $TG_relid The object ID of the table that caused the trigger procedure @@ -500,20 +501,20 @@ SELECT 'doesn''t' AS ret - $TG_relatts + $TG_relatts - A Tcl list of the table field names, prefixed with an empty list - element. So looking up an element name in the list with Tcl's + A Tcl list of the table column names, prefixed with an empty list + element. So looking up a column name in the list with Tcl's lsearch command returns the element's number starting - with 1 for the first column, the same way the fields are customarily + with 1 for the first column, the same way the columns are customarily numbered in PostgreSQL. - $TG_when + $TG_when The string BEFORE or AFTER depending on the @@ -523,7 +524,7 @@ SELECT 'doesn''t' AS ret - $TG_level + $TG_level The string ROW or STATEMENT depending on the @@ -533,44 +534,46 @@ SELECT 'doesn''t' AS ret - $TG_op + $TG_op - The string INSERT, UPDATE or + The string INSERT, UPDATE, or DELETE depending on the type of trigger call. - $NEW + $NEW - An associative array containing the values of the new table row for - INSERT/UPDATE actions, or empty for DELETE. The array is indexed - by field name. Fields that are NULL will not appear in the array! + An associative array containing the values of the new table + row for INSERT or UPDATE actions, or + empty for DELETE. The array is indexed by column + name. Columns that are null will not appear in the array. - $OLD + $OLD - An associative array containing the values of the old table row for - UPDATE/DELETE actions, or empty for INSERT. The array is indexed - by field name. Fields that are NULL will not appear in the array! + An associative array containing the values of the old table + row for UPDATE or DELETE actions, or + empty for INSERT. The array is indexed by column + name. Columns that are null will not appear in the array. - $args + $args A Tcl list of the arguments to the procedure as given in the - CREATE TRIGGER statement. These arguments are also accessible as - $1 ... $n in the procedure body. + CREATE TRIGGER statement. These arguments are also accessible as + $1 ... $n in the procedure body. @@ -582,22 +585,22 @@ SELECT 'doesn''t' AS ret The return value from a trigger procedure can be one of the strings OK or SKIP, or a list as returned by the array get Tcl command. If the return value is OK, - the operation (INSERT/UPDATE/DELETE) that fired the trigger will proceed + the operation (INSERT/UPDATE/DELETE) that fired the trigger will proceed normally. SKIP tells the trigger manager to silently suppress the operation for this row. If a list is returned, it tells PL/Tcl to return a modified row to the trigger manager that will be inserted - instead of the one given in $NEW (this works for INSERT/UPDATE - only). Needless to say that all this is only meaningful when the trigger - is BEFORE and FOR EACH ROW; otherwise the return value is ignored. + instead of the one given in $NEW. (This works for INSERT and UPDATE + only.) Needless to say that all this is only meaningful when the trigger + is BEFORE and FOR EACH ROW; otherwise the return value is ignored. Here's a little example trigger procedure that forces an integer value in a table to keep track of the number of updates that are performed on the row. For new rows inserted, the value is initialized to 0 and then - incremented on every update operation: + incremented on every update operation. - -CREATE FUNCTION trigfunc_modcount() RETURNS TRIGGER AS ' + +CREATE FUNCTION trigfunc_modcount() RETURNS trigger AS ' switch $TG_op { INSERT { set NEW($1) 0 @@ -611,24 +614,24 @@ CREATE FUNCTION trigfunc_modcount() RETURNS TRIGGER AS ' } } return [array get NEW] -' LANGUAGE 'pltcl'; +' LANGUAGE pltcl; CREATE TABLE mytab (num integer, description text, modcnt integer); CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab FOR EACH ROW EXECUTE PROCEDURE trigfunc_modcount('modcnt'); - + Notice that the trigger procedure itself does not know the column name; that's supplied from the trigger arguments. This lets the - trigger procedure be re-used with different tables. + trigger procedure be reused with different tables. - + - - Modules and the <function>unknown</> command + + Modules and the <function>unknown</> command - PL/Tcl has support for auto-loading Tcl code when used. + PL/Tcl has support for autoloading Tcl code when used. It recognizes a special table, pltcl_modules, which is presumed to contain modules of Tcl code. If this table exists, the module unknown is fetched from the table @@ -638,7 +641,7 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab While the unknown module could actually contain any initialization script you need, it normally defines a Tcl - unknown procedure that is invoked whenever Tcl does + unknown procedure that is invoked whenever Tcl does not recognize an invoked procedure name. PL/Tcl's standard version of this procedure tries to find a module in pltcl_modules that will define the required procedure. If one is found, it is @@ -653,7 +656,7 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab support scripts to maintain these tables: pltcl_loadmod, pltcl_listmod, pltcl_delmod, as well as source for the standard - unknown module share/unknown.pltcl. This module + unknown module in share/unknown.pltcl. This module must be loaded into each database initially to support the autoloading mechanism. @@ -662,9 +665,9 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab must be readable by all, but it is wise to make them owned and writable only by the database administrator. - + - + Tcl Procedure Names @@ -674,16 +677,14 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab differ. Tcl, however, requires all procedure names to be distinct. PL/Tcl deals with this by making the internal Tcl procedure names contain the object - ID of the procedure's pg_proc row as part of their name. Thus, + ID of the function from the system table pg_proc as part of their name. Thus, PostgreSQL functions with the same name - and different argument types will be different Tcl procedures too. This + and different argument types will be different Tcl procedures, too. This is not normally a concern for a PL/Tcl programmer, but it might be visible when debugging. - - - + + <application>PyGreSQL</application> - <application>Python</application> Interface @@ -334,13 +334,10 @@ make && make install API at http://www.python.org/topics/database/DatabaseAPI-2.0.html. - - - A tutorial-like introduction to the DB-API can be found at http://www2.linuxjournal.com/lj-issues/issue49/2605.html + >http://www2.linuxjournal.com/lj-issues/issue49/2605.html. @@ -365,16 +362,15 @@ make && make install - pgqueryobject that handles query results. + pgqueryobject, which handles query results. - If you want to see a simple example of the use of some of these - functions, see http://www.druid.net/rides where you can find a link at the + If you want to see a simple example of the use this module, + see where you can find a link at the bottom to the actual Python code for the page. @@ -395,9 +391,9 @@ make && make install INV_WRITE - large objects access modes, used by + Large objects access modes, used by (pgobject.)locreate and - (pglarge.)open. + (pglarge.)open @@ -408,7 +404,7 @@ make && make install SEEK_END - positional flags, used by (pglarge.)seek. + Positional flags, used by (pglarge.)seek @@ -418,7 +414,7 @@ make && make install __version__ - constants that give the current version + Constants that give the current version @@ -443,13 +439,13 @@ make && make install 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 + environment. The support for default variables can be disabled at build time by setting the option in the Python Setup file. Methods relative to this are specified by the tag [DV]. - All variables are set to None at module + All default values are set to None at module initialization, specifying that standard environment variables should be used. @@ -478,7 +474,7 @@ connect(dbname, dbname - Name of connected database (string/None). + Name of connected database (string/None) @@ -486,7 +482,7 @@ connect(dbname, host - Name of the server host (string/None). + Name of the server host (string/None) @@ -494,7 +490,7 @@ connect(dbname, port - Port used by the database server (integer/-1). + Port used by the database server (integer/-1) @@ -503,7 +499,7 @@ connect(dbname, - Options for the server (string/None). + Options for the server (string/None) @@ -513,8 +509,8 @@ connect(dbname, - File or tty for optional debug output from backend - (string/None). + File or TTY for optional debug output from server + (string/None) @@ -524,7 +520,7 @@ connect(dbname, - PostgreSQL user (string/None). + PostgreSQL user (string/None) @@ -533,7 +529,7 @@ connect(dbname, passwd - Password for user (string/None). + Password for user (string/None) @@ -601,12 +597,11 @@ connect(dbname, This method opens a connection to a specified database on a given - PostgreSQL server. You can use - key words here, as described in the - Python tutorial. The names of the + PostgreSQL server. The arguments can be + given using key words here. The names of the key words 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. + to . diff --git a/doc/src/sgml/xplang.sgml b/doc/src/sgml/xplang.sgml index 6e9e81c88cc65839a576ee49112660333b5d3616..26e05528b5f09f60868c288c67c5d9634a07af09 100644 --- a/doc/src/sgml/xplang.sgml +++ b/doc/src/sgml/xplang.sgml @@ -1,13 +1,10 @@ Procedural Languages - - Introduction - PostgreSQL allows users to add new programming languages to be available for writing functions and @@ -21,7 +18,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/xplang.sgml,v 1.20 2003/01/31 00:10:51 tgl could serve as glue between PostgreSQL and an existing implementation of a programming language. The handler itself is a special - programming language function compiled into a shared object and + C language function compiled into a shared object and loaded on demand. @@ -31,7 +28,6 @@ $Header: /cvsroot/pgsql/doc/src/sgml/xplang.sgml,v 1.20 2003/01/31 00:10:51 tgl available in the standard PostgreSQL distribution, which can serve as examples. - Installing Procedural Languages @@ -39,22 +35,24 @@ $Header: /cvsroot/pgsql/doc/src/sgml/xplang.sgml,v 1.20 2003/01/31 00:10:51 tgl A procedural language must be installed into each database where it is to be used. But procedural languages installed in - the template1 database are automatically available in all + the database template1 are automatically available in all subsequently created databases. So the database administrator can - decide which languages are available in which databases, and can make + decide which languages are available in which databases and can make some languages available by default if he chooses. For the languages supplied with the standard distribution, the - shell script createlang may be used instead - of carrying out the details by hand. For example, to install PL/pgSQL - into the template1 database, use + program createlang may be used to install the + language instead of carrying out the details by hand. For + example, to install the language + PL/pgSQL into the database + template1, use createlang plpgsql template1 The manual procedure described below is only recommended for - installing custom languages that createlang + installing custom languages that createlang does not know about. @@ -64,8 +62,11 @@ createlang plpgsql template1 - A procedural language is installed in the database in three - steps, which must be carried out by a database superuser. + A procedural language is installed in a database in three steps, + which must be carried out by a database superuser. The + createlang programm automates and . @@ -81,12 +82,13 @@ createlang plpgsql template1 The handler must be declared with the command -CREATE FUNCTION handler_function_name () - RETURNS LANGUAGE_HANDLER AS - 'path-to-shared-object' LANGUAGE C; +CREATE FUNCTION handler_function_name() + RETURNS language_handler + AS 'path-to-shared-object' + LANGUAGE C; - The special return type of LANGUAGE_HANDLER tells - the database that this function does not return one of + The special return type of language_handler tells + the database system that this function does not return one of the defined SQL data types and is not directly usable in SQL statements. @@ -99,7 +101,7 @@ CREATE FUNCTION handler_function_name () CREATE TRUSTED PROCEDURAL LANGUAGE language-name HANDLER handler_function_name; - The optional key word TRUSTED tells whether + The optional key word TRUSTED specifies that ordinary database users that have no superuser privileges should be allowed to use this language to create functions and trigger procedures. Since PL functions are executed inside the database @@ -119,20 +121,12 @@ CREATE TRUSTED PROCEDURAL LANGUAGE - In a default PostgreSQL installation, - the handler for the PL/pgSQL language - is built and installed into the library - directory. If Tcl/Tk support is configured in, the handlers for - PL/Tcl and PL/TclU are also built and installed in the same - location. Likewise, the PL/Perl and PL/PerlU handlers are built - and installed if Perl support is configured, and PL/Python is - installed if Python support is configured. The - createlang script automates and described above. + shows how the manual + installation procedure would work with the language + PL/pgSQL. - + Manual Installation of <application>PL/pgSQL</application> @@ -140,7 +134,7 @@ CREATE TRUSTED PROCEDURAL LANGUAGE PL/pgSQL language's call handler function. -CREATE FUNCTION plpgsql_call_handler () RETURNS LANGUAGE_HANDLER AS +CREATE FUNCTION plpgsql_call_handler() RETURNS language_handler AS '$libdir/plpgsql' LANGUAGE C; @@ -157,6 +151,17 @@ CREATE TRUSTED PROCEDURAL LANGUAGE plpgsql + + In a default PostgreSQL installation, + the handler for the PL/pgSQL language + is built and installed into the library + directory. If Tcl/Tk support is configured in, the handlers for + PL/Tcl and PL/TclU are also built and installed in the same + location. Likewise, the PL/Perl and PL/PerlU handlers are built + and installed if Perl support is configured, and PL/Python is + installed if Python support is configured. + +