156
Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

  • Upload
    others

  • View
    17

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Learning PL/pgSQLDavid Wheeler

Kineticode

Portland PostgreSQL Users Group2006-07-19

1

Page 2: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

2

Page 3: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

But First…

2

Page 4: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

3

Page 5: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

SQL on Rails!http://www.sqlonrails.org/

3

Page 6: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Learning PL/pgSQLDavid Wheeler

Kineticode

Portland PostgreSQL Users Group2006-07-19

4

Page 7: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Application Tiers

5

Page 8: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Application Tiers

Application developers used to tiers

5

Page 9: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Application Tiers

Application developers used to tiers

UI layer (Browser)

5

Page 10: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Application Tiers

Application developers used to tiers

UI layer (Browser)

Application layer (Perl, Python, Ruby, PHP)

5

Page 11: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Application Tiers

Application developers used to tiers

UI layer (Browser)

Application layer (Perl, Python, Ruby, PHP)

Database (PostgreSQL, MySQL, Oracle)

5

Page 12: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

6

Page 13: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

Application layer

6

Page 14: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

Application layer

P* languages

6

Page 15: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

Application layer

P* languages

SQL

6

Page 16: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

Application layer

P* languages

SQL

Templating

6

Page 17: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

7

Page 18: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

UI layer

7

Page 19: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

UI layer

X?HTML

7

Page 20: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

UI layer

X?HTML

CSS

7

Page 21: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

UI layer

X?HTML

CSS

JavaScript

7

Page 22: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

UI layer

X?HTML

CSS

JavaScript

AJAX

7

Page 23: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

8

Page 24: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

Database layer

8

Page 25: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

Database layer

CREATE TABLE

8

Page 26: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

Database layer

CREATE TABLE

CREATE INDEX

8

Page 27: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

Database layer

CREATE TABLE

CREATE INDEX

Foreign key constraints

8

Page 28: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

Database layer

CREATE TABLE

CREATE INDEX

Foreign key constraints

Erm…

8

Page 29: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What Have We Learned?

Database layer

CREATE TABLE

CREATE INDEX

Foreign key constraints

Erm…

Is that it?

8

Page 30: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What about the Database?

9

Page 31: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What about the Database?

What we haven’t learned

9

Page 32: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What about the Database?

What we haven’t learned

Views

9

Page 33: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What about the Database?

What we haven’t learned

Views

Rules

9

Page 34: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What about the Database?

What we haven’t learned

Views

Rules

Triggers

9

Page 35: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What about the Database?

What we haven’t learned

Views

Rules

Triggers

Domains

9

Page 36: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What about the Database?

What we haven’t learned

Views

Rules

Triggers

Domains

Aggregates

9

Page 37: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What about the Database?

What we haven’t learned

Views

Rules

Triggers

Domains

Aggregates

Functions/Stored Procedures

9

Page 38: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

10

Page 39: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Isn’t it Time We Changed That?

10

Page 40: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

PostgreSQL Functions

11

Page 41: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

PostgreSQL FunctionsSQL

PL/Perl

PL/Python

PL/TCL

PL/Ruby

PL/Java

PL/PHP

PL/pgSQL11

Page 42: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What is PL/pgSQL?

12

Page 43: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What is PL/pgSQL?

Procedural programming language

12

Page 44: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What is PL/pgSQL?

Procedural programming language

More powerful than SQL

12

Page 45: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What is PL/pgSQL?

Procedural programming language

More powerful than SQL

Variables

12

Page 46: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What is PL/pgSQL?

Procedural programming language

More powerful than SQL

Variables

Conditionals

12

Page 47: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What is PL/pgSQL?

Procedural programming language

More powerful than SQL

Variables

Conditionals

Looping constructs

12

Page 48: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What is PL/pgSQL?

Procedural programming language

More powerful than SQL

Variables

Conditionals

Looping constructs

Exceptions

12

Page 49: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What is PL/pgSQL?

Procedural programming language

More powerful than SQL

Variables

Conditionals

Looping constructs

Exceptions

Similar to Oracle’s PL/SQL

12

Page 50: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Adding PL/pgSQL

13

Page 51: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Adding PL/pgSQL

% createlang -U postgres plpgsql template1

13

Page 52: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

CREATE FUNCTION

14

Page 53: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

CREATE FUNCTIONCREATE OR REPLACE FUNCTION name (

14

Page 54: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

CREATE FUNCTION

type,type

CREATE OR REPLACE FUNCTION name (

14

Page 55: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

CREATE FUNCTION

type,type

CREATE OR REPLACE FUNCTION name (

) RETURNS type

14

Page 56: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

CREATE FUNCTION

type,type

CREATE OR REPLACE FUNCTION name (

) RETURNS type AS $$

14

Page 57: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

CREATE FUNCTION

type,type

CREATE OR REPLACE FUNCTION name (

) RETURNS type AS $$// Function Body

14

Page 58: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

CREATE FUNCTION

type,type

CREATE OR REPLACE FUNCTION name (

) RETURNS type AS $$// Function Body

$$ LANGUAGE langname attributes;

14

Page 59: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

A Note on Examples

15

Page 60: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

A Note on Examples

Early examples calculate Fibonacci numbers

15

Page 61: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

A Note on Examples

Early examples calculate Fibonacci numbers

Fibonacci numbers are a sequence

15

Page 62: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

A Note on Examples

Early examples calculate Fibonacci numbers

Fibonacci numbers are a sequence

0, 1, 1, 2, 3, 5, 8, 13, 21…

15

Page 63: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

A Note on Examples

Early examples calculate Fibonacci numbers

Fibonacci numbers are a sequence

0, 1, 1, 2, 3, 5, 8, 13, 21…

http://en.wikipedia.org/wiki/Fibonacci_number

15

Page 64: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

A Note on Examples

Early examples calculate Fibonacci numbers

Fibonacci numbers are a sequence

0, 1, 1, 2, 3, 5, 8, 13, 21…

http://en.wikipedia.org/wiki/Fibonacci_number

Used here entirely for pedagogical purposes

15

Page 65: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

A Note on Examples

Early examples calculate Fibonacci numbers

Fibonacci numbers are a sequence

0, 1, 1, 2, 3, 5, 8, 13, 21…

http://en.wikipedia.org/wiki/Fibonacci_number

Used here entirely for pedagogical purposes

Shows off different PL/pgSQL features

15

Page 66: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

A Note on Examples

Early examples calculate Fibonacci numbers

Fibonacci numbers are a sequence

0, 1, 1, 2, 3, 5, 8, 13, 21…

http://en.wikipedia.org/wiki/Fibonacci_number

Used here entirely for pedagogical purposes

Shows off different PL/pgSQL features

Without further ado…

15

Page 67: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Recursive Fibonacci Function

16

Page 68: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Recursive Fibonacci Function

CREATE OR REPLACE FUNCTION fib ( fib_for integer) RETURNS integer AS $$BEGIN IF fib_for < 2 THEN RETURN fib_for; END IF; RETURN fib(fib_for - 2) + fib(fib_for - 1);END;$$ LANGUAGE plpgsql strict;

16

Page 69: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Recursive Fibonacci Function

CREATE OR REPLACE FUNCTION fib ( fib_for integer) RETURNS integer AS $$BEGIN IF fib_for < 2 THEN RETURN fib_for; END IF; RETURN fib(fib_for - 2) + fib(fib_for - 1);END;$$ LANGUAGE plpgsql strict;

16

Page 70: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Recursive Fibonacci Function

CREATE OR REPLACE FUNCTION fib ( fib_for integer) RETURNS integer AS $$BEGIN IF fib_for < 2 THEN RETURN fib_for; END IF; RETURN fib(fib_for - 2) + fib(fib_for - 1);END;$$ LANGUAGE plpgsql strict;

16

Page 71: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Recursive Fibonacci Function

CREATE OR REPLACE FUNCTION fib ( fib_for integer) RETURNS integer AS $$BEGIN IF fib_for < 2 THEN RETURN fib_for; END IF; RETURN fib(fib_for - 2) + fib(fib_for - 1);END;$$ LANGUAGE plpgsql strict;

16

Page 72: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Recursive Fibonacci Function

CREATE OR REPLACE FUNCTION fib ( fib_for integer) RETURNS integer AS $$BEGIN IF fib_for < 2 THEN RETURN fib_for; END IF; RETURN fib(fib_for - 2) + fib(fib_for - 1);END;$$ LANGUAGE plpgsql strict;

16

Page 73: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Recursive Fibonacci Function

CREATE OR REPLACE FUNCTION fib ( fib_for integer) RETURNS integer AS $$BEGIN IF fib_for < 2 THEN RETURN fib_for; END IF; RETURN fib(fib_for - 2) + fib(fib_for - 1);END;$$ LANGUAGE plpgsql strict;

16

Page 74: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Recursive Fibonacci Function

CREATE OR REPLACE FUNCTION fib ( fib_for integer) RETURNS integer AS $$BEGIN IF fib_for < 2 THEN RETURN fib_for; END IF; RETURN fib(fib_for - 2) + fib(fib_for - 1);END;$$ LANGUAGE plpgsql strict;

16

Page 75: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Try It

17

Page 76: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Accessing the Database

18

Page 77: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Use SQL to access relations

Accessing the Database

18

Page 78: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Use SQL to access relations

Example: Memoize the Fibonacci function

Accessing the Database

18

Page 79: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Use SQL to access relations

Example: Memoize the Fibonacci function

Create a table to store values

Accessing the Database

18

Page 80: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Use SQL to access relations

Example: Memoize the Fibonacci function

Create a table to store values

Accessing the Database

CREATE TABLE fib_cache ( num integer PRIMARY KEY, fib integer NOT NULL);

18

Page 81: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Memoized Fibonacci

19

Page 82: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Memoized FibonacciCREATE OR REPLACE FUNCTION fib_cached( fib_for int) RETURNS integer AS $$DECLARE ret integer;BEGIN if fib_for < 2 THEN RETURN fib_for; END IF;

SELECT INTO ret fib FROM fib_cache WHERE num = fib_for;

IF ret IS NULL THEN ret := fib_cached(fib_for - 2) + fib_cached(fib_for - 1); INSERT INTO fib_cache (num, fib) VALUES (fib_for, ret); END IF; RETURN ret;END;$$ LANGUAGE plpgsql strict;

19

Page 83: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Memoized FibonacciCREATE OR REPLACE FUNCTION fib_cached( fib_for int) RETURNS integer AS $$DECLARE ret integer;BEGIN if fib_for < 2 THEN RETURN fib_for; END IF;

SELECT INTO ret fib FROM fib_cache WHERE num = fib_for;

IF ret IS NULL THEN ret := fib_cached(fib_for - 2) + fib_cached(fib_for - 1); INSERT INTO fib_cache (num, fib) VALUES (fib_for, ret); END IF; RETURN ret;END;$$ LANGUAGE plpgsql strict;

19

Page 84: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Memoized FibonacciCREATE OR REPLACE FUNCTION fib_cached( fib_for int) RETURNS integer AS $$DECLARE ret integer;BEGIN if fib_for < 2 THEN RETURN fib_for; END IF;

SELECT INTO ret fib FROM fib_cache WHERE num = fib_for;

IF ret IS NULL THEN ret := fib_cached(fib_for - 2) + fib_cached(fib_for - 1); INSERT INTO fib_cache (num, fib) VALUES (fib_for, ret); END IF; RETURN ret;END;$$ LANGUAGE plpgsql strict;

19

Page 85: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Memoized FibonacciCREATE OR REPLACE FUNCTION fib_cached( fib_for int) RETURNS integer AS $$DECLARE ret integer;BEGIN if fib_for < 2 THEN RETURN fib_for; END IF;

SELECT INTO ret fib FROM fib_cache WHERE num = fib_for;

IF ret IS NULL THEN ret := fib_cached(fib_for - 2) + fib_cached(fib_for - 1); INSERT INTO fib_cache (num, fib) VALUES (fib_for, ret); END IF; RETURN ret;END;$$ LANGUAGE plpgsql strict;

19

Page 86: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Memoized FibonacciCREATE OR REPLACE FUNCTION fib_cached( fib_for int) RETURNS integer AS $$DECLARE ret integer;BEGIN if fib_for < 2 THEN RETURN fib_for; END IF;

SELECT INTO ret fib FROM fib_cache WHERE num = fib_for;

IF ret IS NULL THEN ret := fib_cached(fib_for - 2) + fib_cached(fib_for - 1); INSERT INTO fib_cache (num, fib) VALUES (fib_for, ret); END IF; RETURN ret;END;$$ LANGUAGE plpgsql strict;

19

Page 87: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Memoized FibonacciCREATE OR REPLACE FUNCTION fib_cached( fib_for int) RETURNS integer AS $$DECLARE ret integer;BEGIN if fib_for < 2 THEN RETURN fib_for; END IF;

SELECT INTO ret fib FROM fib_cache WHERE num = fib_for;

IF ret IS NULL THEN ret := fib_cached(fib_for - 2) + fib_cached(fib_for - 1); INSERT INTO fib_cache (num, fib) VALUES (fib_for, ret); END IF; RETURN ret;END;$$ LANGUAGE plpgsql strict;

19

Page 88: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Memoized FibonacciCREATE OR REPLACE FUNCTION fib_cached( fib_for int) RETURNS integer AS $$DECLARE ret integer;BEGIN if fib_for < 2 THEN RETURN fib_for; END IF;

SELECT INTO ret fib FROM fib_cache WHERE num = fib_for;

IF ret IS NULL THEN ret := fib_cached(fib_for - 2) + fib_cached(fib_for - 1); INSERT INTO fib_cache (num, fib) VALUES (fib_for, ret); END IF; RETURN ret;END;$$ LANGUAGE plpgsql strict;

19

Page 89: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Memoized FibonacciCREATE OR REPLACE FUNCTION fib_cached( fib_for int) RETURNS integer AS $$DECLARE ret integer;BEGIN if fib_for < 2 THEN RETURN fib_for; END IF;

SELECT INTO ret fib FROM fib_cache WHERE num = fib_for;

IF ret IS NULL THEN ret := fib_cached(fib_for - 2) + fib_cached(fib_for - 1); INSERT INTO fib_cache (num, fib) VALUES (fib_for, ret); END IF; RETURN ret;END;$$ LANGUAGE plpgsql strict;

19

Page 90: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Memoized FibonacciCREATE OR REPLACE FUNCTION fib_cached( fib_for int) RETURNS integer AS $$DECLARE ret integer;BEGIN if fib_for < 2 THEN RETURN fib_for; END IF;

SELECT INTO ret fib FROM fib_cache WHERE num = fib_for;

IF ret IS NULL THEN ret := fib_cached(fib_for - 2) + fib_cached(fib_for - 1); INSERT INTO fib_cache (num, fib) VALUES (fib_for, ret); END IF; RETURN ret;END;$$ LANGUAGE plpgsql strict;

19

Page 91: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Try It

20

Page 92: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Using SQL in PL/pgSQL

21

Page 93: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Any SQL can be used

Using SQL in PL/pgSQL

21

Page 94: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Any SQL can be used

Variables can be used in the SQL statements

Using SQL in PL/pgSQL

21

Page 95: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Any SQL can be used

Variables can be used in the SQL statements

SQL statements are compiled into the function

Using SQL in PL/pgSQL

21

Page 96: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Any SQL can be used

Variables can be used in the SQL statements

SQL statements are compiled into the function

Using SQL in PL/pgSQL

INSERT INTO fib_cache (num, fib)VALUES (fib_for, ret);

21

Page 97: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Any SQL can be used

Variables can be used in the SQL statements

SQL statements are compiled into the function

Using SQL in PL/pgSQL

PREPARE some_insert(integer, integer) ASINSERT INTO fib_cache (num, fib)VALUES ($1, $2);

21

Page 98: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Any SQL can be used

Variables can be used in the SQL statements

SQL statements are compiled into the function

Using SQL in PL/pgSQL

PREPARE some_insert(integer, integer) ASINSERT INTO fib_cache (num, fib)VALUES ($1, $2);

EXECUTE some_insert(fib_for, ret);

21

Page 99: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Looping Fibonacci Function

22

Page 100: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Looping Fibonacci Function

CREATE OR REPLACE FUNCTION fib_fast( fib_for int) RETURNS integer AS $$DECLARE ret integer := 0; nxt integer := 1; tmp integer;BEGIN FOR num IN 1..fib_for LOOP tmp := ret; ret := nxt; nxt := tmp + nxt; END LOOP;

RETURN ret;END;$$ LANGUAGE plpgsql strict;

22

Page 101: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Looping Fibonacci Function

CREATE OR REPLACE FUNCTION fib_fast( fib_for int) RETURNS integer AS $$DECLARE ret integer := 0; nxt integer := 1; tmp integer;BEGIN FOR num IN 1..fib_for LOOP tmp := ret; ret := nxt; nxt := tmp + nxt; END LOOP;

RETURN ret;END;$$ LANGUAGE plpgsql strict;

22

Page 102: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Looping Fibonacci Function

CREATE OR REPLACE FUNCTION fib_fast( fib_for int) RETURNS integer AS $$DECLARE ret integer := 0; nxt integer := 1; tmp integer;BEGIN FOR num IN 1..fib_for LOOP tmp := ret; ret := nxt; nxt := tmp + nxt; END LOOP;

RETURN ret;END;$$ LANGUAGE plpgsql strict;

22

Page 103: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Looping Fibonacci Function

CREATE OR REPLACE FUNCTION fib_fast( fib_for int) RETURNS integer AS $$DECLARE ret integer := 0; nxt integer := 1; tmp integer;BEGIN FOR num IN 1..fib_for LOOP tmp := ret; ret := nxt; nxt := tmp + nxt; END LOOP;

RETURN ret;END;$$ LANGUAGE plpgsql strict;

22

Page 104: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Looping Fibonacci Function

CREATE OR REPLACE FUNCTION fib_fast( fib_for int) RETURNS integer AS $$DECLARE ret integer := 0; nxt integer := 1; tmp integer;BEGIN FOR num IN 1..fib_for LOOP tmp := ret; ret := nxt; nxt := tmp + nxt; END LOOP;

RETURN ret;END;$$ LANGUAGE plpgsql strict;

22

Page 105: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Try It

23

Page 106: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Set Returning Functions

24

Page 107: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Set Returning Functions

Functions can return sets

24

Page 108: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Set Returning Functions

Functions can return sets

Similar to continuations

24

Page 109: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Set Returning Functions

Functions can return sets

Similar to continuations

Think of a SELECT on a single-column table

24

Page 110: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Set Returning Functions

Functions can return sets

Similar to continuations

Think of a SELECT on a single-column table

Set returning function used like a table

24

Page 111: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Set Returning Functions

Functions can return sets

Similar to continuations

Think of a SELECT on a single-column table

Set returning function used like a table

Easy to implement in PL/pgSQL

24

Page 112: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Fibonacci Set

25

Page 113: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Fibonacci SetCREATE OR REPLACE FUNCTION fib_fast( fib_for int) RETURNS integer AS $$DECLARE ret integer := 0; nxt integer := 1; tmp integer;BEGIN FOR num IN 1..fib_for LOOP

tmp := ret; ret := nxt; nxt := tmp + nxt; END LOOP;

RETURN ret;END;$$ LANGUAGE plpgsql strict;

25

Page 114: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Fibonacci SetCREATE OR REPLACE FUNCTION fibs_to( fib_for int) RETURNS SETOF integer AS $$DECLARE ret integer := 0; nxt integer := 1; tmp integer;BEGIN FOR num IN 1..fib_for LOOP

tmp := ret; ret := nxt; nxt := tmp + nxt; END LOOP;

RETURN ret;END;$$ LANGUAGE plpgsql strict;

25

Page 115: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Fibonacci SetCREATE OR REPLACE FUNCTION fibs_to( fib_for int) RETURNS SETOF integer AS $$DECLARE ret integer := 0; nxt integer := 1; tmp integer;BEGIN FOR num IN 1..fib_for LOOP RETURN NEXT ret; tmp := ret; ret := nxt; nxt := tmp + nxt; END LOOP;

RETURN ret;END;$$ LANGUAGE plpgsql strict;

25

Page 116: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Fibonacci SetCREATE OR REPLACE FUNCTION fibs_to( fib_for int) RETURNS SETOF integer AS $$DECLARE ret integer := 0; nxt integer := 1; tmp integer;BEGIN FOR num IN 1..fib_for LOOP RETURN NEXT ret; tmp := ret; ret := nxt; nxt := tmp + nxt; END LOOP;

RETURN NEXT ret;END;$$ LANGUAGE plpgsql strict;

25

Page 117: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Try It

26

Page 118: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Yea, but What Good are They?

27

Page 119: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Yea, but What Good are They?

I’m glad you asked

27

Page 120: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Yea, but What Good are They?

I’m glad you asked

Let’s get practical

27

Page 121: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Yea, but What Good are They?

I’m glad you asked

Let’s get practical

Let’s manage an ordered many-to-many relationship

27

Page 122: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

A Blogging App’s Schema

28

Page 123: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

A Blogging App’s SchemaCREATE TABLE entry ( id SERIAL PRIMARY KEY, title TEXT, content TEXT);

28

Page 124: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

A Blogging App’s SchemaCREATE TABLE entry ( id SERIAL PRIMARY KEY, title TEXT, content TEXT);

CREATE TABLE tag ( id SERIAL PRIMARY KEY, name text);

28

Page 125: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

A Blogging App’s SchemaCREATE TABLE entry ( id SERIAL PRIMARY KEY, title TEXT, content TEXT);

CREATE TABLE tag ( id SERIAL PRIMARY KEY, name text);

CREATE TABLE entry_coll_tag ( entry_id integer REFERENCES entry(id) ON UPDATE CASCADE ON DELETE CASCADE, tag_id integer REFERENCES tag(id) ON UPDATE CASCADE ON DELETE CASCADE, tag_order smallint, PRIMARY KEY (entry_id, tag_id));

28

Page 126: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

A Blogging App’s SchemaCREATE TABLE entry ( id SERIAL PRIMARY KEY, title TEXT, content TEXT);

CREATE TABLE tag ( id SERIAL PRIMARY KEY, name text);

CREATE TABLE entry_coll_tag ( entry_id integer REFERENCES entry(id) ON UPDATE CASCADE ON DELETE CASCADE, tag_id integer REFERENCES tag(id) ON UPDATE CASCADE ON DELETE CASCADE, tag_order smallint, PRIMARY KEY (entry_id, tag_id));

CREATE UNIQUE INDEX idx_entry_coll_tag_orderON entry_coll_tag (entry_id, tag_order);

28

Page 127: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

SELECT Tags for an Entry

29

Page 128: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

SELECT Tags for an Entry

SELECT tag.id, tag.nameFROM tag, entry_coll_tagWHERE tag.id = entry_coll_tag.tag_id AND entry_coll_tag.entry_id = 1ORDER BY entry_coll_tag.tag_order;

29

Page 129: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Try ItSELECT tag.id, tag.nameFROM tag, entry_coll_tagWHERE tag.id = entry_coll_tag.tag_id AND entry_coll_tag.entry_id = 1ORDER BY entry_coll_tag.tag_order;

30

Page 130: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Setting Tags

31

Page 131: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Setting Tags# Use prepared statements.insert = dbh.prepare('INSERT INTO entry (title, content) VALUES (?, ?)');sel_id = dbh.prepare("SELECT CURRVAL('entry_id_seq')");

ins_coll = dbh.prepare(' INSERT INTO entry_coll_tag (entry_id, tag_id, tag_order) VALUES (?, ?, ?)');

# Do everything inside a transaction.dbh.begin;

# Insert the new entry.insert.execute(entry.title, entry.content);sel_id.execute;entry.id = sel_id.fetch;

# Associate the tags with the entry.i = 0;foreach tag in (tag_array) { ins_coll.execute(entry.id, tag.id, ++i);}

# Make it so!dbh.commit;

31

Page 132: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What’s Wrong with That?

32

Page 133: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What’s Wrong with That?

It’s Slow

32

Page 134: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What’s Wrong with That?

It’s Slow

A separate query for each tag

32

Page 135: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What’s Wrong with That?

It’s Slow

A separate query for each tag

What if there were 100 tags?

32

Page 136: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What’s Wrong with That?

It’s Slow

A separate query for each tag

What if there were 100 tags?

There’s a race condition

32

Page 137: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What’s Wrong with That?

It’s Slow

A separate query for each tag

What if there were 100 tags?

There’s a race condition

Tag order can be wrong

32

Page 138: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

What’s Wrong with That?

It’s Slow

A separate query for each tag

What if there were 100 tags?

There’s a race condition

Tag order can be wrong

When two processes update tags for the same entry at the same time

32

Page 139: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

How can PL/pgSQL Help?

33

Page 140: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

How can PL/pgSQL Help?

A function can associate tags with entries

33

Page 141: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

How can PL/pgSQL Help?

A function can associate tags with entries

It can carefully control for the race condition

33

Page 142: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

How can PL/pgSQL Help?

A function can associate tags with entries

It can carefully control for the race condition

It can be called once to associate all tags with a given entry

33

Page 143: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Let’s Do It!

34

Page 144: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Let’s Do It!CREATE OR REPLACE FUNCTION entry_coll_tag_set ( obj_id integer, coll_ids integer[]) RETURNS VOID AS $$BEGIN PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;

UPDATE entry_coll_tag SET tag_order = -tag_order WHERE entry_id = obj_id

FOR iloop IN 1..array_upper(coll_ids, 1) LOOP IF coll_ids[iloop] IS NULL THEN CONTINUE; END IF;

UPDATE entry_coll_tag SET tag_order = iloop WHERE entry_id = obj_id AND tag_id = coll_ids[iloop];

IF FOUND IS false THEN INSERT INTO entry_coll_tag (entry_id, tag_id, tag_order) VALUES (obj_id, coll_ids[iloop], iloop); END IF; END LOOP;

DELETE FROM entry_coll_tag WHERE entry_id = obj_id AND tag_order < 0;END;$$ LANGUAGE plpgsql;

34

Page 145: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Let’s Do It!CREATE OR REPLACE FUNCTION entry_coll_tag_set ( obj_id integer, coll_ids integer[]) RETURNS VOID AS $$BEGIN PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;

UPDATE entry_coll_tag SET tag_order = -tag_order WHERE entry_id = obj_id

FOR iloop IN 1..array_upper(coll_ids, 1) LOOP IF coll_ids[iloop] IS NULL THEN CONTINUE; END IF;

UPDATE entry_coll_tag SET tag_order = iloop WHERE entry_id = obj_id AND tag_id = coll_ids[iloop];

IF FOUND IS false THEN INSERT INTO entry_coll_tag (entry_id, tag_id, tag_order) VALUES (obj_id, coll_ids[iloop], iloop); END IF; END LOOP;

DELETE FROM entry_coll_tag WHERE entry_id = obj_id AND tag_order < 0;END;$$ LANGUAGE plpgsql;

34

Page 146: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Let’s Do It!CREATE OR REPLACE FUNCTION entry_coll_tag_set ( obj_id integer, coll_ids integer[]) RETURNS VOID AS $$BEGIN PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;

UPDATE entry_coll_tag SET tag_order = -tag_order WHERE entry_id = obj_id

FOR iloop IN 1..array_upper(coll_ids, 1) LOOP IF coll_ids[iloop] IS NULL THEN CONTINUE; END IF;

UPDATE entry_coll_tag SET tag_order = iloop WHERE entry_id = obj_id AND tag_id = coll_ids[iloop];

IF FOUND IS false THEN INSERT INTO entry_coll_tag (entry_id, tag_id, tag_order) VALUES (obj_id, coll_ids[iloop], iloop); END IF; END LOOP;

DELETE FROM entry_coll_tag WHERE entry_id = obj_id AND tag_order < 0;END;$$ LANGUAGE plpgsql;

34

Page 147: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Let’s Do It!CREATE OR REPLACE FUNCTION entry_coll_tag_set ( obj_id integer, coll_ids integer[]) RETURNS VOID AS $$BEGIN PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;

UPDATE entry_coll_tag SET tag_order = -tag_order WHERE entry_id = obj_id

FOR iloop IN 1..array_upper(coll_ids, 1) LOOP IF coll_ids[iloop] IS NULL THEN CONTINUE; END IF;

UPDATE entry_coll_tag SET tag_order = iloop WHERE entry_id = obj_id AND tag_id = coll_ids[iloop];

IF FOUND IS false THEN INSERT INTO entry_coll_tag (entry_id, tag_id, tag_order) VALUES (obj_id, coll_ids[iloop], iloop); END IF; END LOOP;

DELETE FROM entry_coll_tag WHERE entry_id = obj_id AND tag_order < 0;END;$$ LANGUAGE plpgsql;

34

Page 148: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Let’s Do It!CREATE OR REPLACE FUNCTION entry_coll_tag_set ( obj_id integer, coll_ids integer[]) RETURNS VOID AS $$BEGIN PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;

UPDATE entry_coll_tag SET tag_order = -tag_order WHERE entry_id = obj_id

FOR iloop IN 1..array_upper(coll_ids, 1) LOOP IF coll_ids[iloop] IS NULL THEN CONTINUE; END IF;

UPDATE entry_coll_tag SET tag_order = iloop WHERE entry_id = obj_id AND tag_id = coll_ids[iloop];

IF FOUND IS false THEN INSERT INTO entry_coll_tag (entry_id, tag_id, tag_order) VALUES (obj_id, coll_ids[iloop], iloop); END IF; END LOOP;

DELETE FROM entry_coll_tag WHERE entry_id = obj_id AND tag_order < 0;END;$$ LANGUAGE plpgsql;

34

Page 149: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Let’s Do It!CREATE OR REPLACE FUNCTION entry_coll_tag_set ( obj_id integer, coll_ids integer[]) RETURNS VOID AS $$BEGIN PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;

UPDATE entry_coll_tag SET tag_order = -tag_order WHERE entry_id = obj_id

FOR iloop IN 1..array_upper(coll_ids, 1) LOOP IF coll_ids[iloop] IS NULL THEN CONTINUE; END IF;

UPDATE entry_coll_tag SET tag_order = iloop WHERE entry_id = obj_id AND tag_id = coll_ids[iloop];

IF FOUND IS false THEN INSERT INTO entry_coll_tag (entry_id, tag_id, tag_order) VALUES (obj_id, coll_ids[iloop], iloop); END IF; END LOOP;

DELETE FROM entry_coll_tag WHERE entry_id = obj_id AND tag_order < 0;END;$$ LANGUAGE plpgsql;

34

Page 150: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Let’s Do It!CREATE OR REPLACE FUNCTION entry_coll_tag_set ( obj_id integer, coll_ids integer[]) RETURNS VOID AS $$BEGIN PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;

UPDATE entry_coll_tag SET tag_order = -tag_order WHERE entry_id = obj_id

FOR iloop IN 1..array_upper(coll_ids, 1) LOOP IF coll_ids[iloop] IS NULL THEN CONTINUE; END IF;

UPDATE entry_coll_tag SET tag_order = iloop WHERE entry_id = obj_id AND tag_id = coll_ids[iloop];

IF FOUND IS false THEN INSERT INTO entry_coll_tag (entry_id, tag_id, tag_order) VALUES (obj_id, coll_ids[iloop], iloop); END IF; END LOOP;

DELETE FROM entry_coll_tag WHERE entry_id = obj_id AND tag_order < 0;END;$$ LANGUAGE plpgsql;

34

Page 151: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Let’s Do It!CREATE OR REPLACE FUNCTION entry_coll_tag_set ( obj_id integer, coll_ids integer[]) RETURNS VOID AS $$BEGIN PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;

UPDATE entry_coll_tag SET tag_order = -tag_order WHERE entry_id = obj_id

FOR iloop IN 1..array_upper(coll_ids, 1) LOOP IF coll_ids[iloop] IS NULL THEN CONTINUE; END IF;

UPDATE entry_coll_tag SET tag_order = iloop WHERE entry_id = obj_id AND tag_id = coll_ids[iloop];

IF FOUND IS false THEN INSERT INTO entry_coll_tag (entry_id, tag_id, tag_order) VALUES (obj_id, coll_ids[iloop], iloop); END IF; END LOOP;

DELETE FROM entry_coll_tag WHERE entry_id = obj_id AND tag_order < 0;END;$$ LANGUAGE plpgsql;

34

Page 152: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Let’s Do It!CREATE OR REPLACE FUNCTION entry_coll_tag_set ( obj_id integer, coll_ids integer[]) RETURNS VOID AS $$BEGIN PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;

UPDATE entry_coll_tag SET tag_order = -tag_order WHERE entry_id = obj_id

FOR iloop IN 1..array_upper(coll_ids, 1) LOOP IF coll_ids[iloop] IS NULL THEN CONTINUE; END IF;

UPDATE entry_coll_tag SET tag_order = iloop WHERE entry_id = obj_id AND tag_id = coll_ids[iloop];

IF FOUND IS false THEN INSERT INTO entry_coll_tag (entry_id, tag_id, tag_order) VALUES (obj_id, coll_ids[iloop], iloop); END IF; END LOOP;

DELETE FROM entry_coll_tag WHERE entry_id = obj_id AND tag_order < 0;END;$$ LANGUAGE plpgsql;

34

Page 153: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Try It

35

Page 154: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Try ItSELECT entry_coll_tag_set(1, '{1,4,6,3}');

35

Page 155: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Learn More

My O’Reilly Articleshttp://www.oreillynet.com/pub/au/1059

The PostgreSQL Documentationhttp://www.postgresql.org/docs/current/static/plpgsql.html

PostgreSQL: Introduction and Conceptshttp://www.postgresql.org/files/documentation/books/aw_pgsql/node165.html

36

Page 156: Learning PL/pgSQL · Learning PL/pgSQL David Wheeler Kineticode Portland PostgreSQL Users Group 2006-07-19 1

Learning PL/pgSQLDavid Wheeler

Kineticode

Portland PostgreSQL Users Group2006-07-19

Thank You!

37