<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-479035033564585331</id><updated>2012-02-15T23:35:58.097-08:00</updated><title type='text'>SELECT ... INTO</title><subtitle type='html'>A lesson a week in PL/pgSQL.  As I learn how to do useful things in PL/pgSQL, I will share the lessons with you.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://select-into.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://select-into.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>David Keeney</name><uri>http://www.blogger.com/profile/05853065350985701131</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>10</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-479035033564585331.post-674856013856072675</id><published>2012-01-06T08:37:00.000-08:00</published><updated>2012-01-06T08:37:00.783-08:00</updated><title type='text'>Stub: Multiple Rows</title><content type='html'>Multiple records returned by a function.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Example:&lt;br /&gt;&lt;br /&gt;convert integers to words&lt;br /&gt;'41' =&gt; 'forty one'&lt;br /&gt;'17' =&gt; 'seven teen'&lt;br /&gt;'501' =&gt; 'five hundred one'&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;illustrate converting one integer, and then returning a whole series.&lt;br /&gt;&lt;br /&gt;Also shows use of array variables&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE FUNCTION namenumbers( n INTEGER )&lt;br /&gt;RETURNS VARCHAR&lt;br /&gt;AS $$&lt;br /&gt;DECLARE &lt;br /&gt;  ary INTEGER[];&lt;br /&gt;BEGIN  &lt;br /&gt;  ary[0] := 1;&lt;br /&gt;  RETURN 'one';&lt;br /&gt;END; &lt;br /&gt;$$ LANGUAGE plpgsql;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SELECT namenumbers(%s);&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/479035033564585331-674856013856072675?l=select-into.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://select-into.blogspot.com/feeds/674856013856072675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://select-into.blogspot.com/2012/01/stub-multiple-rows.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/674856013856072675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/674856013856072675'/><link rel='alternate' type='text/html' href='http://select-into.blogspot.com/2012/01/stub-multiple-rows.html' title='Stub: Multiple Rows'/><author><name>David Keeney</name><uri>http://www.blogger.com/profile/05853065350985701131</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-479035033564585331.post-522803359998160623</id><published>2011-01-27T05:56:00.000-08:00</published><updated>2011-02-02T16:05:16.481-08:00</updated><title type='text'>More elegant forms of returning records.</title><content type='html'>Like the blog subtitle says, I learn PL/pgSQL as I go, and share the lessons with you. &amp;nbsp;A couple of weeks ago, I posted on returning a few records using OUT parameters. &amp;nbsp;I used that method because it is what I knew. &amp;nbsp;A reader, Tzvi R., kindly commented with a couple of ways to return records more gracefully. &amp;nbsp;In this post, I am giving those methods more prominence than the comment provided.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;SETOF RECORD&lt;/b&gt;&lt;br /&gt;Here the record is defined within the function, keeping the parameter list a clean list of inputs. &amp;nbsp;A limitation of this approach is that since the record definition is hidden from the caller, the column list must be provided as part of the retrieving &lt;code&gt;SELECT&lt;/code&gt;.&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;CREATE OR REPLACE FUNCTION squares(ct INT)&lt;br /&gt;   RETURNS SETOF RECORD&lt;br /&gt;AS $$&lt;br /&gt;DECLARE&lt;br /&gt;  v_rec RECORD;&lt;br /&gt;BEGIN&lt;br /&gt;  FOR i IN 0..ct-1 LOOP&lt;br /&gt;    SELECT i, POWER(i,2)::INT INTO v_rec;&lt;br /&gt;    RETURN NEXT v_rec;&lt;br /&gt;  END LOOP;&lt;br /&gt;END;&lt;br /&gt;$$&lt;br /&gt;LANGUAGE 'plpgsql' IMMUTABLE;&lt;br /&gt;&lt;br /&gt;SELECT * FROM squares(5) AS (A INT, B INT);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Line 5 creates a variable, of indeterminate shape, for the output record. &amp;nbsp;Line 8 creates the record using a &lt;code&gt;SELECT .. INTO&lt;/code&gt;, and inserts it into the variable, giving the variable its shape. &lt;br /&gt;&lt;br /&gt;Line 9 is a &lt;code&gt;RETURN NEXT..&lt;/code&gt;, which adds the record to the list of records to eventually return. &amp;nbsp;This form of &lt;code&gt;RETURN&lt;/code&gt; does &lt;b&gt;not &lt;/b&gt;terminate the function, but rather augments the return value, which gets returned whenever the function returns. &amp;nbsp;In this example, we just passively fall out the bottom of the function, but a bare &lt;code&gt;RETURN&lt;/code&gt; can be used to explicitly exit the function. &amp;nbsp;The bare &lt;code&gt;RETURN&lt;/code&gt; does not interfere with the returning of the built-up result set.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Predefined Types&lt;/b&gt;&lt;br /&gt;If you define the record type outside of the function, and use it in the function definition, the caller no longer needs a column list, but determines columns from the record type.&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;CREATE TYPE square_type AS (a INT, b INT);&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE FUNCTION squares(ct INT)&lt;br /&gt;   RETURNS SETOF square_type&lt;br /&gt;AS $$&lt;br /&gt;DECLARE&lt;br /&gt;  v_rec square_type;&lt;br /&gt;BEGIN&lt;br /&gt;  FOR i IN 0..ct-1 LOOP&lt;br /&gt;    SELECT i, POWER(i,2)::INT INTO v_rec;&lt;br /&gt;    RETURN NEXT v_rec;&lt;br /&gt;  END LOOP;&lt;br /&gt;END;&lt;br /&gt;$$&lt;br /&gt;LANGUAGE 'plpgsql' IMMUTABLE;&lt;br /&gt;&lt;br /&gt;SELECT * FROM squares(5);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Line 1 is the type definition, defining 'square_type' as a pair of &lt;code&gt;INT&lt;/code&gt; fields. &amp;nbsp;Lines 4 and 7&amp;nbsp;define the return type and the variable type as 'square_type'. &amp;nbsp;Because the caller can see the return type of the function from the function definition, it knows to treat the return values as pairs of &lt;code&gt;INT&lt;/code&gt; columns, and Line 17 can be appealingly spare. &lt;br /&gt;&lt;br /&gt;In my opinion, this is the most elegant of the three forms; it does require an additional line to define the type, and an additional addition to the namespace, but it reads easily. &amp;nbsp;The next best is the OUT parameter method. Having to specify the row shape in the calling SELECT, as in the first example above, is just too awkward.&lt;br /&gt;&lt;br /&gt;One caution is that there is no &lt;code&gt;CREATE OR REPLACE TYPE&amp;nbsp;&amp;lt;typename&amp;gt;&lt;/code&gt;, so you need to use a &lt;code&gt;DROP TYPE &amp;lt;typename&amp;gt;&lt;/code&gt; if you wish to repeat the execution of the above quoted code block complete. &amp;nbsp; In another week or two, I plan to show how to use Exception handling to gracefully absorb the errors produced by redundantly creating a type.&lt;br /&gt;&lt;br /&gt;Thank you, Tzvi, for your assistance.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;2 Feb changed function types to IMMUTABLE&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/479035033564585331-522803359998160623?l=select-into.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://select-into.blogspot.com/feeds/522803359998160623/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://select-into.blogspot.com/2011/01/more-elegant-forms-of-returning-records.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/522803359998160623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/522803359998160623'/><link rel='alternate' type='text/html' href='http://select-into.blogspot.com/2011/01/more-elegant-forms-of-returning-records.html' title='More elegant forms of returning records.'/><author><name>David Keeney</name><uri>http://www.blogger.com/profile/05853065350985701131</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-479035033564585331.post-7061832214271457288</id><published>2011-01-20T09:14:00.000-08:00</published><updated>2011-01-24T15:31:22.484-08:00</updated><title type='text'>Beyond Little Bobby Tables</title><content type='html'>The xkcd comic about 'little bobby tables' is a classic, widely known and often quoted:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://imgs.xkcd.com/comics/exploits_of_a_mom.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="122" src="http://imgs.xkcd.com/comics/exploits_of_a_mom.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://xkcd.com/327/"&gt;xkcd: Exploits of a Mom&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;the strip title is 'Exploits of a Mom', and the punch-line is 'I hope you've learned to sanitize your database inputs'.&lt;br /&gt;&lt;br /&gt;The comic gets quoted and linked often, generally to illustrate the point about sanitizing database inputs. &amp;nbsp;While the verb &lt;i&gt;sanitize&lt;/i&gt; is a terse comic-scale expression to represent avoiding SQL injection attacks, it is only part of securing the database. &amp;nbsp; To &lt;i&gt;sanitize&lt;/i&gt; is to remove the dangerous parts of the input. &amp;nbsp;Sanitizing the input in the xkcd example would presumably involve escaping the singe quote, and would have protected the database.&lt;br /&gt;&lt;br /&gt;However, there are other ways to accomplish the desired safety. &lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;If you use parameterized queries, the inputs are quoted for you. &amp;nbsp;&lt;/li&gt;&lt;li&gt;If you embed the query in a procedure, with typed parameters, the parameters are firstly forced to match the parameter type, and are then safely interpolated into the embedded query.&lt;/li&gt;&lt;li&gt;Using a limited role to run the query, a role that does not have permissions to do more hazardous operations than are required for the query. &amp;nbsp;For example a role sufficient to the xkcd example would require INSERT privilege, but not DROP nor DELETE nor CREATE.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;These three can all be used together, providing multi-layered security. &amp;nbsp;Using procedures with typed parameters will neutralize SQL injections which are matched to parameter types other than char, varchar, text, and blob. &amp;nbsp; The SQL injections which map to valid textual types reach the query, but are implicitly quoted and escaped. &amp;nbsp;Should someone defeat the escaping mechanism somehow (there have been a few exploits in PostgreSQL over the years, not necessarily in the escaping), then the role privilege limits would prevent the most severe of data losses. &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/479035033564585331-7061832214271457288?l=select-into.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://select-into.blogspot.com/feeds/7061832214271457288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://select-into.blogspot.com/2011/01/little-bobby-tables.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/7061832214271457288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/7061832214271457288'/><link rel='alternate' type='text/html' href='http://select-into.blogspot.com/2011/01/little-bobby-tables.html' title='Beyond Little Bobby Tables'/><author><name>David Keeney</name><uri>http://www.blogger.com/profile/05853065350985701131</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-479035033564585331.post-2606789478117201530</id><published>2011-01-13T19:09:00.000-08:00</published><updated>2011-01-13T20:49:15.253-08:00</updated><title type='text'>Returning a few rows</title><content type='html'>This week, we will look at returning a few rows from a function.  Database programming typically involves getting sets of records, so this output ability is natural to look for, in a programming language that runs internally to a database engine.&lt;br /&gt;&lt;br /&gt;Here's an example:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;CREATE OR REPLACE FUNCTION squares(IN ct INTEGER,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; OUT INTEGER, OUT INTEGER)&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;RETURNS SETOF RECORD&lt;br /&gt;AS $$&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;BEGIN&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; FOR i IN 0..ct-1 LOOP&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; RETURN QUERY SELECT i, POWER(i,2)::INTEGER; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;END LOOP;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;END;&lt;br /&gt;$$ LANGUAGE 'plpgsql';&lt;br /&gt;&lt;br /&gt;SELECT * FROM squares(3);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;Record Type&lt;/b&gt;&lt;br /&gt;The third line declares the return type to be SETOF RECORD, indicating that it will return a variable number of elements whose type is RECORD, to be specified elsewhere. &amp;nbsp;The 'elsewhere' is the set of OUT parameters, which indicate that the RECORD is two integers. &amp;nbsp;The OUT variables are shown in line 2. &amp;nbsp;Line 1 names the function, and declares&amp;nbsp;one input parameter to take a count limit.&lt;br /&gt;&lt;br /&gt;Lines 6 through 8 define a loop, which iterates over values from 0 to the count limit. &amp;nbsp;Each iteration runs line 7, which we will discuss in the next paragraph. &amp;nbsp;Lines 4,5,9,10 are just boilerplate that is found in every PL/pgSQL function.  Line 12 provides a context in which to run the function and display the results.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Get Record from SELECT&lt;/b&gt;&lt;br /&gt;Line 7 is: &amp;nbsp;&lt;code&gt;RETURN QUERY SELECT i, POWER(i,2):INTEGER&lt;/code&gt;. &amp;nbsp;The &lt;code&gt;RETURN&lt;/code&gt; tells us this line is part of the results. &amp;nbsp; The &lt;code&gt;QUERY&lt;/code&gt; catches the results of the subsequent &lt;code&gt;SELECT&lt;/code&gt;, as an ordinary &lt;code&gt;SELECT&lt;/code&gt; statement is not permitted in PL/pgSQL. &amp;nbsp;The &lt;code&gt;SELECT&lt;/code&gt;s in PL/pgSQL must be qualified somehow, with an &lt;code&gt;INTO&lt;/code&gt; clause or a &lt;code&gt;QUERY&lt;/code&gt; as above. &amp;nbsp;The rest of the line just creates a two part record of the counter index and its square. &amp;nbsp;The &lt;code&gt;::INTEGER&lt;/code&gt; cast avoids a type error; as &lt;code&gt;POWER&lt;/code&gt; is a NUMERIC returning function.&lt;br /&gt;&lt;br /&gt;Starting in version 8.4, there is a nice&amp;nbsp;&lt;code&gt;TABLE&lt;/code&gt; syntax, for returning records and I will discuss that in a future post. &amp;nbsp;The Rdbhost server does not support 8.4 at present, but is scheduled for an upgrade.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Multiple Records&lt;/b&gt;&lt;br /&gt;If the embedded query returns multiple records, that works too, as all such records get included; witness this variation to the example, which provides both squares and cubes:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;BEGIN&lt;br /&gt;  FOR i IN 0..ct-1 LOOP&lt;br /&gt;    RETURN QUERY       SELECT i, POWER(i,2)::INTEGER&lt;br /&gt;                 UNION SELECT i, POWER(i,3)::INTEGER; &lt;br /&gt;  END LOOP;&lt;br /&gt;END;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;b&gt;RETURN NEXT&lt;/b&gt;&lt;br /&gt;An alternate way to return records is with the &lt;code&gt;RETURN NEXT&lt;/code&gt; syntax. &amp;nbsp;This example names the two output parameters, assigns to them, and then adds their aggregate to the return set.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;DROP FUNCTION squares(INTEGER);&lt;br /&gt;CREATE OR REPLACE FUNCTION squares(IN ct INTEGER,&lt;br /&gt;                                   OUT a INTEGER, OUT b INTEGER)&lt;br /&gt;    RETURNS SETOF RECORD&lt;br /&gt;AS $$&lt;br /&gt;    BEGIN&lt;br /&gt;         FOR i IN 0..ct-1 LOOP&lt;br /&gt;             a := i;&lt;br /&gt;             b := POWER(i,2)::INTEGER; &lt;br /&gt;             RETURN NEXT;&lt;br /&gt;        END LOOP;&lt;br /&gt;    END;&lt;br /&gt;$$ LANGUAGE 'plpgsql';&lt;br /&gt;&lt;br /&gt;SELECT * FROM squares(3);&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;Line 3 names the output parameters. &amp;nbsp;Lines 8 and 9 put values into those named output parameters. &amp;nbsp;Line 10 &lt;code&gt;RETURN NEXT&lt;/code&gt; creates a record from the above parameters, and adds it to the result set. &amp;nbsp;The columns created are named after the parameters, so you might want names longer and more informative than 'a' and 'b'.&lt;br /&gt;&lt;br /&gt;Later PostgreSQL versions support more elegant syntax for returning records, but the above patterns do the job, and can still be used in current versions. &lt;br /&gt;&lt;br /&gt;That's enough for this week. &amp;nbsp;This week, we looked at getting multiple records out of the function. &amp;nbsp;Next week, we will look at getting multiple rows into the function to work with.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/479035033564585331-2606789478117201530?l=select-into.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://select-into.blogspot.com/feeds/2606789478117201530/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://select-into.blogspot.com/2011/01/returning-few-rows.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/2606789478117201530'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/2606789478117201530'/><link rel='alternate' type='text/html' href='http://select-into.blogspot.com/2011/01/returning-few-rows.html' title='Returning a few rows'/><author><name>David Keeney</name><uri>http://www.blogger.com/profile/05853065350985701131</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-479035033564585331.post-5255265414082742254</id><published>2011-01-06T19:09:00.000-08:00</published><updated>2011-01-06T19:09:26.643-08:00</updated><title type='text'>What's next..</title><content type='html'>PL/pgSQL is basically a language for writing functions. You cannot just inline a PL/pgSQL statement into a query wherever. &amp;nbsp;You define functions, and then use those functions to enhance queries, or to perform automated actions as triggers.&lt;br /&gt;&lt;br /&gt;I will not be covering PL/pgSQL comprehensively, but in terms of what I would find useful to have. &amp;nbsp;Here is a short list of stuff I hope to cover:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;How to structure a function; what are the minimum set of parts.&lt;/li&gt;&lt;li&gt;How to update or insert a record with a PL function call.&lt;/li&gt;&lt;li&gt;How to return a simple calculated value&lt;/li&gt;&lt;li&gt;How to return a few selected records, with and without enhancing calculations&lt;/li&gt;&lt;li&gt;How to return generated data as if they were a set of records&lt;/li&gt;&lt;li&gt;How to return summaries of selected data&lt;/li&gt;&lt;li&gt;How to write adaptive functions, that change their return sets based on what tables they are used on.&lt;/li&gt;&lt;li&gt;How to create a trigger from a function.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;br /&gt;I hope to write a post a week, published on Thursday, but I'm only 4 or 5 weeks in, and am already struggling to keep up, using this outline as a stop-gap post for this week. &amp;nbsp; I hope to have something more substantial for next week, and stay a post or two ahead; we will see how it goes.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/479035033564585331-5255265414082742254?l=select-into.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://select-into.blogspot.com/feeds/5255265414082742254/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://select-into.blogspot.com/2011/01/whats-next.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/5255265414082742254'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/5255265414082742254'/><link rel='alternate' type='text/html' href='http://select-into.blogspot.com/2011/01/whats-next.html' title='What&apos;s next..'/><author><name>David Keeney</name><uri>http://www.blogger.com/profile/05853065350985701131</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-479035033564585331.post-769673400772257879</id><published>2010-12-30T07:45:00.000-08:00</published><updated>2011-01-06T21:46:34.854-08:00</updated><title type='text'>A row at a time.</title><content type='html'>This week, we will be discussing functions that use exactly one row. &amp;nbsp;Functions can take individual rows as parameters, and can generate individual rows. &amp;nbsp;We will be discussing various syntax forms used for these purposes.&lt;br /&gt;&lt;br /&gt;But first, an aside:&lt;br /&gt;&lt;br /&gt;As I am trying to think up examples of good PL/pgSQL functions, I keep asking myself the question: why use a function at all, rather than plain old SQL? &amp;nbsp; One reason is that a function can execute as the creator role, even when called by another role. &amp;nbsp;So it functions as a 'sudo', allowing narrow functionality to be used by a limited privilege role that would otherwise require a higher privilege. &amp;nbsp;The 'SECURITY DEFINER' phrase in the function definition gives the function that quality. &amp;nbsp;The default would be 'SECURITY INVOKER', where the function runs at the privilege of the caller.&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;CREATE OR REPLACE FUNCTION sudo( INT, INT )&lt;br /&gt;&amp;nbsp;&amp;nbsp;RETURNS INT&lt;br /&gt;AS $$&lt;br /&gt;BEGIN&lt;br /&gt;&amp;nbsp;&amp;nbsp;-- some operation requiring elevated privilege&lt;br /&gt;END;&lt;br /&gt;$$ LANGUAGE plpgsql&lt;br /&gt;&amp;nbsp;&amp;nbsp;SECURITY DEFINER;&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;If this function was created by a role with greater privilege (such as the Super role at an Rdbhost database), then it can be executed by another role (say a Reader or Preauth role at Rdbhost), and it can affect tables and other resources as if it were executed by the greater role.&lt;br /&gt;&lt;br /&gt;Back to rows:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Table-name as Type&lt;/b&gt;&lt;br /&gt;If your function is to accept a row of a particular table, you can just use the table name as the type in the parameter list. &amp;nbsp;FWIW, you are actually specifying an aggregate type which happens to share a name with the table.&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;CREATE OR REPLACE FUNCTION ctysize( cty capitols )&lt;br /&gt; RETURNS TEXT&lt;br /&gt;AS $$&lt;br /&gt;  BEGIN&lt;br /&gt;    IF    cty.population &amp;gt; 10000000 THEN&lt;br /&gt;         RETURN 'LARGE';&lt;br /&gt;    ELSIF cty.population &amp;gt; 1000000 THEN&lt;br /&gt;         RETURN 'MEDIUM';&lt;br /&gt;    ELSE&lt;br /&gt;         RETURN 'SMALL'; &lt;br /&gt;    END IF;&lt;br /&gt;  END;&lt;br /&gt;$$ LANGUAGE plpgsql;&lt;br /&gt;&lt;br /&gt;SELECT ctysize(ROW(city,country,population)) FROM capitols LIMIT 3;&lt;/pre&gt;&lt;/blockquote&gt;This could have been done in plain SQL using a CASE, but this does illustrate how to create a ROW from a set of fields.&lt;br /&gt;&lt;br /&gt;There does not seem to be a way to pass a generic table-row as a parameter, so the above could not be written to categorize the populations of just any table, generically, that has a population field. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;ROWTYPE&lt;/b&gt;&lt;br /&gt;Before we move on to outputting rows, let's look at a syntax feature that can facilitate creating rows. &amp;nbsp;We can declare a variable, in the DECLARE section of the function, to be of a table's ROWTYPE. &amp;nbsp;It has attributes for each column of that table, which can then be assigned to. &amp;nbsp;They can be read, as well, though they will initially be NULL. &lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;DECLARE&lt;br /&gt;    ctyrec capitols%%ROWTYPE;&lt;br /&gt;  BEGIN &lt;/pre&gt;&lt;/blockquote&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;RETURNS tablename&lt;/b&gt;&lt;br /&gt;This example illustrates using a table-name as the return type, meaning that the return value will match that row type. &amp;nbsp;If you are testing these on Rdbhost, you need to double up the '%', as the '%R' gets confused with a substitution token and errors about 'too few arguments'. &lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;CREATE OR REPLACE FUNCTION newcty(name TEXT, nation TEXT, size INT)&lt;br /&gt; RETURNS capitols&lt;br /&gt;AS $$&lt;br /&gt;  DECLARE&lt;br /&gt;    ctyrec capitols%ROWTYPE;&lt;br /&gt;  BEGIN&lt;br /&gt;    ctyrec.city := name;&lt;br /&gt;    ctyrec.country := nation;&lt;br /&gt;    ctyrec.population := size;&lt;br /&gt;    RETURN ctyrec;&lt;br /&gt;  END;&lt;br /&gt;$$ LANGUAGE plpgsql;&lt;br /&gt;&lt;br /&gt;SELECT * FROM newcty('caracas','venezuala',10000);&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;The example shows how to create a new record using values provided as arguments. &amp;nbsp;It could be used as input to an SQL insert, like:&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;INSERT INTO capitols SELECT * FROM newcty('caracas','venezuala',10000);&lt;/pre&gt;&lt;/blockquote&gt;Again, this example is so lame that it could be replaced with a straight SQL query of similar complexity. &amp;nbsp;Good simple examples are hard to find. &amp;nbsp;I was disappointed to discover that the ROWTYPE declaration does not imply inclusion of constraints. &amp;nbsp;You can put values into the compound-variable that are not&amp;nbsp;permissible&amp;nbsp;in the table, and you won't learn until you attempt to insert it into the table.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;RETURNS record&lt;/b&gt;&lt;br /&gt;In the last code sample, we saw how a function can return a record by declaring the return type as the tablename, and using a record of that row-type as the return value.&lt;br /&gt;&lt;br /&gt;An alternative method of returning a row value is to use OUT parameters. &amp;nbsp;The parameters represent columns of the resulting row. &amp;nbsp;This example uses only OUT parameters, though you can use IN (and INOUT) parameters in the same parameter list. &amp;nbsp;In this example, a row of 3 values is returned, and that row is identical to the above example.&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;CREATE OR REPLACE FUNCTION newctyrec( name TEXT, nation TEXT, pop INT,&lt;br /&gt;                                      OUT TEXT, OUT TEXT, OUT INT )&lt;br /&gt; RETURNS record&lt;br /&gt;AS $$&lt;br /&gt;  BEGIN&lt;br /&gt;    $4 = name;&lt;br /&gt;    $5 = nation;&lt;br /&gt;    $6 = pop;&lt;br /&gt;  END;&lt;br /&gt;$$ LANGUAGE plpgsql;&lt;br /&gt;&lt;br /&gt;SELECT newctyrec('caracas','venezuala',1000);&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;The IN parameters are not declared as IN, since that is the default mode. &amp;nbsp;The OUT parameters are referenced by their number, as I did not give them names. &amp;nbsp;The 'record' return type represents whatever row shape is indicated by the OUT parameters. &amp;nbsp;Since the OUT parameters are not in fact, parameters at all, the confusion factor in this is high. &amp;nbsp;I recommend formatting your function definitions so that the OUT parameters are on their own line. &lt;br /&gt;&lt;br /&gt;Forget any notion of reference variables from other languages, which they may sorta look like. &amp;nbsp;They are just a list of columns to return, confusingly appended to the parameter list.&lt;br /&gt;&lt;br /&gt;The code sample above returns the following record.&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;(caracas,venezuala,1000)&lt;/pre&gt;&lt;/blockquote&gt;There it is; not the prettiest syntax, but it works.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/479035033564585331-769673400772257879?l=select-into.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://select-into.blogspot.com/feeds/769673400772257879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://select-into.blogspot.com/2010/12/row-row-your.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/769673400772257879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/769673400772257879'/><link rel='alternate' type='text/html' href='http://select-into.blogspot.com/2010/12/row-row-your.html' title='A row at a time.'/><author><name>David Keeney</name><uri>http://www.blogger.com/profile/05853065350985701131</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-479035033564585331.post-2752957245788275350</id><published>2010-12-23T09:33:00.000-08:00</published><updated>2010-12-23T09:33:05.981-08:00</updated><title type='text'>PostgresOnline: Quick Guide to Writing PLPGSQL</title><content type='html'>Just this morning, I discovered a good short series on writing PL/pgSQL functions. &amp;nbsp;These guides are written as part of the &lt;a href="http://www.postgresonline.com/"&gt;Postgres Online Journal&lt;/a&gt;, by Regina Obe and Leo Hsu. &amp;nbsp;Aside from this short series (from back in 2008, but still very relevant), there is a lot of interesting stuff there.&lt;br /&gt;&lt;br /&gt;The Quick Guide to Writing PL/pgSQL.&lt;br /&gt;&lt;a href="http://www.postgresonline.com/journal/archives/58-Quick-Guide-to-writing-PLPGSQL-Functions-Part-1.html"&gt;Part-1&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.postgresonline.com/journal/archives/76-Quick-Guide-to-writing-PLPGSQL-Functions-Part-2-.html"&gt;Part-2&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.postgresonline.com/journal/archives/83-Quick-Guide-to-writing-PLPGSQL-Functions-Part-3---NOTICES,-RECURSION,-and-more.html"&gt;Part-3&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;They also produce a nice Cheat-sheet for PL/pgSQL.&lt;br /&gt;&lt;a href="http://www.postgresonline.com/journal/archives/87-PostgreSQL-8.3-PLPGSQL-Cheatsheet-Overview.html"&gt;Cheat-Sheet&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/479035033564585331-2752957245788275350?l=select-into.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://select-into.blogspot.com/feeds/2752957245788275350/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://select-into.blogspot.com/2010/12/postgresonline-quick-guide-to-writing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/2752957245788275350'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/2752957245788275350'/><link rel='alternate' type='text/html' href='http://select-into.blogspot.com/2010/12/postgresonline-quick-guide-to-writing.html' title='PostgresOnline: Quick Guide to Writing PLPGSQL'/><author><name>David Keeney</name><uri>http://www.blogger.com/profile/05853065350985701131</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-479035033564585331.post-1866683871957567579</id><published>2010-12-16T07:41:00.000-08:00</published><updated>2010-12-16T07:41:00.134-08:00</updated><title type='text'>PL/pgSQL dabbling for free</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div style="margin-left: 1em; margin-right: 1em;"&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;If you do not have a PostgreSQL server available to dabble with, and you wish to do some learning by experimentation, free database accounts are available at Rdbhost.com.&lt;br /&gt;&lt;br /&gt;These databases are accessible through a small variety of APIs, but the straightforward way for our purposes is to use the on-site admin tool, RdbAdmin.&lt;br /&gt;&lt;br /&gt;Let's walk through the process of creating a new database on Rdbhost, and trying some simple PL/pgSQL functions there.&lt;br /&gt;&lt;br /&gt;Visit http://www.rdbhost.com. &amp;nbsp;Provide your email address in the 'Make Me a Database' form, skim the terms of service, click the box acknowledging them, and submit.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;ax href="http://3.bp.blogspot.com/_YdCQDgwrFvw/TQRpbLQhOeI/AAAAAAAAABY/XzXg0uld994/s1600/register-form.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://3.bp.blogspot.com/_YdCQDgwrFvw/TQRpbLQhOeI/AAAAAAAAABY/XzXg0uld994/s400/register-form.png" width="400" /&gt;&lt;/ax&gt;&lt;/div&gt;&lt;divx class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/divx&gt;&lt;br /&gt;&lt;br /&gt;Rdbhost will email you a registration letter, which you should get and read, and copy the password contained therein onto the clipboard. &amp;nbsp;Return to site, and login:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_YdCQDgwrFvw/TQRkEqcO7GI/AAAAAAAAABQ/7Wm-iQSc-_k/s1600/front-page-login.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="348" src="http://2.bp.blogspot.com/_YdCQDgwrFvw/TQRkEqcO7GI/AAAAAAAAABQ/7Wm-iQSc-_k/s400/front-page-login.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Upon your first login, the site will create your database, and show you your profile page, resembling this next image. &amp;nbsp;I recommend that, at this point, you change the password to something memorable to you, so that you can easily login in the future.:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_YdCQDgwrFvw/TQWV34894dI/AAAAAAAAABg/GuEdO_75g3o/s1600/profile.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="225" src="http://2.bp.blogspot.com/_YdCQDgwrFvw/TQWV34894dI/AAAAAAAAABg/GuEdO_75g3o/s400/profile.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Click the 'Rdbadmin' link to open the RdbAdmin app:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_YdCQDgwrFvw/TQWfOmXjNrI/AAAAAAAAABw/zDlvxg4mwUM/s1600/rdbadmin-sql-btn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="262" src="http://2.bp.blogspot.com/_YdCQDgwrFvw/TQWfOmXjNrI/AAAAAAAAABw/zDlvxg4mwUM/s400/rdbadmin-sql-btn.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The 'SQL Command' button will open an edit box for the entry of SQL and PL/pgSQL commands:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_YdCQDgwrFvw/TQWW-1JsSGI/AAAAAAAAABo/0mytRh1jUIA/s1600/query-entry.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="257" src="http://1.bp.blogspot.com/_YdCQDgwrFvw/TQWW-1JsSGI/AAAAAAAAABo/0mytRh1jUIA/s400/query-entry.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Enter an SQL or PL/pgSQL query and click the 'Execute' button. &amp;nbsp;Results of your query will appear above the edit box:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_YdCQDgwrFvw/TQWXQP7_UQI/AAAAAAAAABs/WosTqRKO21s/s1600/results-display0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="325" src="http://4.bp.blogspot.com/_YdCQDgwrFvw/TQWXQP7_UQI/AAAAAAAAABs/WosTqRKO21s/s400/results-display0.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;If you enter a syntax error into your PL/pgSQL, PostgreSQL will issue an error message, and RdbAdmin will display it like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_YdCQDgwrFvw/TQRh8KiQ-iI/AAAAAAAAABI/2PALWhTdWZ4/s1600/error-display.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="306" src="http://4.bp.blogspot.com/_YdCQDgwrFvw/TQRh8KiQ-iI/AAAAAAAAABI/2PALWhTdWZ4/s400/error-display.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;You will likely need data tables to experiment with before you go too far, and such tables can be created a couple of ways. &amp;nbsp;First, you can just write the raw &lt;code&gt;CREATE TABLE&lt;/code&gt; SQL into the SQL edit field, or you can use the Create Table feature:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_YdCQDgwrFvw/TQWlnQkHeiI/AAAAAAAAAB0/kBQ_5Hbu2Y4/s1600/table-feature.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="252" src="http://3.bp.blogspot.com/_YdCQDgwrFvw/TQWlnQkHeiI/AAAAAAAAAB0/kBQ_5Hbu2Y4/s400/table-feature.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;There are features to assist in creating views, schemata, and indexes as well, but none yet for creating&amp;nbsp;functions or triggers. &amp;nbsp;Use the SQL edit box to create functions using the raw SQL; after all, we are here to&amp;nbsp;exercise our PL/pgSQL writing abilities are we not?&lt;br /&gt;&lt;br /&gt;Next week, I will show how to create and return a table record with a PL/pgSQL function.&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/479035033564585331-1866683871957567579?l=select-into.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://select-into.blogspot.com/feeds/1866683871957567579/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://select-into.blogspot.com/2010/12/plpgsql-dabbling-for-free.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/1866683871957567579'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/1866683871957567579'/><link rel='alternate' type='text/html' href='http://select-into.blogspot.com/2010/12/plpgsql-dabbling-for-free.html' title='PL/pgSQL dabbling for free'/><author><name>David Keeney</name><uri>http://www.blogger.com/profile/05853065350985701131</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_YdCQDgwrFvw/TQRpbLQhOeI/AAAAAAAAABY/XzXg0uld994/s72-c/register-form.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-479035033564585331.post-7985422166062008950</id><published>2010-12-09T09:01:00.000-08:00</published><updated>2010-12-09T09:56:23.150-08:00</updated><title type='text'>Our first Function: Simple Record Insertion</title><content type='html'>This initial post will discuss a simple function to insert a record into a table. &amp;nbsp;The table is a simple list of capitol cities, with a city name field, and a country name field. &lt;br /&gt;&lt;br /&gt;Here is the function definition, and line by line explanation will follow:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;CREATE OR REPLACE FUNCTION  addcity&lt;br /&gt;  ( cty VARCHAR, cntry VARCHAR ) &lt;br /&gt;  RETURNS void&lt;br /&gt;AS $$&lt;br /&gt;BEGIN&lt;br /&gt;  INSERT INTO capitols (city,country)&lt;br /&gt;                VALUES (cty,cntry);&lt;br /&gt;END;&lt;br /&gt;$$ LANGUAGE plpgsql;&lt;br /&gt;&lt;br /&gt;SELECT * FROM addcity('paris','france');&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Lines 1,4,5,8,9 are pretty much boiler plate, and you will see a similar elements on each PL/pgSQL function you ever read or write. &amp;nbsp;The others are specific to the purpose of the function. &amp;nbsp;Now line by line:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The &lt;code&gt;Create or replace function&lt;/code&gt; statement creates the function, over-writing any previous function with that &lt;i&gt;signature&lt;/i&gt;. &amp;nbsp;The name, param list, and return definition combine to create the &lt;i&gt;signature&lt;/i&gt;. &amp;nbsp;addcity is the name of our function, as it adds a city to our table.&lt;/li&gt;&lt;li&gt;Line 2 is the parameter list, listing names and types of parameters passed to the function. &amp;nbsp;The type is required, the name is not. &amp;nbsp;There are automatic variables named $1, $2, etc for the parameters. &amp;nbsp;Note that we name the parameters *differently* than the fields. &amp;nbsp; Where the string 'cty' is used, it refers to the first parameter; there is no way to override that, so if the first parameter was named 'city', it would mask the field with the same name, and make the field unreferenceable. &amp;nbsp;So use unique names for parameters.&lt;/li&gt;&lt;li&gt;The return value must be specified; since we do not have a meaningful return value, we indicate so with the 'RETURN void' statement. Return values may be indicated with OUT parameters, but that is outside the scope of this discussion.&lt;/li&gt;&lt;li&gt;The '$$' symbol is known as a dollar quote, and opens a quoted string. &amp;nbsp;The dollar quote can be any non-whitespace characters between '$' signs, and must match at the beginning and end of the string.&lt;/li&gt;&lt;li&gt;Each PG/pgSQL block is bounded by 'BEGIN' and 'END' statements.&lt;/li&gt;&lt;li&gt;These two lines are just SQL with parameters interpolated into it by name. &amp;nbsp;See note 2 for why parameters are named differently than the fields.&lt;/li&gt;&lt;li value="8"&gt;This ends the body of the function&lt;/li&gt;&lt;li&gt;The '$$' closes the definition string, and the remainder of the line tells PostgreSQL that the function is in PL/pgSQL language.&lt;/li&gt;&lt;li value="11"&gt;The SELECT statement provides a context for calling the function. &amp;nbsp;Just calling the function by itself is a syntax error, so we provide a no-results SELECT to call from.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;The SQL in line 6 uses variable names where substitution parameters would be acceptable. &amp;nbsp;If you were to, for example, try to use a variable for the table name, it would fail with an error.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Say for some reason you had multiple tables all with city and country fields, and you wanted this function to work on a table selected at the time of the call.&amp;nbsp; You would pass the table name&amp;nbsp;as a parameter, and&amp;nbsp;you would have to use dynamic commands, created on the fly, like:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;CREATE OR REPLACE FUNCTION  addcity&lt;br /&gt;  ( tblnm VARCHAR, cty VARCHAR, cntry VARCHAR ) &lt;br /&gt;  RETURNS void&lt;br /&gt; AS $$&lt;br /&gt; BEGIN&lt;br /&gt;   EXECUTE 'INSERT INTO ' || tblnm || ' (city,country) VALUES (\'' &lt;br /&gt;                          || cty || '\',\'' || cntry || '\' ) ';&lt;br /&gt; END;&lt;br /&gt; $$ LANGUAGE plpgsql;&lt;br /&gt;&lt;br /&gt; SELECT * FROM addcity('capitols','paris','france');&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;ol&gt;&lt;li value="6"&gt;The SQL is constructed of parts concatenated together with the &lt;code&gt;'||'&lt;/code&gt; concatenation operator to create an executable query; otherwise this example is like the preceding. &lt;/li&gt;&lt;/ol&gt;This dynamic execution is clunky and fragile and insecure. For example, escaping of values is not done for the query dynamically created, &amp;nbsp;so SQL injection attacks might be a threat. &amp;nbsp;Consider carefully how you use such an approach.&lt;br /&gt;&lt;br /&gt;Next week, we will be back with an introduction to the RdbAdmin program, and a walk-through on how to experiment with PL/pgSQL in that software.&lt;br /&gt;&lt;br /&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/479035033564585331-7985422166062008950?l=select-into.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://select-into.blogspot.com/feeds/7985422166062008950/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://select-into.blogspot.com/2010/12/create-or-replace-function-addcity-cty.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/7985422166062008950'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/7985422166062008950'/><link rel='alternate' type='text/html' href='http://select-into.blogspot.com/2010/12/create-or-replace-function-addcity-cty.html' title='Our first Function: Simple Record Insertion'/><author><name>David Keeney</name><uri>http://www.blogger.com/profile/05853065350985701131</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-479035033564585331.post-6570082057796268873</id><published>2010-12-05T10:35:00.000-08:00</published><updated>2010-12-14T20:40:03.468-08:00</updated><title type='text'>Introduction</title><content type='html'>The principal language used within PostgreSQL, other than SQL itself, is PL/pgSQL.  This language adds looping and conditionals, as well as exception handling, to PostgreSQL. &lt;br /&gt;&lt;br /&gt;This language, handy though it is, doesn't seem to get a lot of discussion on web forums.  I may be underestimating the coverage, as it seems many of the PostgreSQL superusers assume it is available, and speak of it as though it were plain old PostgreSQL.  However that may be, I hope to be helpful in providing some entry level posts on how to program in PL/pgSQL. &amp;nbsp;My intention is that these posts will be 'cookbookish', with examples that work, and can be cut-and-pasted and then edited to suit your purposes. &amp;nbsp;I like to 'work from success', and create custom code by iteratively evolving working code.&lt;br /&gt;&lt;br /&gt;PL/pgSQL is for writing functions; you cannot inline PL/pgSQL just anywhere. &amp;nbsp;That said, you can write a function in PL/pgSQL and call it immediately. &amp;nbsp;It becomes a permanent element of the database you are connected to, and can be called from other sessions later, until you explicity drop it.&lt;br /&gt;&lt;br /&gt;Some presentation conventions:&lt;br /&gt;&lt;br /&gt;Code samples will be in block-quotes, like the following example, with syntax highlighting:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="brush:sql"&gt;DECLARE&lt;br /&gt;    key TEXT;&lt;br /&gt;    delta INTEGER;&lt;br /&gt;BEGIN&lt;br /&gt;    ...&lt;br /&gt;    UPDATE mytab SET val = val + delta &lt;br /&gt;     WHERE id = key;&lt;br /&gt;END;&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;SQL and PL/pgSQL language keywords will be in all caps.  Table, schema, and column names will be lower case, which is recommended practice to avoid problems due to PostgreSQLs case normalizing.&lt;br /&gt;&lt;br /&gt;All examples will have been tested, before posting, using an ordinary account at Rdbhost.com .&lt;br /&gt;&lt;br /&gt;You may know PL/pgSQL better than I do, in which case your constructive feedback, via comments, will be welcome.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Edited to add cookbook and 'work from success' items.&lt;br /&gt;Edited to add paragraph on functions&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/479035033564585331-6570082057796268873?l=select-into.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://select-into.blogspot.com/feeds/6570082057796268873/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://select-into.blogspot.com/2010/06/principal-language-used-within.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/6570082057796268873'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/479035033564585331/posts/default/6570082057796268873'/><link rel='alternate' type='text/html' href='http://select-into.blogspot.com/2010/06/principal-language-used-within.html' title='Introduction'/><author><name>David Keeney</name><uri>http://www.blogger.com/profile/05853065350985701131</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
