Thread: pl/pgsql - code review + question
Hi all, I've just written my first pl/pgsql function (code included below for you to pull apart). It takes an int4 mid (e.g. 15) and then using a select pulls out the team number (e.g. 'NE/012' and a unit number (e.g. 2) and returns the full unit number NE/012-02. I now want to write the reverse function, where I can enter 'NE/012-02' and get back the mid 15. The bit I'm stuck on is now I split the team part from the member part so that I can build the select statement. TIA Gary __BEGIN__ CREATE FUNCTION getunitno(int4) RETURNS varchar AS ' DECLARE mid ALIAS FOR $1; results RECORD; BEGIN select into results t.tnumber as tnumber, m.mnumber as mnumber from teams t, members m where t.tid =m.mteam and m.mid = mid; if results.mnumber < 10 then return results.tnumber || ''-0'' || results.mnumber; else return results.tnumber || ''-'' || results.mnumber; end if; END; ' LANGUAGE 'plpgsql'; __END__ -- Gary Stainburn This email does not contain private or confidential material as it may be snooped on by interested government parties for unknown and undisclosed purposes - Regulation of Investigatory Powers Act, 2000
Okay, I've been hit round the back of the head, and I realised that the postgresql functions (inc subtring) are available in pl/pgsql, so that's my problem solved. I've written the getmid function as below, which is basically the same as the getunitno I included in my first post. My problem now is that when I include the code to handle the record not being there, from the pgsql chapter (section 23.2.3.3) I get the following errors based of the function below. Can anyone explain why the concat of the string is failing. If I simply "raise exception ''member not found''" all works fine. __BEGIN__ (screen output) [revcom@curly revcom]$ psql -f t DROP CREATE [revcom@curly revcom]$ psql -c "select getmid('NE/011-06');" NOTICE: plpgsql: ERROR during compile of getmid near line 15 ERROR: parse error at or near "|" [revcom@curly revcom]$ __END__ __BEGIN__ (~/t which contains the function def) drop function getmid(varchar); CREATE FUNCTION getmid(varchar) RETURNS int4 AS ' DECLARE unitno ALIAS FOR $1; teamno varchar; munit int4; results RECORD; BEGIN teamno := substring(unitno from 1 for 6); munit := substring(unitno from 8); select into results m.mid as mid from teams t, members m where t.tid = m.mteam and t.tnumber = ''teamno'' and m.mnumber= munit; if not found then raise exception ''Member '' || unitno || '' not found''; return 0; end if; return results.mid; END; ' LANGUAGE 'plpgsql'; __END__ Gary On Wednesday 18 July 2001 3:10 pm, Gary Stainburn wrote: > Hi all, I've just written my first pl/pgsql function (code included below > for you to pull apart). > > It takes an int4 mid (e.g. 15) and then using a select pulls out the team > number (e.g. 'NE/012' and a unit number (e.g. 2) and returns the full unit > number NE/012-02. > > I now want to write the reverse function, where I can enter 'NE/012-02' and > get back the mid 15. The bit I'm stuck on is now I split the team part > from the member part so that I can build the select statement. > > TIA Gary > > __BEGIN__ > CREATE FUNCTION getunitno(int4) RETURNS varchar AS ' > DECLARE > mid ALIAS FOR $1; > results RECORD; > BEGIN > select into results t.tnumber as tnumber, m.mnumber as mnumber > from teams t, members m > where t.tid = m.mteam and m.mid = mid; > if results.mnumber < 10 then > return results.tnumber || ''-0'' || results.mnumber; > else > return results.tnumber || ''-'' || results.mnumber; > end if; > END; > ' LANGUAGE 'plpgsql'; > __END__ -- Gary Stainburn This email does not contain private or confidential material as it may be snooped on by interested government parties for unknown and undisclosed purposes - Regulation of Investigatory Powers Act, 2000
Hi Jeff, That's sorted my exceptions out, now all I've got to do is find out why it's not finding the record in the first place. Gary. On Wednesday 18 July 2001 4:48 pm, Jeff Eckermann wrote: > I think you need to use syntax: > raise exception ''Member % Not Found'', unitno; > > > -----Original Message----- > > From: Gary Stainburn [SMTP:gary.stainburn@ringways.co.uk] > > Sent: Wednesday, July 18, 2001 10:24 AM > > To: pgsql-sql > > Subject: Re: pl/pgsql - code review + question > > > > Okay, I've been hit round the back of the head, and I realised that the > > postgresql functions (inc subtring) are available in pl/pgsql, so that's > > my > > problem solved. > > > > I've written the getmid function as below, which is basically the same as > > the > > getunitno I included in my first post. > > > > My problem now is that when I include the code to handle the record not > > being > > there, from the pgsql chapter (section 23.2.3.3) I get the following > > errors > > based of the function below. Can anyone explain why the concat of the > > string > > is failing. If I simply "raise exception ''member not found''" all works > > fine. > > > > __BEGIN__ (screen output) > > [revcom@curly revcom]$ psql -f t > > DROP > > CREATE > > [revcom@curly revcom]$ psql -c "select getmid('NE/011-06');" > > NOTICE: plpgsql: ERROR during compile of getmid near line 15 > > ERROR: parse error at or near "|" > > [revcom@curly revcom]$ > > __END__ > > __BEGIN__ (~/t which contains the function def) > > drop function getmid(varchar); > > CREATE FUNCTION getmid(varchar) RETURNS int4 AS ' > > DECLARE > > unitno ALIAS FOR $1; > > teamno varchar; > > munit int4; > > results RECORD; > > BEGIN > > teamno := substring(unitno from 1 for 6); > > munit := substring(unitno from 8); > > select into results m.mid as mid > > from teams t, members m > > where t.tid = m.mteam and > > t.tnumber = ''teamno'' and > > m.mnumber = munit; > > if not found then > > raise exception ''Member '' || unitno || '' not found''; > > return 0; > > end if; > > return results.mid; > > END; > > ' LANGUAGE 'plpgsql'; > > __END__ > > > > Gary > > > > On Wednesday 18 July 2001 3:10 pm, Gary Stainburn wrote: > > > Hi all, I've just written my first pl/pgsql function (code included > > > > below > > > > > for you to pull apart). > > > > > > It takes an int4 mid (e.g. 15) and then using a select pulls out the > > > > team > > > > > number (e.g. 'NE/012' and a unit number (e.g. 2) and returns the full > > > > unit > > > > > number NE/012-02. > > > > > > I now want to write the reverse function, where I can enter 'NE/012-02' > > > > and > > > > > get back the mid 15. The bit I'm stuck on is now I split the team part > > > from the member part so that I can build the select statement. > > > > > > TIA Gary > > > > > > __BEGIN__ > > > CREATE FUNCTION getunitno(int4) RETURNS varchar AS ' > > > DECLARE > > > mid ALIAS FOR $1; > > > results RECORD; > > > BEGIN > > > select into results t.tnumber as tnumber, m.mnumber as mnumber > > > from teams t, members m > > > where t.tid = m.mteam and m.mid = mid; > > > if results.mnumber < 10 then > > > return results.tnumber || ''-0'' || results.mnumber; > > > else > > > return results.tnumber || ''-'' || results.mnumber; > > > end if; > > > END; > > > ' LANGUAGE 'plpgsql'; > > > __END__ > > > > -- > > Gary Stainburn > > > > This email does not contain private or confidential material as it > > may be snooped on by interested government parties for unknown > > and undisclosed purposes - Regulation of Investigatory Powers Act, 2000 > > > > > > ---------------------------(end of broadcast)--------------------------- > > TIP 3: if posting/reading through Usenet, please send an appropriate > > subscribe-nomail command to majordomo@postgresql.org so that your > > message can get through to the mailing list cleanly -- Gary Stainburn This email does not contain private or confidential material as it may be snooped on by interested government parties for unknown and undisclosed purposes - Regulation of Investigatory Powers Act, 2000
I think you need to use syntax: raise exception ''Member % Not Found'', unitno; > -----Original Message----- > From: Gary Stainburn [SMTP:gary.stainburn@ringways.co.uk] > Sent: Wednesday, July 18, 2001 10:24 AM > To: pgsql-sql > Subject: Re: pl/pgsql - code review + question > > Okay, I've been hit round the back of the head, and I realised that the > postgresql functions (inc subtring) are available in pl/pgsql, so that's > my > problem solved. > > I've written the getmid function as below, which is basically the same as > the > getunitno I included in my first post. > > My problem now is that when I include the code to handle the record not > being > there, from the pgsql chapter (section 23.2.3.3) I get the following > errors > based of the function below. Can anyone explain why the concat of the > string > is failing. If I simply "raise exception ''member not found''" all works > fine. > > __BEGIN__ (screen output) > [revcom@curly revcom]$ psql -f t > DROP > CREATE > [revcom@curly revcom]$ psql -c "select getmid('NE/011-06');" > NOTICE: plpgsql: ERROR during compile of getmid near line 15 > ERROR: parse error at or near "|" > [revcom@curly revcom]$ > __END__ > __BEGIN__ (~/t which contains the function def) > drop function getmid(varchar); > CREATE FUNCTION getmid(varchar) RETURNS int4 AS ' > DECLARE > unitno ALIAS FOR $1; > teamno varchar; > munit int4; > results RECORD; > BEGIN > teamno := substring(unitno from 1 for 6); > munit := substring(unitno from 8); > select into results m.mid as mid > from teams t, members m > where t.tid = m.mteam and > t.tnumber = ''teamno'' and > m.mnumber = munit; > if not found then > raise exception ''Member '' || unitno || '' not found''; > return 0; > end if; > return results.mid; > END; > ' LANGUAGE 'plpgsql'; > __END__ > > Gary > On Wednesday 18 July 2001 3:10 pm, Gary Stainburn wrote: > > Hi all, I've just written my first pl/pgsql function (code included > below > > for you to pull apart). > > > > It takes an int4 mid (e.g. 15) and then using a select pulls out the > team > > number (e.g. 'NE/012' and a unit number (e.g. 2) and returns the full > unit > > number NE/012-02. > > > > I now want to write the reverse function, where I can enter 'NE/012-02' > and > > get back the mid 15. The bit I'm stuck on is now I split the team part > > from the member part so that I can build the select statement. > > > > TIA Gary > > > > __BEGIN__ > > CREATE FUNCTION getunitno(int4) RETURNS varchar AS ' > > DECLARE > > mid ALIAS FOR $1; > > results RECORD; > > BEGIN > > select into results t.tnumber as tnumber, m.mnumber as mnumber > > from teams t, members m > > where t.tid = m.mteam and m.mid = mid; > > if results.mnumber < 10 then > > return results.tnumber || ''-0'' || results.mnumber; > > else > > return results.tnumber || ''-'' || results.mnumber; > > end if; > > END; > > ' LANGUAGE 'plpgsql'; > > __END__ > > -- > Gary Stainburn > > This email does not contain private or confidential material as it > may be snooped on by interested government parties for unknown > and undisclosed purposes - Regulation of Investigatory Powers Act, 2000 > > > ---------------------------(end of broadcast)--------------------------- > TIP 3: if posting/reading through Usenet, please send an appropriate > subscribe-nomail command to majordomo@postgresql.org so that your > message can get through to the mailing list cleanly
From: "Gary Stainburn" <gary.stainburn@ringways.co.uk> > Hi all, I've just written my first pl/pgsql function (code included below for > you to pull apart). Looks fine to me. Try it with "SELECT INTO" etc rather than "select into" and see if you prefer it - I find it makes the variables/fields stand out better. > It takes an int4 mid (e.g. 15) and then using a select pulls out the team > number (e.g. 'NE/012' and a unit number (e.g. 2) and returns the full unit > number NE/012-02. > > I now want to write the reverse function, where I can enter 'NE/012-02' and > get back the mid 15. The bit I'm stuck on is now I split the team part from > the member part so that I can build the select statement. Use the substr() function. Since you are careful to turn member-numbers into 2-digits you shouldn't need anything more complex. richardh=> select substr('abcdefghijkl',2,3);substr --------bcd (1 row) So, something like teamnum := substr(idstring,1,6); membnum := substr(idstring,7,2); This can get you your team/member which you can query to get your "mid". If you aren't enforcing uniqueness on (tnumber,mnumber) now might be a good time to do so. If the teamnum isn't always a fixed length search for the '-' with strpos() richardh=> select strpos('abcdefg','e');strpos -------- 5 HTH - Richard Huxton
If the string will always be in that general form, use substring & position functions (see "String Functions and Operators" in the docs. Example: unit_number := substr(team_number, strpos(team_number, ''-'') + 1); If you don't want the leading zero, you could make make the "+1" into "+2". If you might have more than one leading zero, you could use ltrim: unit_number := ltrim(unit_number, ''0''); > -----Original Message----- > From: Gary Stainburn [SMTP:gary.stainburn@ringways.co.uk] > Sent: Wednesday, July 18, 2001 9:10 AM > To: pgsql-sql > Subject: pl/pgsql - code review + question > > Hi all, I've just written my first pl/pgsql function (code included below > for > you to pull apart). > > It takes an int4 mid (e.g. 15) and then using a select pulls out the team > number (e.g. 'NE/012' and a unit number (e.g. 2) and returns the full unit > > number NE/012-02. > > I now want to write the reverse function, where I can enter 'NE/012-02' > and > get back the mid 15. The bit I'm stuck on is now I split the team part > from > the member part so that I can build the select statement. > > TIA Gary > > __BEGIN__ > CREATE FUNCTION getunitno(int4) RETURNS varchar AS ' > DECLARE > mid ALIAS FOR $1; > results RECORD; > BEGIN > select into results t.tnumber as tnumber, m.mnumber as mnumber > from teams t, members m > where t.tid = m.mteam and m.mid = mid; > if results.mnumber < 10 then > return results.tnumber || ''-0'' || results.mnumber; > else > return results.tnumber || ''-'' || results.mnumber; > end if; > END; > ' LANGUAGE 'plpgsql'; > __END__ > > -- > Gary Stainburn > > This email does not contain private or confidential material as it > may be snooped on by interested government parties for unknown > and undisclosed purposes - Regulation of Investigatory Powers Act, 2000 > > > ---------------------------(end of broadcast)--------------------------- > TIP 4: Don't 'kill -9' the postmaster
From: "Gary Stainburn" <gary.stainburn@ringways.co.uk> > My problem now is that when I include the code to handle the record not being > there, from the pgsql chapter (section 23.2.3.3) I get the following errors Hey - stop that! If everyone starts reading the docs and quoting chapter refs all we'll be left with are *difficult* questions ;-) > based of the function below. Can anyone explain why the concat of the string > is failing. If I simply "raise exception ''member not found''" all works fine. Yep - this one keeps getting me too. > raise exception ''Member '' || unitno || '' not found''; RAISE EXCEPTION ''Member % not found'', unitno; Don't know why the parser for RAISE doesn't like string concat. Possibly because it maps to the elog() error-reporting function underneath. - Richard Huxton
Gary Stainburn <gary.stainburn@ringways.co.uk> writes: > My problem now is that when I include the code to handle the record > not being there, from the pgsql chapter (section 23.2.3.3) I get the > following errors based of the function below. IIRC, there's an erroneous example in the 7.1 plpgsql documentation: RAISE doesn't actually accept an expression for its string parameter, only a literal (and only simple variables for the additional parameters). So you need to write something like raise exception ''Member % not found'', unitno; regards, tom lane
"Richard Huxton" <dev@archonet.com> writes: > Don't know why the parser for RAISE doesn't like string concat. Laziness ;-). Someone should fix plpgsql so that RAISE does take expressions, not just literals and simple variables. It probably wouldn't be a big change, but I've no time to look at it myself; any volunteers out there? regards, tom lane
Tom, Folks, > > Don't know why the parser for RAISE doesn't like string concat. > > Laziness ;-). Someone should fix plpgsql so that RAISE does take > expressions, not just literals and simple variables. It probably > wouldn't be a big change, but I've no time to look at it myself; > any volunteers out there? While you're (whoever) at it, OFFSET won't take expressions either. -Josh ______AGLIO DATABASE SOLUTIONS___________________________ Josh Berkus Complete informationtechnology josh@agliodbs.com and data management solutions (415) 565-7293 for law firms, small businesses fax 621-2533 and non-profit organizations. San Francisco
From: "Tom Lane" <tgl@sss.pgh.pa.us> > "Richard Huxton" <dev@archonet.com> writes: > > Don't know why the parser for RAISE doesn't like string concat. > > Laziness ;-). Someone should fix plpgsql so that RAISE does take > expressions, not just literals and simple variables. It probably > wouldn't be a big change, but I've no time to look at it myself; > any volunteers out there? False Laziness perhaps (spot my Perl background) OK - stick me down for having a look at it. Had a quick peek and I think it's within my abilities. Give me a couple of weeks, because I haven't looked at a YACC file since my university days (gram.y is YACC isn't it?) I'll read up the rules for submitting patches and get something workable back by August 3rd. Josh - if I try and do OFFSET at the same time (presumably it's the same change) do you fancy acting as a sanity test site? - Richard Huxton
From: "Richard Huxton" <dev@archonet.com> > False Laziness perhaps (spot my Perl background) > > OK - stick me down for having a look at it. Had a quick peek and I think > it's within my abilities. Give me a couple of weeks, because I haven't > looked at a YACC file since my university days (gram.y is YACC isn't it?) > > I'll read up the rules for submitting patches and get something workable > back by August 3rd. > > Josh - if I try and do OFFSET at the same time (presumably it's the same > change) do you fancy acting as a sanity test site? What am I talking about - OFFSET is going to be parsed by the SQL parser not the plpgsql parser. Ignore me I'm blathering - Richard Huxton
Richard, > > Josh - if I try and do OFFSET at the same time (presumably it's the > same > > change) do you fancy acting as a sanity test site? > > What am I talking about - OFFSET is going to be parsed by the SQL > parser not > the plpgsql parser. Not so, not so! Try the following two PL/pgSQL functions: DECLARE h INT; k INT; a_row a%ROWTYPE; BEGIN h := 1; k := 3; SELECT * FROM a INTO a_row ORDER BY a.1 LIMIT 1 OFFSET (h + k); END; ... blows up, but ... DECLARE h INT; k INT; a_row a%ROWTYPE; BEGIN h := 1; k := (3 + h); SELECT * FROM a INTO a_row ORDER BY a.1 LIMIT 1 OFFSET k; END; ... works. As far as I can tell, PL/pgSQL is not evaluating the expression before passing it on to the SQL parser. Or is the SQL parser supposed to accept (1 + 3) .... Oh, I see what you mean. Sorry! Should I bug-traq this problem? -Josh ______AGLIO DATABASE SOLUTIONS___________________________ Josh Berkus Complete information technology josh@agliodbs.com and data management solutions (415) 565-7293 for law firms, small businesses fax 621-2533 and non-profit organizations. San Francisco
Attachment
> Josh - if I try and do OFFSET at the same time (presumably it's the same > change) do you fancy acting as a sanity test site? > Both LIMIT and OFFSET seem to have that restriction. I will add this to the TODO list. -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 853-3000+ If your life is a hard drive, | 830 Blythe Avenue + Christ can be your backup. | Drexel Hill, Pennsylvania19026
Bruce, while you're at TO-DO list additions: I'd like to have a construct like: loop if <condition> then next [loop]; end if; [more statements] end loop; I want to be able to skip to the next iteration of the loop, if a certain condition is met but I do not want to exit the loop all together. There doesn't seem to be functionality for that right now. Best regards, Chris ----- Original Message ----- From: "Bruce Momjian" <pgman@candle.pha.pa.us> [...] > > Both LIMIT and OFFSET seem to have that restriction. I will add this to > the TODO list. > _________________________________________________________ Do You Yahoo!? Get your free @yahoo.com address at http://mail.yahoo.com
> Bruce, > > while you're at TO-DO list additions: > > I'd like to have a construct like: > > loop > if <condition> then > next [loop]; > end if; > [more statements] > end loop; > > I want to be able to skip to the next iteration of the loop, if a certain > condition is met but I do not want to exit the loop all together. There > doesn't seem to be functionality for that right now. You want C 'continue' statement. Is that for Pl/pgSQL? -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 853-3000+ If your life is a hard drive, | 830 Blythe Avenue + Christ can be your backup. | Drexel Hill, Pennsylvania19026
Bruce, Chris, A lot of us would like a fuller PL/SQL implementation in PL/pgSQL. However, Jan is busy with other things and I don't see anyone stepping up to the plate to take on the project. -Josh ______AGLIO DATABASE SOLUTIONS___________________________ Josh Berkus Complete informationtechnology josh@agliodbs.com and data management solutions (415) 565-7293 for law firms, small businesses fax 621-2533 and non-profit organizations. San Francisco
Josh, I would love to do something like that. Unfortunately, on a scale from 1 to 10, my C knowledge is about minus 5. Maybe, if my current project makes any profit and I don't have to work for a boss any longer, I might find some time to learn how to program in C and then, I might add the one or other thing. I'd love to see pl/pgsql develop into what I saw the other day in the Oracle PL/SQL book.. Best regards, Chris ----- Original Message ----- From: "Josh Berkus" <josh@agliodbs.com> To: "Bruce Momjian" <pgman@candle.pha.pa.us>; "Chris Ruprecht" <chrup999@yahoo.com> Cc: "p-sql" <pgsql-sql@postgresql.org> Sent: Friday, July 20, 2001 3:42 PM Subject: Re: [SQL] TODO List > Bruce, Chris, > > A lot of us would like a fuller PL/SQL implementation in PL/pgSQL. > However, Jan is busy with other things and I don't see anyone stepping > up to the plate to take on the project. > > -Josh > > ______AGLIO DATABASE SOLUTIONS___________________________ > Josh Berkus > Complete information technology josh@agliodbs.com > and data management solutions (415) 565-7293 > for law firms, small businesses fax 621-2533 > and non-profit organizations. San Francisco _________________________________________________________ Do You Yahoo!? Get your free @yahoo.com address at http://mail.yahoo.com