SlideShare a Scribd company logo
Building and Distributing
     PostgreSQL Extensions
      Without Learning C
                                               David E. Wheeler
                                            PostgreSQL Experts, Inc.

                                                 PGXPUG PgDay 2011




Text: Attribution-Noncommercial-Share Alike 3.0 United States:
http://creativecommons.org/licenses/by-nc-sa/3.0/us/
Images licensed independently and © Their respective owners.
Building and Distributing PostgreSQL Extensions Without Learning C
There are
quite a few
PostgreSQL
Extensions.
Core Extensions
Core Extensions
intagg             pgcrypto

intarray           btree_gin

hstore             chkpass

citext             dblink

cube               earthdistance

isn                fuzzystrmatch
Common Extensions
Common Extensions
PostGIS      PL/R

temporal     OracleFCE

pgmemcache   mysqlcompat

PL/Proxy     pg_randomness

PL/Java      pgTAP

PL/Ruby
Building and Distributing PostgreSQL Extensions Without Learning C
You can create
extensions, too!
Building and Distributing PostgreSQL Extensions Without Learning C
I know what
you’re
thinking…
Building and Distributing PostgreSQL Extensions Without Learning C
“Sure, but I
 get C sick.”
Building and Distributing PostgreSQL Extensions Without Learning C
Fortunately…
Building and Distributing PostgreSQL Extensions Without Learning C
There are
many ways
to extend
PostgreSQL.
Functions




truth.sql
Functions
CREATE
FUNCTION
truthy(




text
)
RETURNS
BOOLEAN
IMMUTABLE
LANGUAGE
SQL
AS

$$




SELECT
$1
IS
NOT
NULL







AND
$1
NOT
IN
('',
'0')







AND
NOT
$1
~
'^[0.]+$';
$$;




 truth.sql
Functions
CREATE
FUNCTION
truthy(




text
)
RETURNS
BOOLEAN
IMMUTABLE
LANGUAGE
SQL
AS

$$




SELECT
$1
IS
NOT
NULL







AND
$1
NOT
IN
('',
'0')







AND
NOT
$1
~
'^[0.]+$';
$$;




 truth.sql
Functions
CREATE
FUNCTION
truthy(




text
)
RETURNS
BOOLEAN
IMMUTABLE
LANGUAGE
SQL
AS

$$




SELECT
$1
IS
NOT
NULL







AND
$1
NOT
IN
('',
'0')







AND
NOT
$1
~
'^[0.]+$';
$$;




 truth.sql
Functions
CREATE
FUNCTION
truthy(




text
)
RETURNS
BOOLEAN
IMMUTABLE
LANGUAGE
SQL
AS

$$




SELECT
$1
IS
NOT
NULL







AND
$1
NOT
IN
('',
'0')







AND
NOT
$1
~
'^[0.]+$';
$$;




 truth.sql
Functions
CREATE
FUNCTION
truthy(




text
)
RETURNS
BOOLEAN
IMMUTABLE
LANGUAGE
SQL
AS

$$




SELECT
$1
IS
NOT
NULL







AND
$1
NOT
IN
('',
'0')







AND
NOT
$1
~
'^[0.]+$';
$$;




 truth.sql
Functions
CREATE
FUNCTION
truthy(




text
)
RETURNS
BOOLEAN
IMMUTABLE
LANGUAGE
SQL
AS

$$




SELECT
$1
IS
NOT
NULL







AND
$1
NOT
IN
('',
'0')







AND
NOT
$1
~
'^[0.]+$';
$$;




 truth.sql
             Might be handy!
Domains




timezone.sql
Domains
CREATE
OR
REPLACE
FUNCTION
is_timezone(




tz
CITEXT
)
RETURNS
BOOLEAN
LANGUAGE
plpgsql
STABLE
AS
$$
BEGIN




PERFORM
NOW()
AT
TIME
ZONE
tz;




RETURN
TRUE;
EXCEPTION
WHEN
invalid_parameter_value
THEN




RETURN
FALSE;
END;
$$;

CREATE
DOMAIN
timezone
AS
CITEXT

CHECK
(
is_timezone(
VALUE
)
);
 timezone.sql
Domains
CREATE
OR
REPLACE
FUNCTION
is_timezone(




tz
CITEXT
)
RETURNS
BOOLEAN
LANGUAGE
plpgsql
STABLE
AS
$$
BEGIN




PERFORM
NOW()
AT
TIME
ZONE
tz;




RETURN
TRUE;
EXCEPTION
WHEN
invalid_parameter_value
THEN




RETURN
FALSE;
END;
$$;

CREATE
DOMAIN
timezone
AS
CITEXT

CHECK
(
is_timezone(
VALUE
)
);
 timezone.sql
Domains
CREATE
OR
REPLACE
FUNCTION
is_timezone(




tz
CITEXT
)
RETURNS
BOOLEAN
LANGUAGE
plpgsql
STABLE
AS
$$
BEGIN




PERFORM
NOW()
AT
TIME
ZONE
tz;




RETURN
TRUE;
EXCEPTION
WHEN
invalid_parameter_value
THEN




RETURN
FALSE;
END;
$$;

CREATE
DOMAIN
timezone
AS
CITEXT

CHECK
(
is_timezone(
VALUE
)
);
 timezone.sql
Domains
CREATE
OR
REPLACE
FUNCTION
is_timezone(




tz
CITEXT
)
RETURNS
BOOLEAN
LANGUAGE
plpgsql
STABLE
AS
$$
BEGIN




PERFORM
NOW()
AT
TIME
ZONE
tz;




RETURN
TRUE;
EXCEPTION
WHEN
invalid_parameter_value
THEN




RETURN
FALSE;
END;
$$;

CREATE
DOMAIN
timezone
AS
CITEXT

CHECK
(
is_timezone(
VALUE
)
);
 timezone.sql
Domains
CREATE
OR
REPLACE
FUNCTION
is_timezone(




tz
CITEXT
)
RETURNS
BOOLEAN
LANGUAGE
plpgsql
STABLE
AS
$$
BEGIN




PERFORM
NOW()
AT
TIME
ZONE
tz;




RETURN
TRUE;
EXCEPTION
WHEN
invalid_parameter_value
THEN




RETURN
FALSE;
END;
$$;

CREATE
DOMAIN
timezone
AS
CITEXT

CHECK
(
is_timezone(
VALUE
)
);
 timezone.sql
Domains
CREATE
OR
REPLACE
FUNCTION
is_timezone(




tz
CITEXT
)
RETURNS
BOOLEAN
LANGUAGE
plpgsql
STABLE
AS
$$
BEGIN




PERFORM
NOW()
AT
TIME
ZONE
tz;




RETURN
TRUE;
EXCEPTION
WHEN
invalid_parameter_value
THEN




RETURN
FALSE;
END;
$$;

CREATE
DOMAIN
timezone
AS
CITEXT

CHECK
(
is_timezone(
VALUE
)
);
 timezone.sql
Domains
CREATE
OR
REPLACE
FUNCTION
is_timezone(




tz
CITEXT
)
RETURNS
BOOLEAN
LANGUAGE
plpgsql
STABLE
AS
$$
BEGIN




PERFORM
NOW()
AT
TIME
ZONE
tz;




RETURN
TRUE;
EXCEPTION
WHEN
invalid_parameter_value
THEN




RETURN
FALSE;
END;
$$;

CREATE
DOMAIN
timezone
AS
CITEXT

CHECK
(
is_timezone(
VALUE
)
);
 timezone.sql
Domains
CREATE
OR
REPLACE
FUNCTION
is_timezone(




tz
CITEXT
)
RETURNS
BOOLEAN
LANGUAGE
plpgsql
STABLE
AS
$$
BEGIN




PERFORM
NOW()
AT
TIME
ZONE
tz;




RETURN
TRUE;
EXCEPTION
WHEN
invalid_parameter_value
THEN




RETURN
FALSE;
END;
$$;                      I use this
CREATE
DOMAIN
timezone
AS
CITEXT

CHECK
(
is_timezone(
VALUE
)
);
 timezone.sql
Types




pair.sql
Types
CREATE
TYPE
pair
AS
(
k
text,
v
text
);

CREATE
OR
REPLACE
FUNCTION
store(




params
variadic
pair[]
)
RETURNS
VOID
LANGUAGE
plpgsql
AS
$$
DECLARE




param
pair;
BEGIN




FOR
param
IN
SELECT
*
FROM
unnest(params)
LOOP








‐‐
...




END
LOOP;
END;
$$

 pair.sql
Types
CREATE
TYPE
pair
AS
(
k
text,
v
text
);

CREATE
OR
REPLACE
FUNCTION
store(




params
variadic
pair[]
)
RETURNS
VOID
LANGUAGE
plpgsql
AS
$$
DECLARE




param
pair;
BEGIN




FOR
param
IN
SELECT
*
FROM
unnest(params)
LOOP








‐‐
...




END
LOOP;
END;
$$

 pair.sql
Types
CREATE
TYPE
pair
AS
(
k
text,
v
text
);

CREATE
OR
REPLACE
FUNCTION
store(




params
variadic
pair[]
)
RETURNS
VOID
LANGUAGE
plpgsql
AS
$$
DECLARE




param
pair;
BEGIN




FOR
param
IN
SELECT
*
FROM
unnest(params)
LOOP








‐‐
...




END
LOOP;
END;
$$

 pair.sql
Types
CREATE
TYPE
pair
AS
(
k
text,
v
text
);

CREATE
OR
REPLACE
FUNCTION
store(




params
variadic
pair[]
)
RETURNS
VOID
LANGUAGE
plpgsql
AS
$$
DECLARE




param
pair;
BEGIN




FOR
param
IN
SELECT
*
FROM
unnest(params)
LOOP








‐‐
...




END
LOOP;
END;
$$

 pair.sql
Types
CREATE
TYPE
pair
AS
(
k
text,
v
text
);

CREATE
OR
REPLACE
FUNCTION
store(




params
variadic
pair[]
)
RETURNS
VOID
LANGUAGE
plpgsql
AS
$$
DECLARE




param
pair;
BEGIN




FOR
param
IN
SELECT
*
FROM
unnest(params)
LOOP








‐‐
...




END
LOOP;
END;
$$

 pair.sql
            Could be useful…
Operators




truthy.sql
Operators
CREATE
OPERATOR
@
(

 RIGHTARG

=
text,

 PROCEDURE
=
truthy
);




 truthy.sql
Operators
CREATE
OPERATOR
@
(

 RIGHTARG

=
text,

 PROCEDURE
=
truthy
);




 truthy.sql
Operators
CREATE
OPERATOR
@
(

 RIGHTARG

=
text,

 PROCEDURE
=
truthy
);




 truthy.sql
Operators
CREATE
OPERATOR
@
(

 RIGHTARG

=
text,

 PROCEDURE
=
truthy
);

SELECT
@'foo';
‐‐
true
SELECT
@'';



‐‐
false




 truthy.sql
Operators
CREATE
OPERATOR
@
(

 RIGHTARG

=
text,

 PROCEDURE
=
truthy
);

SELECT
@'foo';
‐‐
true
SELECT
@'';



‐‐
false




 truthy.sql
              I could use this!
Building and Distributing PostgreSQL Extensions Without Learning C
PostgreSQL
is not merely
a database…
Building and Distributing PostgreSQL Extensions Without Learning C
…It’s an
application
development
platform.
Building and Distributing PostgreSQL Extensions Without Learning C
Develop
reusable
extension
libraries…
Building and Distributing PostgreSQL Extensions Without Learning C
And combine
them when
building apps.
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
Let’s create an
extension and
distribute it.
The Steps
The Steps
Stub source, test, and doc files
The Steps
Stub source, test, and doc files

Create the Makefile
The Steps
Stub source, test, and doc files

Create the Makefile

Write the tests
The Steps
Stub source, test, and doc files

Create the Makefile

Write the tests

Write the code
The Steps
Stub source, test, and doc files

Create the Makefile

Write the tests

Write the code

Write the docs
The Steps
Stub source, test, and doc files

Create the Makefile

Write the tests

Write the code

Write the docs

Create the metadata file
The Steps
Stub source, test, and doc files

Create the Makefile

Write the tests

Write the code

Write the docs

Create the metadata file

Package it up
The Steps
Stub source, test, and doc files

Create the Makefile

Write the tests

Write the code

Write the docs

Create the metadata file

Package it up

Release
Stub the Source




sql/pair.sql
Stub the Source
SET
client_min_messages
=
warning;

‐‐
Create
the
type
here.




 sql/pair.sql
Stub the Source
SET
client_min_messages
=
warning;

‐‐
Create
the
type
here.




 sql/pair.sql
Stub the Source
SET
client_min_messages
=
warning;

‐‐
Create
the
type
here.




 sql/pair.sql
Stub the Source
SET
client_min_messages
=
warning;

‐‐
Create
the
type
here.




                    sql/pair.sql
 sql/pair.sql
Stub the Tests




test/sql/base.sql
Stub the Tests
BEGIN;

set
ECHO
0
set
QUIET
1
i
sql/pair.sql
set
QUIET
0

ROLLBACK;




 test/sql/base.sql
Stub the Tests
BEGIN;

set
ECHO
0
set
QUIET
1
i
sql/pair.sql
set
QUIET
0

ROLLBACK;




 test/sql/base.sql
Stub the Tests
BEGIN;

set
ECHO
0
set
QUIET
1
i
sql/pair.sql
set
QUIET
0

ROLLBACK;




     test/sql/base.sql
 test/sql/base.sql
Stub the Docs




test/sql/base.sql
Stub the Docs
pair
0.1.0
==========

Synopsis
‐‐‐‐‐‐‐‐





‐‐
Example
code
here.





Description
‐‐‐‐‐‐‐‐‐‐‐

This
library
blah
blah
blah...

Usage
‐‐‐‐‐

Here's
how
to
use
this
library.

Author
‐‐‐‐‐‐

[David
E.
Wheeler](http://justatheory.com/)

Copyright
‐‐‐‐‐‐‐‐‐

Copyright
(c)
2010
David
E.
Wheeler.


   test/sql/base.sql
Stub the Docs
pair
0.1.0




                                  doc/pair.txt
==========

Synopsis
‐‐‐‐‐‐‐‐





‐‐
Example
code
here.





Description
‐‐‐‐‐‐‐‐‐‐‐

This
library
blah
blah
blah...

Usage
‐‐‐‐‐

Here's
how
to
use
this
library.

Author
‐‐‐‐‐‐

[David
E.
Wheeler](http://justatheory.com/)

Copyright
‐‐‐‐‐‐‐‐‐

Copyright
(c)
2010
David
E.
Wheeler.


   test/sql/base.sql
Create the Makefile




Makefile
Create the Makefile
DATA



=
$(wildcard
sql/*.sql)
DOCS



=
$(wildcard
doc/*.*)
MODULES
=
$(patsubst
%.c,%,$(wildcard
src/*.c))




 Makefile
Create the Makefile
DATA



=
$(wildcard
sql/*.sql)
DOCS



=
$(wildcard
doc/*.*)
MODULES
=
$(patsubst
%.c,%,$(wildcard
src/*.c))




 Makefile
Create the Makefile
DATA



=
$(wildcard
sql/*.sql)
DOCS



=
$(wildcard
doc/*.*)
MODULES
=
$(patsubst
%.c,%,$(wildcard
src/*.c))




 Makefile
Create the Makefile
DATA



=
$(wildcard
sql/*.sql)
DOCS



=
$(wildcard
doc/*.*)
MODULES
=
$(patsubst
%.c,%,$(wildcard
src/*.c))




 Makefile
Create the Makefile
DATA



=
$(wildcard
sql/*.sql)
DOCS



=
$(wildcard
doc/*.*)
MODULES
=
$(patsubst
%.c,%,$(wildcard
src/*.c))

TESTS
=
$(wildcard
test/sql/*.sql)
REGRESS
=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test




 Makefile
Create the Makefile
DATA



=
$(wildcard
sql/*.sql)
DOCS



=
$(wildcard
doc/*.*)
MODULES
=
$(patsubst
%.c,%,$(wildcard
src/*.c))

TESTS
=
$(wildcard
test/sql/*.sql)
REGRESS
=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test




 Makefile
Create the Makefile
DATA



=
$(wildcard
sql/*.sql)
DOCS



=
$(wildcard
doc/*.*)
MODULES
=
$(patsubst
%.c,%,$(wildcard
src/*.c))

TESTS
=
$(wildcard
test/sql/*.sql)
REGRESS
=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test




 Makefile
Create the Makefile
DATA



=
$(wildcard
sql/*.sql)
DOCS



=
$(wildcard
doc/*.*)
MODULES
=
$(patsubst
%.c,%,$(wildcard
src/*.c))

TESTS
=
$(wildcard
test/sql/*.sql)
REGRESS
=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test




 Makefile
Create the Makefile

                   inputdir
DATA



=
$(wildcard
sql/*.sql)
DOCS



=
$(wildcard
doc/*.*)
MODULES
=
$(patsubst
%.c,%,$(wildcard
src/*.c))

TESTS
=
$(wildcard
test/sql/*.sql)
REGRESS
=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test




 Makefile
Create the Makefile
DATA



=
$(wildcard
sql/*.sql)
DOCS



=
$(wildcard
doc/*.*)
MODULES
=
$(patsubst
%.c,%,$(wildcard
src/*.c))

TESTS
=
$(wildcard
test/sql/*.sql)
REGRESS
=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test

PG_CONFIG
=
pg_config
PGXS
:=
$(shell
$(PG_CONFIG)
‐‐pgxs)
include
$(PGXS)




 Makefile
Create the Makefile
DATA



=
$(wildcard
sql/*.sql)
DOCS



=
$(wildcard
doc/*.*)
MODULES
=
$(patsubst
%.c,%,$(wildcard
src/*.c))

TESTS
=
$(wildcard
test/sql/*.sql)
REGRESS
=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test

PG_CONFIG
=
pg_config
PGXS
:=
$(shell
$(PG_CONFIG)
‐‐pgxs)
include
$(PGXS)




 Makefile
Create the Makefile
DATA



=
$(wildcard
sql/*.sql)
DOCS



=
$(wildcard
doc/*.*)
MODULES
=
$(patsubst
%.c,%,$(wildcard
src/*.c))

TESTS
=
$(wildcard
test/sql/*.sql)
REGRESS
=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test

PG_CONFIG
=
pg_config
PGXS
:=
$(shell
$(PG_CONFIG)
‐‐pgxs)
include
$(PGXS)




 Makefile
Create the Makefile
DATA



=
$(wildcard
sql/*.sql)
DOCS



=
$(wildcard
doc/*.*)
MODULES
=
$(patsubst
%.c,%,$(wildcard
src/*.c))

TESTS
=
$(wildcard
test/sql/*.sql)
REGRESS
=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test

PG_CONFIG
=
pg_config
PGXS
:=
$(shell
$(PG_CONFIG)
‐‐pgxs)
include
$(PGXS)




 Makefile
Give it a Try
%

Give it a Try


make
%

make:
Nothing
to
be
done
for
`all'.
%
Give it a Try


make
%

make:
Nothing
to
be
done
for
`all'.


make
installcheck
%
pg_regress
‐‐psqldir=/pgsql/bin
‐‐inputdir=test
base
===
dropping
database
"regression"

===
DROP
DATABASE
===
creating
database
"regression"

===
CREATE
DATABASE
ALTER
DATABASE
===
running
regression
test
queries
===
test
base
















...
diff:


test/expected/base.out:
No
such
file
or
directory
diff
command
failed
with
status
512:


diff

"test/expected/base.out"
"results/base.out"
>

"results/base.out.diff"
make:
***
[installcheck]
Error
2
%
Give it a Try


make
%

make:
Nothing
to
be
done
for
`all'.


make
installcheck
%
pg_regress
‐‐psqldir=/pgsql/bin
‐‐inputdir=test
base
===
dropping
database
"regression"

===
DROP
DATABASE
===
creating
database
"regression"

===
CREATE
DATABASE
ALTER
DATABASE
===
running
regression
test
queries
===
test
base
















...
diff:


test/expected/base.out:
No
such
file
or
directory
diff
command
failed
with
status
512:


diff

"test/expected/base.out"
"results/base.out"
>

"results/base.out.diff"
make:
***
[installcheck]
Error
2
%
Give it a Try


make
%

make:
Nothing
to
be
done
for
`all'.


make
installcheck
%
pg_regress
‐‐psqldir=/pgsql/bin
‐‐inputdir=test
base
===
dropping
database
"regression"

===
DROP
DATABASE
===
creating
database
"regression"

===
                                             We’ll come
CREATE
DATABASE
ALTER
DATABASE
                                            back to that.
===
running
regression
test
queries
===
test
base
















...
diff:


test/expected/base.out:
No
such
file
or
directory
diff
command
failed
with
status
512:


diff

"test/expected/base.out"
"results/base.out"
>

"results/base.out.diff"
make:
***
[installcheck]
Error
2
%
Write Some Tests
i
sql/pair.sql
set
QUIET
0

ROLLBACK;




 test/sql/base.sql
Write Some Tests
i
sql/pair.sql
set
QUIET
0







SELECT
pair('foo',
'bar')
UNION
SELECT
pair('HEY'::text,
'bar')
UNION
SELECT
pair('foo'::text,
'woah'::text)
UNION
SELECT
pair('ick',
'foo'::text)
UNION
SELECT
pair('foo'::text,
1)
UNION
SELECT
pair(12.3,
'foo'::text)
UNION
SELECT
pair(1,
12)
ORDER
BY
pair;

ROLLBACK;

 test/sql/base.sql
Write Some Tests
i
sql/pair.sql
set
QUIET
0







SELECT
pair('foo',
'bar')
UNION
SELECT
pair('HEY'::text,
'bar')
UNION
SELECT
pair('foo'::text,
'woah'::text)
UNION
SELECT
pair('ick',
'foo'::text)
UNION
SELECT
pair('foo'::text,
1)
UNION
SELECT
pair(12.3,
'foo'::text)
UNION
SELECT
pair(1,
12)
ORDER
BY
pair;

ROLLBACK;

 test/sql/base.sql
Write Some Tests
i
sql/pair.sql
set
QUIET
0







SELECT
pair('foo',
'bar')
UNION
SELECT
pair('HEY'::text,
'bar')
UNION
SELECT
pair('foo'::text,
'woah'::text)
UNION
SELECT
pair('ick',
'foo'::text)
UNION
SELECT
pair('foo'::text,
1)
UNION
SELECT
pair(12.3,
'foo'::text)
UNION
SELECT
pair(1,
12)
ORDER
BY
pair;

ROLLBACK;

 test/sql/base.sql
Write Some Tests
i
sql/pair.sql
set
QUIET
0







SELECT
pair('foo',
'bar')
UNION
SELECT
pair('HEY'::text,
'bar')
UNION
SELECT
pair('foo'::text,
'woah'::text)
UNION
SELECT
pair('ick',
'foo'::text)
UNION
SELECT
pair('foo'::text,
1)
UNION
SELECT
pair(12.3,
'foo'::text)
UNION
SELECT
pair(1,
12)
ORDER
BY
pair;

ROLLBACK;

 test/sql/base.sql
Run Them
%

Run Them
%



psql
‐d
try
‐Xf
test/sql/base.sql
BEGIN
psql:test/sql/base.sql:15:
ERROR:

function
pair(unknown,

unknown)
does
not
exist
LINE
1:
SELECT
pair('foo',
'bar')















^
ROLLBACK
%
Run Them
%



psql
‐d
try
‐Xf
test/sql/base.sql
BEGIN
psql:test/sql/base.sql:15:
ERROR:

function
pair(unknown,

unknown)
does
not
exist
LINE
1:
SELECT
pair('foo',
'bar')















^
ROLLBACK
%
                Guess we
             should write it.
SET
client_min_messages
=
warning;

‐‐
Create
the
type
here.




  sql/pair.sql
SET
client_min_messages
=
warning;

CREATE
TYPE
pair
AS
(
k
text,
v
text
);

CREATE
OR
REPLACE
FUNCTION
pair(anyelement,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(text,
anyelement)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(anyelement,
anyelement)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(text,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair;';




  sql/pair.sql
SET
client_min_messages
=
warning;

CREATE
TYPE
pair
AS
(
k
text,
v
text
);

CREATE
OR
REPLACE
FUNCTION
pair(anyelement,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(text,
anyelement)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(anyelement,
anyelement)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(text,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair;';




  sql/pair.sql
SET
client_min_messages
=
warning;

CREATE
TYPE
pair
AS
(
k
text,
v
text
);

CREATE
OR
REPLACE
FUNCTION
pair(anyelement,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(text,
anyelement)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(anyelement,
anyelement)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(text,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair;';




  sql/pair.sql
SET
client_min_messages
=
warning;

CREATE
TYPE
pair
AS
(
k
text,
v
text
);

CREATE
OR
REPLACE
FUNCTION
pair(anyelement,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(text,
anyelement)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(anyelement,
anyelement)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(text,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair;';




  sql/pair.sql
SET
client_min_messages
=
warning;

CREATE
TYPE
pair
AS
(
k
text,
v
text
);

CREATE
OR
REPLACE
FUNCTION
pair(anyelement,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(text,
anyelement)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(anyelement,
anyelement)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(text,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair;';




  sql/pair.sql
SET
client_min_messages
=
warning;

CREATE
TYPE
pair
AS
(
k
text,
v
text
);

CREATE
OR
REPLACE
FUNCTION
pair(anyelement,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(text,
anyelement)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(anyelement,
anyelement)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair';

CREATE
OR
REPLACE
FUNCTION
pair(text,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair;';




  sql/pair.sql
%
%


psql
‐d
try
‐Xf
test/sql/base.sql
BEGIN




pair




‐‐‐‐‐‐‐‐‐‐‐‐

(1,12)

(12.3,foo)

(HEY,bar)

(foo,1)

(foo,bar)

(foo,woah)

(ick,foo)
(7
rows)

ROLLBACK
%




                                      W00t!
Moar Tests!
UNION
SELECT
pair(12.3,
'foo'::text)
UNION
SELECT
pair(1,
12)
ORDER
BY
pair;

ROLLBACK;




 test/sql/base.sql
Moar Tests!
UNION
SELECT
pair(12.3,
'foo'::text)
UNION
SELECT
pair(1,
12)
ORDER
BY
pair;







SELECT
'foo'
~>
'bar'
AS
arrowop
UNION
SELECT
'HEY'::text
~>
'bar'
UNION
SELECT
'foo'::text
~>
'woah'::text
UNION
SELECT
'ick'
~>
'foo'::text
UNION
SELECT
'foo'::text
~>
1
UNION
SELECT
12.3
~>
'foo'::text
UNION
SELECT
1
~>
12
ORDER
BY
arrowop;

ROLLBACK;
 test/sql/base.sql
Moar Tests!
UNION
SELECT
pair(12.3,
'foo'::text)
UNION
SELECT
pair(1,
12)
ORDER
BY
pair;







SELECT
'foo'
~>
'bar'
AS
arrowop
UNION
SELECT
'HEY'::text
~>
'bar'
UNION
SELECT
'foo'::text
~>
'woah'::text
UNION
SELECT
'ick'
~>
'foo'::text
UNION
SELECT
'foo'::text
~>
1
UNION
SELECT
12.3
~>
'foo'::text
UNION
SELECT
1
~>
12
ORDER
BY
arrowop;

ROLLBACK;
 test/sql/base.sql
Moar Tests!
UNION
SELECT
pair(12.3,
'foo'::text)
UNION
SELECT
pair(1,
12)
ORDER
BY
pair;







SELECT
'foo'
~>
'bar'
AS
arrowop
UNION
SELECT
'HEY'::text
~>
'bar'
UNION
SELECT
'foo'::text
~>
'woah'::text
UNION
SELECT
'ick'
~>
'foo'::text
UNION
SELECT
'foo'::text
~>
1
UNION
SELECT
12.3
~>
'foo'::text
UNION
SELECT
1
~>
12
ORDER
BY
arrowop;

ROLLBACK;
 test/sql/base.sql
Moar Tests!
UNION
SELECT
pair(12.3,
'foo'::text)
UNION
SELECT
pair(1,
12)
ORDER
BY
pair;







SELECT
'foo'
~>
'bar'
AS
arrowop
UNION
SELECT
'HEY'::text
~>
'bar'
UNION
SELECT
'foo'::text
~>
'woah'::text
UNION
SELECT
'ick'
~>
'foo'::text
UNION
SELECT
'foo'::text
~>
1
UNION
SELECT
12.3
~>
'foo'::text
UNION
SELECT
1
~>
12
ORDER
BY
arrowop;

ROLLBACK;
 test/sql/base.sql
%
%


psql
‐d
try
‐Xf
test/sql/base.sql
BEGIN




pair




‐‐‐‐‐‐‐‐‐‐‐‐

(1,12)

(12.3,foo)

(HEY,bar)

(foo,1)

(foo,bar)

(foo,woah)

(ick,foo)
(7
rows)

psql:test/sql/base.sql:25:
ERROR:

operator
does
not
exist:

unknown
~>
unknown
LINE
1:
SELECT
'foo'
~>
'bar'
AS
arrowop





















^
HINT:

No
operator
matches
the
given
name
and
argument

type(s).
You
might
need
to
add
explicit
type
casts.
ROLLBACK
%

(foo,1)

(foo,bar)
%

(foo,woah)

(ick,foo)
(7
rows)

psql:test/sql/base.sql:25:
ERROR:

operator

unknown
~>
unknown
LINE
1:
SELECT
'foo'
~>
'bar'
AS
arrowop





















^
                      Guess we
HINT:

No
operator
matches
the
given
name
an
                   should write it.
type(s).
You
might
need
to
add
explicit
type
ROLLBACK
%
CREATE
OR
REPLACE
FUNCTION
pair(text,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair;';




  sql/pair.sql
CREATE
OR
REPLACE
FUNCTION
pair(text,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair;';

CREATE
OPERATOR
~>
(

   LEFTARG


=
text,

   RIGHTARG

=
anyelement,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
anyelement,

   RIGHTARG

=
text,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
anyelement,

   RIGHTARG

=
anyelement,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
text,

   RIGHTARG

=
text,

   PROCEDURE
=
pair
);
  sql/pair.sql
CREATE
OR
REPLACE
FUNCTION
pair(text,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair;';

CREATE
OPERATOR
~>
(

   LEFTARG


=
text,

   RIGHTARG

=
anyelement,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
anyelement,

   RIGHTARG

=
text,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
anyelement,

   RIGHTARG

=
anyelement,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
text,

   RIGHTARG

=
text,

   PROCEDURE
=
pair
);
  sql/pair.sql
CREATE
OR
REPLACE
FUNCTION
pair(text,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair;';

CREATE
OPERATOR
~>
(

   LEFTARG


=
text,

   RIGHTARG

=
anyelement,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
anyelement,

   RIGHTARG

=
text,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
anyelement,

   RIGHTARG

=
anyelement,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
text,

   RIGHTARG

=
text,

   PROCEDURE
=
pair
);
  sql/pair.sql
CREATE
OR
REPLACE
FUNCTION
pair(text,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair;';

CREATE
OPERATOR
~>
(

   LEFTARG


=
text,

   RIGHTARG

=
anyelement,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
anyelement,

   RIGHTARG

=
text,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
anyelement,

   RIGHTARG

=
anyelement,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
text,

   RIGHTARG

=
text,

   PROCEDURE
=
pair
);
  sql/pair.sql
CREATE
OR
REPLACE
FUNCTION
pair(text,
text)
RETURNS
pair
LANGUAGE
SQL
AS
'SELECT
ROW($1,
$2)::pair;';

CREATE
OPERATOR
~>
(

   LEFTARG


=
text,

   RIGHTARG

=
anyelement,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
anyelement,

   RIGHTARG

=
text,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
anyelement,

   RIGHTARG

=
anyelement,

   PROCEDURE
=
pair
);

CREATE
OPERATOR
~>
(

   LEFTARG


=
text,

   RIGHTARG

=
text,

   PROCEDURE
=
pair
);
  sql/pair.sql
%
%


psql
‐d
try
‐Xf
test/sql/base.sql
BEGIN




pair




‐‐‐‐‐‐‐‐‐‐‐‐

(1,12)

(12.3,foo)

(HEY,bar)

(foo,1)

(foo,bar)

(foo,woah)

(ick,foo)
(7
rows)



arrowop



‐‐‐‐‐‐‐‐‐‐‐‐

(1,12)

(12.3,foo)

(HEY,bar)

(foo,1)

(foo,bar)

(foo,woah)

(ick,foo)
(7
rows)

ROLLBACK

(foo,bar)

(foo,woah)
%


(ick,foo)
(7
rows)



arrowop



‐‐‐‐‐‐‐‐‐‐‐‐

(1,12)

(12.3,foo)

(HEY,bar)

(foo,1)

(foo,bar)

(foo,woah)

(ick,foo)
(7
rows)
               Yay!
ROLLBACK
One More Time!
UNION
SELECT
12.3
~>
'foo'::text
UNION
SELECT
1
~>
12
ORDER
BY
arrowop;

ROLLBACK;




 test/sql/base.sql
One More Time!
UNION
SELECT
12.3
~>
'foo'::text
UNION
SELECT
1
~>
12
ORDER
BY
arrowop;

CREATE
TABLE
kv
(




pair
pair
);

INSERT
INTO
kv
VALUES('foo'
~>
'bar'),
(1
~>
2);

SELECT
(pair).k,
(pair).v
FROM
kv
ORDER
BY
(pair).k;

SELECT
(pair('foo',
'bar')).k,
(pair('foo',
'bar')).v;

SELECT
('foo'
~>
'bar').k,
('foo'
~>
'bar').v;
ROLLBACK;
 test/sql/base.sql
One More Time!
UNION
SELECT
12.3
~>
'foo'::text
UNION
SELECT
1
~>
12
ORDER
BY
arrowop;

CREATE
TABLE
kv
(




pair
pair
);

INSERT
INTO
kv
VALUES('foo'
~>
'bar'),
(1
~>
2);

SELECT
(pair).k,
(pair).v
FROM
kv
ORDER
BY
(pair).k;

SELECT
(pair('foo',
'bar')).k,
(pair('foo',
'bar')).v;

SELECT
('foo'
~>
'bar').k,
('foo'
~>
'bar').v;
ROLLBACK;
 test/sql/base.sql
One More Time!
UNION
SELECT
12.3
~>
'foo'::text
UNION
SELECT
1
~>
12
ORDER
BY
arrowop;

CREATE
TABLE
kv
(




pair
pair
);

INSERT
INTO
kv
VALUES('foo'
~>
'bar'),
(1
~>
2);

SELECT
(pair).k,
(pair).v
FROM
kv
ORDER
BY
(pair).k;

SELECT
(pair('foo',
'bar')).k,
(pair('foo',
'bar')).v;

SELECT
('foo'
~>
'bar').k,
('foo'
~>
'bar').v;
ROLLBACK;
 test/sql/base.sql
One More Time!
UNION
SELECT
12.3
~>
'foo'::text
UNION
SELECT
1
~>
12
ORDER
BY
arrowop;

CREATE
TABLE
kv
(




pair
pair
);

INSERT
INTO
kv
VALUES('foo'
~>
'bar'),
(1
~>
2);

SELECT
(pair).k,
(pair).v
FROM
kv
ORDER
BY
(pair).k;

SELECT
(pair('foo',
'bar')).k,
(pair('foo',
'bar')).v;

SELECT
('foo'
~>
'bar').k,
('foo'
~>
'bar').v;
ROLLBACK;
 test/sql/base.sql
One More Time!
UNION
SELECT
12.3
~>
'foo'::text
UNION
SELECT
1
~>
12
ORDER
BY
arrowop;

CREATE
TABLE
kv
(




pair
pair
);

INSERT
INTO
kv
VALUES('foo'
~>
'bar'),
(1
~>
2);

SELECT
(pair).k,
(pair).v
FROM
kv
ORDER
BY
(pair).k;

SELECT
(pair('foo',
'bar')).k,
(pair('foo',
'bar')).v;

SELECT
('foo'
~>
'bar').k,
('foo'
~>
'bar').v;
ROLLBACK;
 test/sql/base.sql
One More Time!
UNION
SELECT
12.3
~>
'foo'::text
UNION
SELECT
1
~>
12
ORDER
BY
arrowop;

CREATE
TABLE
kv
(




pair
pair
);

INSERT
INTO
kv
VALUES('foo'
~>
'bar'),
(1
~>
2);

SELECT
(pair).k,
(pair).v
FROM
kv
ORDER
BY
(pair).k;

SELECT
(pair('foo',
'bar')).k,
(pair('foo',
'bar')).v;

SELECT
('foo'
~>
'bar').k,
('foo'
~>
'bar').v;
ROLLBACK;
 test/sql/base.sql
%
%


psql
‐d
try
‐Xf
test/sql/base.sql
BEGIN




pair




‐‐‐‐‐‐‐‐‐‐‐‐

(1,12)

(12.3,foo)

(HEY,bar)

(foo,1)

(foo,bar)

(foo,woah)

(ick,foo)
(7
rows)



arrowop



‐‐‐‐‐‐‐‐‐‐‐‐

(1,12)

(12.3,foo)

(HEY,bar)

(foo,1)

(foo,bar)

(foo,woah)

(ick,foo)
(7
rows)

CREATE
TABLE
INSERT
0
2


k

|

v


‐‐‐‐‐+‐‐‐‐‐

1


|
2

foo
|
bar
(2
rows)



k

|

v


‐‐‐‐‐+‐‐‐‐‐

foo
|
bar




                              We’re golden!
(1
row)



k

|

v


‐‐‐‐‐+‐‐‐‐‐

foo
|
bar
(1
row)

ROLLBACK
%
Remember This?
%

Remember This?
%



make
installcheck
pg_regress
‐‐psqldir=/pgsql/bin
‐‐inputdir=test
base
===
dropping
database
"regression"

===
DROP
DATABASE
===
creating
database
"regression"

===
CREATE
DATABASE
ALTER
DATABASE
===
running
regression
test
queries
===
test
base
















...
diff:


test/expected/base.out:
No
such
file
or
directory
diff
command
failed
with
status
512:


diff

"test/expected/base.out"
"results/base.out"
>

"results/base.out.diff"
make:
***
[installcheck]
Error
2
%
Let’s Fix That
%

Let’s Fix That
%



echo
'set
ECHO
0'
>
test/expected/base.out

%
Let’s Fix That
%



echo
'set
ECHO
0'
>
test/expected/base.out



psql
‐d
try
‐Xf
test/sql/base.sql

%


|
grep
‐v
^BEGIN
>>
test/expected/base.out
%
Let’s Fix That
%



echo
'set
ECHO
0'
>
test/expected/base.out



psql
‐d
try
‐Xf
test/sql/base.sql

%


|
grep
‐v
^BEGIN
>>
test/expected/base.out


%
make
installcheck
pg_regress
‐‐psqldir=/pgsql/bin
‐‐inputdir=test
base
(using
postmaster
on
Unix
socket,
default
port)
==============
dropping
database
"regression"









==============
DROP
DATABASE
==============
creating
database
"regression"









==============
CREATE
DATABASE
ALTER
DATABASE
==============
running
regression
test
queries








==============
test
base
















...
ok

=====================

All
1
tests
passed.

=====================
Let’s Fix That
%



echo
'set
ECHO
0'
>
test/expected/base.out



psql
‐d
try
‐Xf
test/sql/base.sql

%


|
grep
‐v
^BEGIN
>>
test/expected/base.out


%
make
installcheck
pg_regress
‐‐psqldir=/pgsql/bin
‐‐inputdir=test
base
(using
postmaster
on
Unix
socket,
default
port)
==============
dropping
database
"regression"









==============
DROP
DATABASE
==============
creating
database
"regression"









==============
CREATE
DATABASE
ALTER
DATABASE
==============
running
regression
test
queries








==============
test
base
















...
ok

=====================

All
1
tests
passed.

=====================
                                 Woo hoo!
Where are We?
Stub source, test, and doc files

Create the Makefile

Write the tests

Write the code

Write the docs

Create the metadata file

Package it up

Release
Where are We?
✔   Stub source, test, and doc files

    Create the Makefile

    Write the tests

    Write the code

    Write the docs

    Create the metadata file

    Package it up

    Release
Where are We?
✔   Stub source, test, and doc files

✔   Create the Makefile

    Write the tests

    Write the code

    Write the docs

    Create the metadata file

    Package it up

    Release
Where are We?
✔   Stub source, test, and doc files

✔   Create the Makefile

✔   Write the tests

    Write the code

    Write the docs

    Create the metadata file

    Package it up

    Release
Where are We?
✔   Stub source, test, and doc files

✔   Create the Makefile

✔   Write the tests

✔   Write the code

    Write the docs

    Create the metadata file

    Package it up

    Release
Write the Docs
pair
0.1.0
==========

Synopsis
‐‐‐‐‐‐‐‐





‐‐
Example
code
here.

Description
‐‐‐‐‐‐‐‐‐‐‐

This
library
blah
blah
blah...

Usage
‐‐‐‐‐

Here's
how
to
use
this
library.

Author
‐‐‐‐‐‐

[David
E.
Wheeler](http://justatheory.com/)

Copyright
‐‐‐‐‐‐‐‐‐

Copyright
(c)
2010
David
E.
Wheeler.


   test/sql/base.sql
Write the Docs
pair
0.1.0
==========

Synopsis
‐‐‐‐‐‐‐‐





%
CREATE
EXTENSION
pair;




CREATE
EXTENSION





%
SELECT
pair('foo',
'bar');








pair




‐‐‐‐‐‐‐‐‐‐‐‐





(foo,bar)





%
SELECT
'foo'
~>
'bar';








pair




‐‐‐‐‐‐‐‐‐‐‐‐





(foo,bar)

Description
‐‐‐‐‐‐‐‐‐‐‐

This
library
blah
blah
blah...

Usage
‐‐‐‐‐

Here's
how
to
use
this
library.

Author
   test/sql/base.sql
‐‐‐‐‐‐
Write the Docs
Description
‐‐‐‐‐‐‐‐‐‐‐

This
library
blah
blah
blah...

Usage
‐‐‐‐‐

Here's
how
to
use
this
library.

Author
‐‐‐‐‐‐

[David
E.
Wheeler](http://justatheory.com/)

Copyright
‐‐‐‐‐‐‐‐‐

Copyright
(c)
2010
David
E.
Wheeler.




   test/sql/base.sql
Write the Docs
Description
‐‐‐‐‐‐‐‐‐‐‐

This
library
contains
a
single
PostgreSQL
extension,
a
key/value
pair
data
type
called
`pair`,
along
with
a
convenience
function
for
constructing
key/value
pairs.
It's
just
a
simple
thing,
really:
a
two‐value
composite
type
that
can
store
any
type
of
value
in
its
slots,
which
are
named
`k`
and
`v`.

Usage
‐‐‐‐‐

Here's
how
to
use
this
library.

Author
‐‐‐‐‐‐

[David
E.
Wheeler](http://justatheory.com/)

Copyright
‐‐‐‐‐‐‐‐‐

Copyright
(c)
2010
David
E.
Wheeler.




   test/sql/base.sql
META.json




META.json
META.json
{



"name":






"pair",



"abstract":


"A
key/value
pair
data
type",



"version":



"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":



"postgresql",



"provides":
{






"pair":
{









"file":




"pair.sql",









"version":

"0.1.0"






}



},



"meta‐spec":

{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



}
}


  META.json
META.json
{



"name":






"pair",



"abstract":


"A
key/value
pair
data
type",



"version":



"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":



"postgresql",



"provides":
{






"pair":
{









"file":




"pair.sql",









"version":

"0.1.0"






}



},



"meta‐spec":

{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



}
}


  META.json
META.json
{



"name":






"pair",



"abstract":


"A
key/value
pair
data
type",



"version":



"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":



"postgresql",



"provides":
{






"pair":
{









"file":




"pair.sql",









"version":

"0.1.0"






}



},



"meta‐spec":

{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



}
}


  META.json
META.json
{



"name":






"pair",



"abstract":


"A
key/value
pair
data
type",



"version":



"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":



"postgresql",



"provides":
{






"pair":
{









"file":




"pair.sql",









"version":

"0.1.0"






}



},



"meta‐spec":

{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



}
}


  META.json
META.json
{                   semver.org



"name":






"pair",



"abstract":


"A
key/value
pair
data
type",



"version":



"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":



"postgresql",



"provides":
{






"pair":
{









"file":




"pair.sql",









"version":

"0.1.0"






}



},



"meta‐spec":

{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



}
}


  META.json
META.json
{



"name":






"pair",



"abstract":


"A
key/value
pair
data
type",



"version":



"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":



"postgresql",



"provides":
{






"pair":
{









"file":




"pair.sql",









"version":

"0.1.0"






}



},



"meta‐spec":

{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



}
}


  META.json
META.json
{



"name":






"pair",



"abstract":


"A
key/value
pair
data
type",



"version":



"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":



"postgresql",



"provides":
{






"pair":
{









"file":




"pair.sql",









"version":

"0.1.0"






}



},



"meta‐spec":

{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



}
}


  META.json
META.json
{



"name":






"pair",



"abstract":


"A
key/value
pair
data
type",



"version":



"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":



"postgresql",



"provides":
{






"pair":
{









"file":




"pair.sql",









"version":

"0.1.0"






}



},



"meta‐spec":

{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



}
}


  META.json
META.json
{



"name":






"pair",



"abstract":


"A
key/value
pair
data
type",



"version":



"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":



"postgresql",



"provides":
{






"pair":
{









"file":




"pair.sql",









"version":

"0.1.0"






}



},



"meta‐spec":

{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



}
}


  META.json
META.json
{



"name":






"pair",



"abstract":


"A
key/value
pair
data
type",



"version":



"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":



"postgresql",



"provides":
{






"pair":
{









"file":




"pair.sql",









"version":

"0.1.0"






}



},



"meta‐spec":

{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



               At least this…



}
}


  META.json
Package it Up!
%

Package it Up!
%



git
archive
‐‐format
zip
‐‐prefix=pair‐0.1.0/



‐‐output
~/Desktop/pair‐0.1.0.zip
master
%
Package it Up!
%



git
archive
‐‐format
zip
‐‐prefix=pair‐0.1.0/



‐‐output
~/Desktop/pair‐0.1.0.zip
master
%




                         Easy, eh?
Where are We?
✔   Stub source, test, and doc files

✔   Create the Makefile

✔   Write the tests

✔   Write the code

    Write the docs

    Create the metadata file

    Package it up

    Release
Where are We?
✔   Stub source, test, and doc files

✔   Create the Makefile

✔   Write the tests

✔   Write the code

✔   Write the docs

    Create the metadata file

    Package it up

    Release
Where are We?
✔   Stub source, test, and doc files

✔   Create the Makefile

✔   Write the tests

✔   Write the code

✔   Write the docs

✔   Create the metadata file

    Package it up

    Release
Where are We?
✔   Stub source, test, and doc files

✔   Create the Makefile

✔   Write the tests

✔   Write the code

✔   Write the docs

✔   Create the metadata file

✔   Package it up

    Release
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
Tom Lane

tgl@postgresql.org

http://postgresql.org/~tgl/

tomlane

@tomlane




I’ve got some killer extensions in development that I think
will be useful to everyone, including:

* pair: an ordered pair data type
* PL/Brainfuck: just what it sounds like
Tom Lane

tgl@postgresql.org

http://postgresql.org/~tgl/

tomlane

@tomlane




I’ve got some killer extensions in development that I think
will be useful to everyone, including:

* pair: an ordered pair data type
* PL/Brainfuck: just what it sounds like
tomlane
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
omg WTF ROTFL lolz
omg WTF ROTFL lolz
omg WTF ROTFL lolz
omg WTF ROTFL lolz
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
tomlane

●●●●●●●●●●●●●●●●●●
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
tomlane
tomlane
META.json
{



"name":
"pair",



"abstract":
"A
key/value
pair
data
type",



"description":
"This
library
contains
a
single
PostgreSQL
extension,
a
key/value



"version":
"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":
"postgresql",



"provides":
{






"pair":
{









"file":
"sql/pair.sql",









"version":
"0.1.0"






}



},



"resources":
{






"bugtracker":
{









"web":
"http://github.com/tgl/kv‐pair/issues/"






},






"repository":
{








"url":

"git://github.com/tgl/kv‐pair.git",








"web":

"http://github.com/tgl/kv‐pair/",








"type":
"git"






}



},



"generated_by":
"Tom
Lane",



"meta‐spec":
{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



},



"tags":
[






"variadic
function",
"ordered
pair",
"pair",
"key
value",
"key
value
pair"



]
}


  META.json
{



"name":
"pair",



"abstract":
"A
key/value
pair
data
type",



"description":
"This
library
contains
a
single
PostgreSQL
extension,
a
key/value



"version":
"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":
"postgresql",



"provides":
{






"pair":
{









"file":
"sql/pair.sql",









"version":
"0.1.0"






}



},



"resources":
{






"bugtracker":
{









"web":
"http://github.com/tgl/kv‐pair/issues/"






},






"repository":
{








"url":

"git://github.com/tgl/kv‐pair.git",








"web":

"http://github.com/tgl/kv‐pair/",








"type":
"git"






}



},



"generated_by":
"Tom
Lane",



"meta‐spec":
{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



},



"tags":
[






"variadic
function",
"ordered
pair",
"pair",
"key
value",
"key
value
pair"



]
}


  META.json
{



"name":
"pair",



"abstract":
"A
key/value
pair
data
type",



"description":
"This
library
contains
a
single
PostgreSQL
extension,
a
key/value



"version":
"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":
"postgresql",



"provides":
{






"pair":
{









"file":
"sql/pair.sql",









"version":
"0.1.0"






}



},



"resources":
{






"bugtracker":
{









"web":
"http://github.com/tgl/kv‐pair/issues/"






},






"repository":
{








"url":

"git://github.com/tgl/kv‐pair.git",








"web":

"http://github.com/tgl/kv‐pair/",








"type":
"git"






}



},



"generated_by":
"Tom
Lane",



"meta‐spec":
{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



},



"tags":
[






"variadic
function",
"ordered
pair",
"pair",
"key
value",
"key
value
pair"



]
}


  META.json
{



"name":
"pair",



"abstract":
"A
key/value
pair
data
type",



"description":
"This
library
contains
a
single
PostgreSQL
extension,
a
key/value



"version":
"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":
"postgresql",



"provides":
{






"pair":
{









"file":
"sql/pair.sql",









"version":
"0.1.0"






}



},



"resources":
{






"bugtracker":
{









"web":
"http://github.com/tgl/kv‐pair/issues/"






},






"repository":
{








"url":

"git://github.com/tgl/kv‐pair.git",








"web":

"http://github.com/tgl/kv‐pair/",








"type":
"git"






}



},



"generated_by":
"Tom
Lane",



"meta‐spec":
{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



},



"tags":
[






"variadic
function",
"ordered
pair",
"pair",
"key
value",
"key
value
pair"



]
}


  META.json
{



"name":
"pair",



"abstract":
"A
key/value
pair
data
type",



"description":
"This
library
contains
a
single
PostgreSQL
extension,
a
key/value



"version":
"0.1.0",



"maintainer":
"Tom
Lane
<tgl@postgresql.org>",



"license":
"postgresql",



"provides":
{






"pair":
{









"file":
"sql/pair.sql",









"version":
"0.1.0"






}



},



"resources":
{






"bugtracker":
{









"web":
"http://github.com/tgl/kv‐pair/issues/"






},






"repository":
{








"url":

"git://github.com/tgl/kv‐pair.git",








"web":

"http://github.com/tgl/kv‐pair/",








"type":
"git"






}



},



"generated_by":
"Tom
Lane",



"meta‐spec":
{






"version":
"1.0.0",






"url":
"http://pgxn.org/meta/spec.txt"



},



"tags":
[






"variadic
function",
"ordered
pair",
"pair",
"key
value",
"key
value
pair"



]
}


  META.json
Where are We?
✔   Stub source, test, and doc files

✔   Create the Makefile

✔   Write the tests

✔   Write the code

✔   Write the docs

✔   Create the metadata file

✔   Package it up

    Release
Where are We?
✔   Stub source, test, and doc files

✔   Create the Makefile

✔   Write the tests

✔   Write the code

✔   Write the docs

✔   Create the metadata file

✔   Package it up

✔   Release
Why PGXN?
Why PGXN?
World-wide network of mirrors
Why PGXN?
World-wide network of mirrors

Fully indexed extensions
Why PGXN?
World-wide network of mirrors

Fully indexed extensions

RESTful metadata distribution
Why PGXN?
World-wide network of mirrors

Fully indexed extensions

RESTful metadata distribution

Search and documentation site
Why PGXN?
World-wide network of mirrors

Fully indexed extensions

RESTful metadata distribution

Search and documentation site

Comprehensive REST API
Why PGXN?
World-wide network of mirrors

Fully indexed extensions

RESTful metadata distribution

Search and documentation site

Comprehensive REST API

Command-line client
Building and Distributing PostgreSQL Extensions Without Learning C
pair
pair
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
PGXN Client
%
PGXN Client
%


sudo
easy_install
pgxnclient
Installing
pgxncli.py
script
to
/usr/local/bin
Installing
pgxn
script
to
/usr/local/bin
Processing
dependencies
for
pgxnclient
Finished
processing
dependencies
for
pgxnclient
%
PGXN Client
%


sudo
easy_install
pgxnclient
Installing
pgxncli.py
script
to
/usr/local/bin
Installing
pgxn
script
to
/usr/local/bin
Processing
dependencies
for
pgxnclient
Finished
processing
dependencies
for
pgxnclient


pgxn
install
pair
%
INFO:
best
version:
pair
0.1.3
INFO:
saving
/tmp/pair‐0.1.3.zip
INFO:
unpacking:
/tmp/pair‐0.1.3.zip
INFO:
building
extension
INFO:
installing
extension
%
PGXN Client
%


sudo
easy_install
pgxnclient
Installing
pgxncli.py
script
to
/usr/local/bin
Installing
pgxn
script
to
/usr/local/bin
Processing
dependencies
for
pgxnclient
Finished
processing
dependencies
for
pgxnclient


pgxn
install
pair
%
INFO:
best
version:
pair
0.1.3
INFO:
saving
/tmp/pair‐0.1.3.zip
INFO:
unpacking:
/tmp/pair‐0.1.3.zip
INFO:
building
extension
INFO:
installing
extension
%
PGXN Client
%


sudo
easy_install
pgxnclient
Installing
pgxncli.py
script
to
/usr/local/bin
Installing
pgxn
script
to
/usr/local/bin
Processing
dependencies
for
pgxnclient
Finished
processing
dependencies
for
pgxnclient


pgxn
install
pair
%
INFO:
best
version:
pair
0.1.3
INFO:
saving
/tmp/pair‐0.1.3.zip
INFO:
unpacking:
/tmp/pair‐0.1.3.zip
INFO:
building
extension
INFO:
installing
extension


%
pgxn
load
pair
‐d
try


INFO:
best
version:
pair
0.1.3
CREATE
TYPE
CREATE
FUNCTION
CREATE
OPERATOR
%
Thank you
Daniele Varrazzo
Coming in 9.1
Coming in 9.1
9.1 adding extension support
Coming in 9.1
9.1 adding extension support
CREATE
EXTENSION
pair
WITH
SCHEMA
utils;
Coming in 9.1
9.1 adding extension support
CREATE
EXTENSION
pair
WITH
SCHEMA
utils;

Encapsulates dump/restore
Coming in 9.1
9.1 adding extension support
CREATE
EXTENSION
pair
WITH
SCHEMA
utils;

Encapsulates dump/restore

Whatever is defined in the SQL script
Coming in 9.1
9.1 adding extension support
CREATE
EXTENSION
pair
WITH
SCHEMA
utils;

Encapsulates dump/restore

Whatever is defined in the SQL script

Supporting it is easy
9.1 Extension Packaging
9.1 Extension Packaging
 Control file
9.1 Extension Packaging
 Control file

 Migration from unpackaged
9.1 Extension Packaging
 Control file

 Migration from unpackaged

   pair--unpackaged--0.1.0.sql
9.1 Extension Packaging
 Control file

 Migration from unpackaged

   pair--unpackaged--0.1.0.sql

 Properly-named SQL script
9.1 Extension Packaging
 Control file

 Migration from unpackaged

   pair--unpackaged--0.1.0.sql

 Properly-named SQL script

   pair--0.1.0.sql
Create the control file




pair.control
Create the control file

#
pair
extension
comment
=
'A
key/value
pair
data
type'
default_version
=
'0.1.0'
module_pathname
=
'$libdir/pair'
relocatable
=
true
superuser
=
false




 pair.control
Create the control file

#
pair
extension
comment
=
'A
key/value
pair
data
type'
default_version
=
'0.1.0'
module_pathname
=
'$libdir/pair'
relocatable
=
true
superuser
=
false




 pair.control
Create the control file
                         Optional
#
pair
extension
comment
=
'A
key/value
pair
data
type'
default_version
=
'0.1.0'
module_pathname
=
'$libdir/pair'
relocatable
=
true
superuser
=
false




 pair.control
Create the control file

#
pair
extension
comment
=
'A
key/value
pair
data
type'
default_version
=
'0.1.0'
module_pathname
=
'$libdir/pair'
relocatable
=
true
superuser
=
false




 pair.control
Create the control file

#
pair
extension
comment
=
'A
key/value
pair
data
type'
default_version
=
'0.1.0'
module_pathname
=
'$libdir/pair'
relocatable
=
true
superuser
=
false




 pair.control
Create the control file

#
pair
extension
                                 For C
comment
=
'A
key/value
pair
data
type'
default_version
=
'0.1.0'        code
module_pathname
=
'$libdir/pair'
relocatable
=
true
superuser
=
false




 pair.control
Create the control file

#
pair
extension
comment
=
'A
key/value
pair
data
type'
default_version
=
'0.1.0'
module_pathname
=
'$libdir/pair'
relocatable
=
true
superuser
=
false




 pair.control
Create the control file

#
pair
extension Can move
comment
=
'A
key/value
pair
data
type'
                 schemas
default_version
=
'0.1.0'
module_pathname
=
'$libdir/pair'
relocatable
=
true
superuser
=
false




 pair.control
Create the control file

#
pair
extension
comment
=
'A
key/value
pair
data
type'
default_version
=
'0.1.0'
module_pathname
=
'$libdir/pair'
relocatable
=
true
superuser
=
false




 pair.control
Create the control file

#
pair
extension
comment
=
'A
key/value
pair
data
type'
default_version
=
'0.1.0'
module_pathname
=
'$libdir/pair'
relocatable
=
true Not required
superuser
=
false
                     to install



 pair.control
Create the control file

#
pair
extension
comment
=
'A
key/value
pair
data
type'
default_version
=
'0.1.0'
module_pathname
=
'$libdir/pair'
relocatable
=
true
superuser
=
false



                pair.control
 pair.control
Migration from
                    Unpackaged




sql/pair‐‐unpac…
Migration from
                      Unpackaged
ALTER
EXTENSION
pair
ADD
TYPE
pair;
ALTER
EXTENSION
pair
ADD
FUNCTION
pair(anyelement,
text);
ALTER
EXTENSION
pair
ADD
FUNCTION
pair(text,
anyelement);
ALTER
EXTENSION
pair
ADD
FUNCTION
pair(anyelement,
anyelement);
ALTER
EXTENSION
pair
ADD
FUNCTION
pair(text,
text);
ALTER
EXTENSION
pair
ADD
OPERATOR
~>(text,
anyelement);
ALTER
EXTENSION
pair
ADD
OPERATOR
~>(anyelement,
text);
ALTER
EXTENSION
pair
ADD
OPERATOR
~>(anyelement,
anyelement);
ALTER
EXTENSION
pair
ADD
OPERATOR
~>(text,
text);




  sql/pair‐‐unpac…
Migration from
                      Unpackaged
ALTER
EXTENSION
pair
ADD
TYPE
pair;
ALTER
EXTENSION
pair
ADD
FUNCTION
pair(anyelement,
text);
ALTER
EXTENSION
pair
ADD
FUNCTION
pair(text,
anyelement);
ALTER
EXTENSION
pair
ADD
FUNCTION
pair(anyelement,
anyelement);
ALTER
EXTENSION
pair
ADD
FUNCTION
pair(text,
text);
ALTER
EXTENSION
pair
ADD
OPERATOR
~>(text,
anyelement);
ALTER
EXTENSION
pair
ADD
OPERATOR
~>(anyelement,
text);
ALTER
EXTENSION
pair
ADD
OPERATOR
~>(anyelement,
anyelement);
ALTER
EXTENSION
pair
ADD
OPERATOR
~>(text,
text);




          sql/pair--unpackaged--0.1.0.sql
  sql/pair‐‐unpac…
Update the Makefile




Makefile
Update the Makefile
EXTENSION



=
pair
EXTVERSION


=
$(shell
grep
default_version



$(EXTENSION).control
|

sed
‐e



"s/default_version[
]*=[
]*'([^']*)'/1/")
DATA








=
$(filter‐out
$(wildcard
sql/*‐‐*.sql),$
(wildcard
sql/*.sql))
DOCS








=
$(wildcard
doc/*.*)
TESTS







=
$(wildcard
test/sql/*.sql)
REGRESS





=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test
MODULES





=
$(patsubst
%.c,%,$(wildcard
src/*.c))

PG_CONFIG



=
pg_config
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

 Makefile
Update the Makefile
EXTENSION



=
pair
EXTVERSION


=
$(shell
grep
default_version



$(EXTENSION).control
|

sed
‐e



"s/default_version[
]*=[
]*'([^']*)'/1/")
DATA








=
$(filter‐out
$(wildcard
sql/*‐‐*.sql),$
(wildcard
sql/*.sql))
DOCS








=
$(wildcard
doc/*.*)
TESTS







=
$(wildcard
test/sql/*.sql)
REGRESS





=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test
MODULES





=
$(patsubst
%.c,%,$(wildcard
src/*.c))

PG_CONFIG



=
pg_config
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

 Makefile
Update the Makefile
EXTENSION



=
pair
EXTVERSION


=
$(shell
grep
default_version



$(EXTENSION).control
|

sed
‐e



"s/default_version[
]*=[
]*'([^']*)'/1/")
DATA








=
$(filter‐out
$(wildcard
sql/*‐‐*.sql),$
(wildcard
sql/*.sql))
DOCS








=
$(wildcard
doc/*.*)
TESTS







=
$(wildcard
test/sql/*.sql)
REGRESS





=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test
MODULES





=
$(patsubst
%.c,%,$(wildcard
src/*.c))

PG_CONFIG



=
pg_config
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

 Makefile
Update the Makefile
                                               Extract from
EXTENSION



=
pair
EXTVERSION


=
$(shell
grep
default_version
   control file


$(EXTENSION).control
|

sed
‐e



"s/default_version[
]*=[
]*'([^']*)'/1/")
DATA








=
$(filter‐out
$(wildcard
sql/*‐‐*.sql),$
(wildcard
sql/*.sql))
DOCS








=
$(wildcard
doc/*.*)
TESTS







=
$(wildcard
test/sql/*.sql)
REGRESS





=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test
MODULES





=
$(patsubst
%.c,%,$(wildcard
src/*.c))

PG_CONFIG



=
pg_config
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

 Makefile
Update the Makefile
EXTENSION



=
pair
EXTVERSION


=
$(shell
grep
default_version



$(EXTENSION).control
|

sed
‐e



"s/default_version[
]*=[
]*'([^']*)'/1/")
DATA








=
$(filter‐out
$(wildcard
sql/*‐‐*.sql),$
(wildcard
sql/*.sql))
DOCS








=
$(wildcard
doc/*.*)
TESTS







=
$(wildcard
test/sql/*.sql)
REGRESS





=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test
MODULES





=
$(patsubst
%.c,%,$(wildcard
src/*.c))

PG_CONFIG



=
pg_config
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

 Makefile
Update the Makefile
EXTENSION



=
pair
EXTVERSION


=
$(shell
grep
default_version



$(EXTENSION).control
|

sed
‐e



"s/default_version[
]*=[
]*'([^']*)'/1/")
DATA








=
$(filter‐out
$(wildcard
sql/*‐‐*.sql),$
(wildcard
sql/*.sql))
DOCS








=
$(wildcard
doc/*.*)
TESTS







=
$(wildcard
test/sql/*.sql)
REGRESS





=
$(patsubst
test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS
=
‐‐inputdir=test
MODULES





=
$(patsubst
%.c,%,$(wildcard
src/*.c))

PG_CONFIG



=
pg_config
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

 Makefile
Update the Makefile
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)




 Makefile
Update the Makefile
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

ifeq
($(PG91),yes)
all:
sql/$(EXTENSION)‐‐$(EXTVERSION).sql

sql/$(EXTENSION)‐‐$(EXTVERSION).sql:
sql/$(EXTENSION).sql

 cp
$<
$@

DATA
=
$(wildcard
sql/*‐‐*.sql)
sql/$(EXTENSION)‐‐$
(EXTVERSION).sql
EXTRA_CLEAN
=
sql/$(EXTENSION)‐‐$(EXTVERSION).sql
endif

PGXS
:=
$(shell
$(PG_CONFIG)
‐‐pgxs)
include
$(PGXS)
 Makefile
Update the Makefile
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

ifeq
($(PG91),yes)
all:
sql/$(EXTENSION)‐‐$(EXTVERSION).sql

sql/$(EXTENSION)‐‐$(EXTVERSION).sql:
sql/$(EXTENSION).sql

 cp
$<
$@

DATA
=
$(wildcard
sql/*‐‐*.sql)
sql/$(EXTENSION)‐‐$
(EXTVERSION).sql
EXTRA_CLEAN
=
sql/$(EXTENSION)‐‐$(EXTVERSION).sql
endif

PGXS
:=
$(shell
$(PG_CONFIG)
‐‐pgxs)
include
$(PGXS)
 Makefile
Update the Makefile
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

ifeq
($(PG91),yes)
all:
sql/$(EXTENSION)‐‐$(EXTVERSION).sql

sql/$(EXTENSION)‐‐$(EXTVERSION).sql:
sql/$(EXTENSION).sql

 cp
$<
$@

DATA
=
$(wildcard
sql/*‐‐*.sql)
sql/$(EXTENSION)‐‐$
(EXTVERSION).sql
EXTRA_CLEAN
=
sql/$(EXTENSION)‐‐$(EXTVERSION).sql
endif

PGXS
:=
$(shell
$(PG_CONFIG)
‐‐pgxs)
include
$(PGXS)
 Makefile
Update the Makefile
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

ifeq
($(PG91),yes)
all:
sql/$(EXTENSION)‐‐$(EXTVERSION).sql

sql/$(EXTENSION)‐‐$(EXTVERSION).sql:
sql/$(EXTENSION).sql

 cp
$<
$@

DATA
=
$(wildcard
sql/*‐‐*.sql)
sql/$(EXTENSION)‐‐$
(EXTVERSION).sql
EXTRA_CLEAN
=
sql/$(EXTENSION)‐‐$(EXTVERSION).sql
endif

PGXS
:=
$(shell
$(PG_CONFIG)
‐‐pgxs)
include
$(PGXS)
 Makefile
Update the Makefile
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

ifeq
($(PG91),yes)
all:
sql/$(EXTENSION)‐‐$(EXTVERSION).sql

sql/$(EXTENSION)‐‐$(EXTVERSION).sql:
sql/$(EXTENSION).sql

 cp
$<
$@

DATA
=
$(wildcard
sql/*‐‐*.sql)
sql/$(EXTENSION)‐‐$
(EXTVERSION).sql
EXTRA_CLEAN
=
sql/$(EXTENSION)‐‐$(EXTVERSION).sql
endif

PGXS
:=
$(shell
$(PG_CONFIG)
‐‐pgxs)
include
$(PGXS)
 Makefile
Update the Makefile
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

ifeq
($(PG91),yes)
all:
sql/$(EXTENSION)‐‐$(EXTVERSION).sql

sql/$(EXTENSION)‐‐$(EXTVERSION).sql:
sql/$(EXTENSION).sql

 cp
$<
$@

DATA
=
$(wildcard
sql/*‐‐*.sql)
sql/$(EXTENSION)‐‐$
(EXTVERSION).sql
EXTRA_CLEAN
=
sql/$(EXTENSION)‐‐$(EXTVERSION).sql
endif

PGXS
:=
$(shell
$(PG_CONFIG)
‐‐pgxs)
include
$(PGXS)
 Makefile
Update the Makefile
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

ifeq
($(PG91),yes)
all:
sql/$(EXTENSION)‐‐$(EXTVERSION).sql

sql/$(EXTENSION)‐‐$(EXTVERSION).sql:
sql/$(EXTENSION).sql

 cp
$<
$@

DATA
=
$(wildcard
sql/*‐‐*.sql)
sql/$(EXTENSION)‐‐$
(EXTVERSION).sql
EXTRA_CLEAN
=
sql/$(EXTENSION)‐‐$(EXTVERSION).sql
endif

PGXS
:=
$(shell
$(PG_CONFIG)
‐‐pgxs)
include
$(PGXS)
 Makefile
Update the Makefile
PG91

=
$(shell
$(PG_CONFIG)
‐‐version



|
grep
‐qE
"
8.|
9.0"
&&
echo
no
||
echo
yes)

ifeq
($(PG91),yes)
all:
sql/$(EXTENSION)‐‐$(EXTVERSION).sql

sql/$(EXTENSION)‐‐$(EXTVERSION).sql:
sql/$(EXTENSION).sql

 cp
$<
$@

DATA
=
$(wildcard
sql/*‐‐*.sql)
sql/$(EXTENSION)‐‐$
(EXTVERSION).sql
EXTRA_CLEAN
=
sql/$(EXTENSION)‐‐$(EXTVERSION).sql
endif

PGXS
:=
$(shell
$(PG_CONFIG)
‐‐pgxs)
include
$(PGXS)
 Makefile
Or Forget It
Or Forget It
Copy Makefile
Or Forget It
Copy Makefile

Edit first line
Or Forget It
Copy Makefile

Edit first line

   EXTENSION=pair
Or Forget It
Copy Makefile

Edit first line

   EXTENSION=pair

Ignore the rest
Or Forget It
Copy Makefile

Edit first line

   EXTENSION=pair

Ignore the rest




             Better still…
Skeleton in the Closet
%
Skeleton in the Closet
%


sudo
gem
install
pgxn_utils
Installing
ri
documentation
for
pgxn_utils‐0.1.2...
Installing
RDoc
documentation
for
pgxn_utils‐0.1.2...
%
Skeleton in the Closet
%


sudo
gem
install
pgxn_utils
Installing
ri
documentation
for
pgxn_utils‐0.1.2...
Installing
RDoc
documentation
for
pgxn_utils‐0.1.2...


pgxn_utils
skeleton
semver
%






create

semver






create

semver/semver.control






create

semver/META.json






create

semver/Makefile






create

semver/README.md






create

semver/doc/semver.md






create

semver/sql/semver.sql






create

semver/sql/uninstall_semver.sql






create

semver/test/expected/base.out






create

semver/test/sql/base.sql
%
Thank you
Dickson S. Guedes
Install Extension
%
Install Extension


pgxn
load
pair
‐d
try
%
INFO:
best
version:
pair
0.1.2
CREATE
EXTENSION
%

    Nice.
Resources
Resources
http:/
     /pgxn.org/ — Browse
Resources
http:/
     /pgxn.org/ — Browse

http:/
     /manager.pgxn.org/ — Release
Resources
http:/
     /pgxn.org/ — Browse

http:/
     /manager.pgxn.org/ — Release

http:/
     /s.coop/pgextdocs — Learn
Resources
http:/
     /pgxn.org/ — Browse

http:/
     /manager.pgxn.org/ — Release

http:/
     /s.coop/pgextdocs — Learn

http:/
     /s.coop/4y4 — Slides
Resources
http:/
     /pgxn.org/ — Browse

http:/
     /manager.pgxn.org/ — Release

http:/
     /s.coop/pgextdocs — Learn

http:/
     /s.coop/4y4 — Slides

http:/
     /s.coop/pgxnclient — Client
Resources
http:/
     /pgxn.org/ — Browse

http:/
     /manager.pgxn.org/ — Release

http:/
     /s.coop/pgextdocs — Learn

http:/
     /s.coop/4y4 — Slides

http:/
     /s.coop/pgxnclient — Client

http:/
     /s.coop/pgxnutils — Develop
Building and Distributing
     PostgreSQL Extensions
      Without Learning C
                                               David E. Wheeler
                                            PostgreSQL Experts, Inc.

                                                 PGXPUG PgDay 2011




Text: Attribution-Noncommercial-Share Alike 3.0 United States:
http://creativecommons.org/licenses/by-nc-sa/3.0/us/
Images licensed independently and © Their respective owners.
Ad

More Related Content

What's hot (20)

Forget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowForget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers Cracow
Kacper Gunia
 
New in php 7
New in php 7New in php 7
New in php 7
Vic Metcalfe
 
Puppet Camp Paris 2015: Power of Puppet 4 (Beginner)
Puppet Camp Paris 2015: Power of Puppet 4 (Beginner) Puppet Camp Paris 2015: Power of Puppet 4 (Beginner)
Puppet Camp Paris 2015: Power of Puppet 4 (Beginner)
Puppet
 
Symfony without the framework
Symfony without the frameworkSymfony without the framework
Symfony without the framework
GOG.com dev team
 
Barely Legal Xxx Perl Presentation
Barely Legal Xxx Perl PresentationBarely Legal Xxx Perl Presentation
Barely Legal Xxx Perl Presentation
Attila Balazs
 
PHP 良好實踐 (Best Practice)
PHP 良好實踐 (Best Practice)PHP 良好實踐 (Best Practice)
PHP 良好實踐 (Best Practice)
Win Yu
 
Perl6 grammars
Perl6 grammarsPerl6 grammars
Perl6 grammars
Andrew Shitov
 
Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl Techniques
Dave Cross
 
Good Evils In Perl
Good Evils In PerlGood Evils In Perl
Good Evils In Perl
Kang-min Liu
 
Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6
Workhorse Computing
 
I, For One, Welcome Our New Perl6 Overlords
I, For One, Welcome Our New Perl6 OverlordsI, For One, Welcome Our New Perl6 Overlords
I, For One, Welcome Our New Perl6 Overlords
heumann
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
jsmith92
 
Best Practices in Plugin Development (WordCamp Seattle)
Best Practices in Plugin Development (WordCamp Seattle)Best Practices in Plugin Development (WordCamp Seattle)
Best Practices in Plugin Development (WordCamp Seattle)
andrewnacin
 
Introduction to Perl - Day 2
Introduction to Perl - Day 2Introduction to Perl - Day 2
Introduction to Perl - Day 2
Dave Cross
 
PHP traits, treat or threat?
PHP traits, treat or threat?PHP traits, treat or threat?
PHP traits, treat or threat?
Nick Belhomme
 
Symfony CMF - PHP Conference Brazil 2011
Symfony CMF - PHP Conference Brazil 2011Symfony CMF - PHP Conference Brazil 2011
Symfony CMF - PHP Conference Brazil 2011
Jacopo Romei
 
Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)
Kang-min Liu
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!
Kacper Gunia
 
Introduction to Perl - Day 1
Introduction to Perl - Day 1Introduction to Perl - Day 1
Introduction to Perl - Day 1
Dave Cross
 
Introduction to Modern Perl
Introduction to Modern PerlIntroduction to Modern Perl
Introduction to Modern Perl
Dave Cross
 
Forget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowForget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers Cracow
Kacper Gunia
 
Puppet Camp Paris 2015: Power of Puppet 4 (Beginner)
Puppet Camp Paris 2015: Power of Puppet 4 (Beginner) Puppet Camp Paris 2015: Power of Puppet 4 (Beginner)
Puppet Camp Paris 2015: Power of Puppet 4 (Beginner)
Puppet
 
Symfony without the framework
Symfony without the frameworkSymfony without the framework
Symfony without the framework
GOG.com dev team
 
Barely Legal Xxx Perl Presentation
Barely Legal Xxx Perl PresentationBarely Legal Xxx Perl Presentation
Barely Legal Xxx Perl Presentation
Attila Balazs
 
PHP 良好實踐 (Best Practice)
PHP 良好實踐 (Best Practice)PHP 良好實踐 (Best Practice)
PHP 良好實踐 (Best Practice)
Win Yu
 
Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl Techniques
Dave Cross
 
Good Evils In Perl
Good Evils In PerlGood Evils In Perl
Good Evils In Perl
Kang-min Liu
 
Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6
Workhorse Computing
 
I, For One, Welcome Our New Perl6 Overlords
I, For One, Welcome Our New Perl6 OverlordsI, For One, Welcome Our New Perl6 Overlords
I, For One, Welcome Our New Perl6 Overlords
heumann
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
jsmith92
 
Best Practices in Plugin Development (WordCamp Seattle)
Best Practices in Plugin Development (WordCamp Seattle)Best Practices in Plugin Development (WordCamp Seattle)
Best Practices in Plugin Development (WordCamp Seattle)
andrewnacin
 
Introduction to Perl - Day 2
Introduction to Perl - Day 2Introduction to Perl - Day 2
Introduction to Perl - Day 2
Dave Cross
 
PHP traits, treat or threat?
PHP traits, treat or threat?PHP traits, treat or threat?
PHP traits, treat or threat?
Nick Belhomme
 
Symfony CMF - PHP Conference Brazil 2011
Symfony CMF - PHP Conference Brazil 2011Symfony CMF - PHP Conference Brazil 2011
Symfony CMF - PHP Conference Brazil 2011
Jacopo Romei
 
Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)
Kang-min Liu
 
Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!Forget about index.php and build you applications around HTTP!
Forget about index.php and build you applications around HTTP!
Kacper Gunia
 
Introduction to Perl - Day 1
Introduction to Perl - Day 1Introduction to Perl - Day 1
Introduction to Perl - Day 1
Dave Cross
 
Introduction to Modern Perl
Introduction to Modern PerlIntroduction to Modern Perl
Introduction to Modern Perl
Dave Cross
 

Viewers also liked (6)

Test Driven Database Development
Test Driven Database DevelopmentTest Driven Database Development
Test Driven Database Development
David Wheeler
 
Simple SQL Change Management with Sqitch
Simple SQL Change Management with SqitchSimple SQL Change Management with Sqitch
Simple SQL Change Management with Sqitch
David Wheeler
 
Sane SQL Change Management with Sqitch
Sane SQL Change Management with SqitchSane SQL Change Management with Sqitch
Sane SQL Change Management with Sqitch
David Wheeler
 
PgTAP Best Practices
PgTAP Best PracticesPgTAP Best Practices
PgTAP Best Practices
David Wheeler
 
Lightning Talk - pgTAP
Lightning Talk - pgTAPLightning Talk - pgTAP
Lightning Talk - pgTAP
Galen Charlton
 
Lovingly crafting a mountain, not by hand: managing piles of metadata
Lovingly crafting a mountain, not by hand: managing piles of metadataLovingly crafting a mountain, not by hand: managing piles of metadata
Lovingly crafting a mountain, not by hand: managing piles of metadata
Galen Charlton
 
Test Driven Database Development
Test Driven Database DevelopmentTest Driven Database Development
Test Driven Database Development
David Wheeler
 
Simple SQL Change Management with Sqitch
Simple SQL Change Management with SqitchSimple SQL Change Management with Sqitch
Simple SQL Change Management with Sqitch
David Wheeler
 
Sane SQL Change Management with Sqitch
Sane SQL Change Management with SqitchSane SQL Change Management with Sqitch
Sane SQL Change Management with Sqitch
David Wheeler
 
PgTAP Best Practices
PgTAP Best PracticesPgTAP Best Practices
PgTAP Best Practices
David Wheeler
 
Lightning Talk - pgTAP
Lightning Talk - pgTAPLightning Talk - pgTAP
Lightning Talk - pgTAP
Galen Charlton
 
Lovingly crafting a mountain, not by hand: managing piles of metadata
Lovingly crafting a mountain, not by hand: managing piles of metadataLovingly crafting a mountain, not by hand: managing piles of metadata
Lovingly crafting a mountain, not by hand: managing piles of metadata
Galen Charlton
 
Ad

Similar to Building and Distributing PostgreSQL Extensions Without Learning C (20)

Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl Techniques
Dave Cross
 
P H P Part I I, By Kian
P H P  Part  I I,  By  KianP H P  Part  I I,  By  Kian
P H P Part I I, By Kian
phelios
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Tatsuhiko Miyagawa
 
Porting Applications From Oracle To PostgreSQL
Porting Applications From Oracle To PostgreSQLPorting Applications From Oracle To PostgreSQL
Porting Applications From Oracle To PostgreSQL
Peter Eisentraut
 
Architecture | Busy Java Developers Guide to NoSQL | Ted Neward
Architecture | Busy Java Developers Guide to NoSQL | Ted NewardArchitecture | Busy Java Developers Guide to NoSQL | Ted Neward
Architecture | Busy Java Developers Guide to NoSQL | Ted Neward
JAX London
 
Bioinformatica 10-11-2011-p6-bioperl
Bioinformatica 10-11-2011-p6-bioperlBioinformatica 10-11-2011-p6-bioperl
Bioinformatica 10-11-2011-p6-bioperl
Prof. Wim Van Criekinge
 
Writing maintainable Oracle PL/SQL code
Writing maintainable Oracle PL/SQL codeWriting maintainable Oracle PL/SQL code
Writing maintainable Oracle PL/SQL code
Donald Bales
 
Writing Maintainable Code
Writing Maintainable CodeWriting Maintainable Code
Writing Maintainable Code
Donald Bales
 
Sqladria 2009 SRC
Sqladria 2009 SRCSqladria 2009 SRC
Sqladria 2009 SRC
tepsum
 
Real life-coffeescript
Real life-coffeescriptReal life-coffeescript
Real life-coffeescript
David Furber
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Paulo Ragonha
 
Free The Enterprise With Ruby & Master Your Own Domain
Free The Enterprise With Ruby & Master Your Own DomainFree The Enterprise With Ruby & Master Your Own Domain
Free The Enterprise With Ruby & Master Your Own Domain
Ken Collins
 
Why you should be using the shiny new C# 6.0 features now!
Why you should be using the shiny new C# 6.0 features now!Why you should be using the shiny new C# 6.0 features now!
Why you should be using the shiny new C# 6.0 features now!
Eric Phan
 
Testing Code and Assuring Quality
Testing Code and Assuring QualityTesting Code and Assuring Quality
Testing Code and Assuring Quality
Kent Cowgill
 
Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013
Michelangelo van Dam
 
Rails 101
Rails 101Rails 101
Rails 101
The Active Network
 
Mastering Namespaces in PHP
Mastering Namespaces in PHPMastering Namespaces in PHP
Mastering Namespaces in PHP
Nick Belhomme
 
Fatc
FatcFatc
Fatc
Wade Arnold
 
Laying the proper foundation for plugin and theme development
Laying the proper foundation for plugin and theme developmentLaying the proper foundation for plugin and theme development
Laying the proper foundation for plugin and theme development
Tammy Hart
 
Into to DBI with DBD::Oracle
Into to DBI with DBD::OracleInto to DBI with DBD::Oracle
Into to DBI with DBD::Oracle
byterock
 
Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl Techniques
Dave Cross
 
P H P Part I I, By Kian
P H P  Part  I I,  By  KianP H P  Part  I I,  By  Kian
P H P Part I I, By Kian
phelios
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Tatsuhiko Miyagawa
 
Porting Applications From Oracle To PostgreSQL
Porting Applications From Oracle To PostgreSQLPorting Applications From Oracle To PostgreSQL
Porting Applications From Oracle To PostgreSQL
Peter Eisentraut
 
Architecture | Busy Java Developers Guide to NoSQL | Ted Neward
Architecture | Busy Java Developers Guide to NoSQL | Ted NewardArchitecture | Busy Java Developers Guide to NoSQL | Ted Neward
Architecture | Busy Java Developers Guide to NoSQL | Ted Neward
JAX London
 
Writing maintainable Oracle PL/SQL code
Writing maintainable Oracle PL/SQL codeWriting maintainable Oracle PL/SQL code
Writing maintainable Oracle PL/SQL code
Donald Bales
 
Writing Maintainable Code
Writing Maintainable CodeWriting Maintainable Code
Writing Maintainable Code
Donald Bales
 
Sqladria 2009 SRC
Sqladria 2009 SRCSqladria 2009 SRC
Sqladria 2009 SRC
tepsum
 
Real life-coffeescript
Real life-coffeescriptReal life-coffeescript
Real life-coffeescript
David Furber
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Paulo Ragonha
 
Free The Enterprise With Ruby & Master Your Own Domain
Free The Enterprise With Ruby & Master Your Own DomainFree The Enterprise With Ruby & Master Your Own Domain
Free The Enterprise With Ruby & Master Your Own Domain
Ken Collins
 
Why you should be using the shiny new C# 6.0 features now!
Why you should be using the shiny new C# 6.0 features now!Why you should be using the shiny new C# 6.0 features now!
Why you should be using the shiny new C# 6.0 features now!
Eric Phan
 
Testing Code and Assuring Quality
Testing Code and Assuring QualityTesting Code and Assuring Quality
Testing Code and Assuring Quality
Kent Cowgill
 
Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013
Michelangelo van Dam
 
Mastering Namespaces in PHP
Mastering Namespaces in PHPMastering Namespaces in PHP
Mastering Namespaces in PHP
Nick Belhomme
 
Laying the proper foundation for plugin and theme development
Laying the proper foundation for plugin and theme developmentLaying the proper foundation for plugin and theme development
Laying the proper foundation for plugin and theme development
Tammy Hart
 
Into to DBI with DBD::Oracle
Into to DBI with DBD::OracleInto to DBI with DBD::Oracle
Into to DBI with DBD::Oracle
byterock
 
Ad

Recently uploaded (20)

Linux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdfLinux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdf
RHCSA Guru
 
Greenhouse_Monitoring_Presentation.pptx.
Greenhouse_Monitoring_Presentation.pptx.Greenhouse_Monitoring_Presentation.pptx.
Greenhouse_Monitoring_Presentation.pptx.
hpbmnnxrvb
 
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In FranceManifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
chb3
 
AI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global TrendsAI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global Trends
InData Labs
 
Big Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur MorganBig Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur Morgan
Arthur Morgan
 
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
Alan Dix
 
Special Meetup Edition - TDX Bengaluru Meetup #52.pptx
Special Meetup Edition - TDX Bengaluru Meetup #52.pptxSpecial Meetup Edition - TDX Bengaluru Meetup #52.pptx
Special Meetup Edition - TDX Bengaluru Meetup #52.pptx
shyamraj55
 
Linux Support for SMARC: How Toradex Empowers Embedded Developers
Linux Support for SMARC: How Toradex Empowers Embedded DevelopersLinux Support for SMARC: How Toradex Empowers Embedded Developers
Linux Support for SMARC: How Toradex Empowers Embedded Developers
Toradex
 
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
BookNet Canada
 
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
BookNet Canada
 
Procurement Insights Cost To Value Guide.pptx
Procurement Insights Cost To Value Guide.pptxProcurement Insights Cost To Value Guide.pptx
Procurement Insights Cost To Value Guide.pptx
Jon Hansen
 
Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...
Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...
Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...
Aqusag Technologies
 
Increasing Retail Store Efficiency How can Planograms Save Time and Money.pptx
Increasing Retail Store Efficiency How can Planograms Save Time and Money.pptxIncreasing Retail Store Efficiency How can Planograms Save Time and Money.pptx
Increasing Retail Store Efficiency How can Planograms Save Time and Money.pptx
Anoop Ashok
 
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptxDevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
Justin Reock
 
Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025
Splunk
 
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Impelsys Inc.
 
The Evolution of Meme Coins A New Era for Digital Currency ppt.pdf
The Evolution of Meme Coins A New Era for Digital Currency ppt.pdfThe Evolution of Meme Coins A New Era for Digital Currency ppt.pdf
The Evolution of Meme Coins A New Era for Digital Currency ppt.pdf
Abi john
 
tecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdftecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdf
fjgm517
 
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdfSAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
Precisely
 
HCL Nomad Web – Best Practices and Managing Multiuser Environments
HCL Nomad Web – Best Practices and Managing Multiuser EnvironmentsHCL Nomad Web – Best Practices and Managing Multiuser Environments
HCL Nomad Web – Best Practices and Managing Multiuser Environments
panagenda
 
Linux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdfLinux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdf
RHCSA Guru
 
Greenhouse_Monitoring_Presentation.pptx.
Greenhouse_Monitoring_Presentation.pptx.Greenhouse_Monitoring_Presentation.pptx.
Greenhouse_Monitoring_Presentation.pptx.
hpbmnnxrvb
 
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In FranceManifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
chb3
 
AI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global TrendsAI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global Trends
InData Labs
 
Big Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur MorganBig Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur Morgan
Arthur Morgan
 
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
Alan Dix
 
Special Meetup Edition - TDX Bengaluru Meetup #52.pptx
Special Meetup Edition - TDX Bengaluru Meetup #52.pptxSpecial Meetup Edition - TDX Bengaluru Meetup #52.pptx
Special Meetup Edition - TDX Bengaluru Meetup #52.pptx
shyamraj55
 
Linux Support for SMARC: How Toradex Empowers Embedded Developers
Linux Support for SMARC: How Toradex Empowers Embedded DevelopersLinux Support for SMARC: How Toradex Empowers Embedded Developers
Linux Support for SMARC: How Toradex Empowers Embedded Developers
Toradex
 
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
Transcript: #StandardsGoals for 2025: Standards & certification roundup - Tec...
BookNet Canada
 
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
BookNet Canada
 
Procurement Insights Cost To Value Guide.pptx
Procurement Insights Cost To Value Guide.pptxProcurement Insights Cost To Value Guide.pptx
Procurement Insights Cost To Value Guide.pptx
Jon Hansen
 
Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...
Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...
Massive Power Outage Hits Spain, Portugal, and France: Causes, Impact, and On...
Aqusag Technologies
 
Increasing Retail Store Efficiency How can Planograms Save Time and Money.pptx
Increasing Retail Store Efficiency How can Planograms Save Time and Money.pptxIncreasing Retail Store Efficiency How can Planograms Save Time and Money.pptx
Increasing Retail Store Efficiency How can Planograms Save Time and Money.pptx
Anoop Ashok
 
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptxDevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
DevOpsDays Atlanta 2025 - Building 10x Development Organizations.pptx
Justin Reock
 
Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025
Splunk
 
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Impelsys Inc.
 
The Evolution of Meme Coins A New Era for Digital Currency ppt.pdf
The Evolution of Meme Coins A New Era for Digital Currency ppt.pdfThe Evolution of Meme Coins A New Era for Digital Currency ppt.pdf
The Evolution of Meme Coins A New Era for Digital Currency ppt.pdf
Abi john
 
tecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdftecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdf
fjgm517
 
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdfSAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
Precisely
 
HCL Nomad Web – Best Practices and Managing Multiuser Environments
HCL Nomad Web – Best Practices and Managing Multiuser EnvironmentsHCL Nomad Web – Best Practices and Managing Multiuser Environments
HCL Nomad Web – Best Practices and Managing Multiuser Environments
panagenda
 

Building and Distributing PostgreSQL Extensions Without Learning C

Editor's Notes