SlideShare uma empresa Scribd logo
1 de 286
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.
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
You can create
extensions, too!
I know what
you’re
thinking…
“Sure, but I
 get C sick.”
Fortunately…
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!
PostgreSQL
is not merely
a database…
…It’s an
application
development
platform.
Develop
reusable
extension
libraries…
And combine
them when
building apps.
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
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
omg WTF ROTFL lolz
omg WTF ROTFL lolz
omg WTF ROTFL lolz
omg WTF ROTFL lolz
tomlane

●●●●●●●●●●●●●●●●●●
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
pair
pair
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.

Mais conteúdo relacionado

Mais procurados

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 CracowKacper 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 frameworkGOG.com dev team
 
Barely Legal Xxx Perl Presentation
Barely Legal Xxx Perl PresentationBarely Legal Xxx Perl Presentation
Barely Legal Xxx Perl PresentationAttila 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 TechniquesDave Cross
 
Good Evils In Perl
Good Evils In PerlGood Evils In Perl
Good Evils In PerlKang-min Liu
 
Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6Workhorse 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 Overlordsheumann
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overviewjsmith92
 
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 2Dave 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 2011Jacopo 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 1Dave Cross
 
Introduction to Modern Perl
Introduction to Modern PerlIntroduction to Modern Perl
Introduction to Modern PerlDave Cross
 

Mais procurados (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
 
New in php 7
New in php 7New in php 7
New in php 7
 
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)
 
Symfony without the framework
Symfony without the frameworkSymfony without the framework
Symfony without the framework
 
Barely Legal Xxx Perl Presentation
Barely Legal Xxx Perl PresentationBarely Legal Xxx Perl Presentation
Barely Legal Xxx Perl Presentation
 
PHP 良好實踐 (Best Practice)
PHP 良好實踐 (Best Practice)PHP 良好實踐 (Best Practice)
PHP 良好實踐 (Best Practice)
 
Perl6 grammars
Perl6 grammarsPerl6 grammars
Perl6 grammars
 
Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl Techniques
 
Good Evils In Perl
Good Evils In PerlGood Evils In Perl
Good Evils In Perl
 
Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6
 
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
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
 
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)
 
Introduction to Perl - Day 2
Introduction to Perl - Day 2Introduction to Perl - Day 2
Introduction to Perl - Day 2
 
PHP traits, treat or threat?
PHP traits, treat or threat?PHP traits, treat or threat?
PHP traits, treat or threat?
 
Symfony CMF - PHP Conference Brazil 2011
Symfony CMF - PHP Conference Brazil 2011Symfony CMF - PHP Conference Brazil 2011
Symfony CMF - PHP Conference Brazil 2011
 
Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)
 
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!
 
Introduction to Perl - Day 1
Introduction to Perl - Day 1Introduction to Perl - Day 1
Introduction to Perl - Day 1
 
Introduction to Modern Perl
Introduction to Modern PerlIntroduction to Modern Perl
Introduction to Modern Perl
 

Destaque

Test Driven Database Development
Test Driven Database DevelopmentTest Driven Database Development
Test Driven Database DevelopmentDavid Wheeler
 
Simple SQL Change Management with Sqitch
Simple SQL Change Management with SqitchSimple SQL Change Management with Sqitch
Simple SQL Change Management with SqitchDavid Wheeler
 
Sane SQL Change Management with Sqitch
Sane SQL Change Management with SqitchSane SQL Change Management with Sqitch
Sane SQL Change Management with SqitchDavid Wheeler
 
PgTAP Best Practices
PgTAP Best PracticesPgTAP Best Practices
PgTAP Best PracticesDavid Wheeler
 
Lightning Talk - pgTAP
Lightning Talk - pgTAPLightning Talk - pgTAP
Lightning Talk - pgTAPGalen 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 metadataGalen Charlton
 

Destaque (6)

Test Driven Database Development
Test Driven Database DevelopmentTest Driven Database Development
Test Driven Database Development
 
Simple SQL Change Management with Sqitch
Simple SQL Change Management with SqitchSimple SQL Change Management with Sqitch
Simple SQL Change Management with Sqitch
 
Sane SQL Change Management with Sqitch
Sane SQL Change Management with SqitchSane SQL Change Management with Sqitch
Sane SQL Change Management with Sqitch
 
PgTAP Best Practices
PgTAP Best PracticesPgTAP Best Practices
PgTAP Best Practices
 
Lightning Talk - pgTAP
Lightning Talk - pgTAPLightning Talk - pgTAP
Lightning Talk - pgTAP
 
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
 

Semelhante a Building and Distributing PostgreSQL Extensions Without Learning C

Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl TechniquesDave 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 Kianphelios
 
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 jQueryTatsuhiko Miyagawa
 
Porting Applications From Oracle To PostgreSQL
Porting Applications From Oracle To PostgreSQLPorting Applications From Oracle To PostgreSQL
Porting Applications From Oracle To PostgreSQLPeter 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 NewardJAX London
 
Writing maintainable Oracle PL/SQL code
Writing maintainable Oracle PL/SQL codeWriting maintainable Oracle PL/SQL code
Writing maintainable Oracle PL/SQL codeDonald Bales
 
Writing Maintainable Code
Writing Maintainable CodeWriting Maintainable Code
Writing Maintainable CodeDonald Bales
 
Sqladria 2009 SRC
Sqladria 2009 SRCSqladria 2009 SRC
Sqladria 2009 SRCtepsum
 
Real life-coffeescript
Real life-coffeescriptReal life-coffeescript
Real life-coffeescriptDavid 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 JasminePaulo 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 DomainKen 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 QualityKent 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 2013Michelangelo van Dam
 
Mastering Namespaces in PHP
Mastering Namespaces in PHPMastering Namespaces in PHP
Mastering Namespaces in PHPNick 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 developmentTammy Hart
 
Into to DBI with DBD::Oracle
Into to DBI with DBD::OracleInto to DBI with DBD::Oracle
Into to DBI with DBD::Oraclebyterock
 

Semelhante a Building and Distributing PostgreSQL Extensions Without Learning C (20)

Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl Techniques
 
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
 
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
 
Porting Applications From Oracle To PostgreSQL
Porting Applications From Oracle To PostgreSQLPorting Applications From Oracle To PostgreSQL
Porting Applications From Oracle To PostgreSQL
 
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
 
Bioinformatica 10-11-2011-p6-bioperl
Bioinformatica 10-11-2011-p6-bioperlBioinformatica 10-11-2011-p6-bioperl
Bioinformatica 10-11-2011-p6-bioperl
 
Writing maintainable Oracle PL/SQL code
Writing maintainable Oracle PL/SQL codeWriting maintainable Oracle PL/SQL code
Writing maintainable Oracle PL/SQL code
 
Writing Maintainable Code
Writing Maintainable CodeWriting Maintainable Code
Writing Maintainable Code
 
Sqladria 2009 SRC
Sqladria 2009 SRCSqladria 2009 SRC
Sqladria 2009 SRC
 
Real life-coffeescript
Real life-coffeescriptReal life-coffeescript
Real life-coffeescript
 
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
 
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
 
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!
 
Testing Code and Assuring Quality
Testing Code and Assuring QualityTesting Code and Assuring Quality
Testing Code and Assuring Quality
 
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
 
Rails 101
Rails 101Rails 101
Rails 101
 
Mastering Namespaces in PHP
Mastering Namespaces in PHPMastering Namespaces in PHP
Mastering Namespaces in PHP
 
Fatc
FatcFatc
Fatc
 
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
 
Into to DBI with DBD::Oracle
Into to DBI with DBD::OracleInto to DBI with DBD::Oracle
Into to DBI with DBD::Oracle
 

Último

Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demoHarshalMandlekar2
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
What is Artificial Intelligence?????????
What is Artificial Intelligence?????????What is Artificial Intelligence?????????
What is Artificial Intelligence?????????blackmambaettijean
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxBkGupta21
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfMounikaPolabathina
 

Último (20)

Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demo
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
What is Artificial Intelligence?????????
What is Artificial Intelligence?????????What is Artificial Intelligence?????????
What is Artificial Intelligence?????????
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptx
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdf
 

Building and Distributing PostgreSQL Extensions Without Learning C

Notas do Editor

  1. TODO:\n&amp;#x2022; Add more privilege stuff?\n&amp;#x2022; Add example of renaming `flips.timestamp`?\n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. \n
  72. \n
  73. \n
  74. \n
  75. \n
  76. \n
  77. \n
  78. \n
  79. \n
  80. \n
  81. \n
  82. \n
  83. \n
  84. \n
  85. \n
  86. \n
  87. \n
  88. \n
  89. \n
  90. \n
  91. \n
  92. \n
  93. \n
  94. \n
  95. \n
  96. \n
  97. \n
  98. \n
  99. \n
  100. \n
  101. \n
  102. \n
  103. \n
  104. \n
  105. \n
  106. \n
  107. \n
  108. \n
  109. \n
  110. \n
  111. \n
  112. \n
  113. \n
  114. \n
  115. \n
  116. \n
  117. \n
  118. \n
  119. \n
  120. \n
  121. \n
  122. \n
  123. \n
  124. \n
  125. \n
  126. \n
  127. \n
  128. \n
  129. \n
  130. \n
  131. \n
  132. \n
  133. \n
  134. \n
  135. \n
  136. \n
  137. \n
  138. \n
  139. \n
  140. \n
  141. \n
  142. \n
  143. \n
  144. \n
  145. \n
  146. \n
  147. \n
  148. \n
  149. \n
  150. \n
  151. \n
  152. \n
  153. \n
  154. \n
  155. \n
  156. \n
  157. \n
  158. \n
  159. \n
  160. \n
  161. \n
  162. \n
  163. \n
  164. \n
  165. \n
  166. \n
  167. \n
  168. \n
  169. \n
  170. \n
  171. \n
  172. \n
  173. \n
  174. \n
  175. \n
  176. \n
  177. \n
  178. \n
  179. \n
  180. \n
  181. \n
  182. \n
  183. \n
  184. \n
  185. \n
  186. \n
  187. \n
  188. \n
  189. \n
  190. \n
  191. \n
  192. \n
  193. \n
  194. \n
  195. \n
  196. \n
  197. \n
  198. \n
  199. \n
  200. \n
  201. \n
  202. \n
  203. \n
  204. \n
  205. \n
  206. \n
  207. \n
  208. \n
  209. \n
  210. \n
  211. \n
  212. \n
  213. \n
  214. \n
  215. \n
  216. \n
  217. \n
  218. \n
  219. \n
  220. \n
  221. \n
  222. \n
  223. \n
  224. \n
  225. \n
  226. \n
  227. \n
  228. \n
  229. \n
  230. \n
  231. \n
  232. \n
  233. \n
  234. \n
  235. \n
  236. \n
  237. \n
  238. \n
  239. \n
  240. \n
  241. \n
  242. \n
  243. \n
  244. \n
  245. \n
  246. \n
  247. \n
  248. \n
  249. \n
  250. \n
  251. \n
  252. \n
  253. \n
  254. \n
  255. \n
  256. \n
  257. \n
  258. \n
  259. \n
  260. \n
  261. \n
  262. \n
  263. \n
  264. \n
  265. \n
  266. \n
  267. \n
  268. \n
  269. \n
  270. \n
  271. \n
  272. \n
  273. \n
  274. \n
  275. \n
  276. \n
  277. \n
  278. \n
  279. \n
  280. \n
  281. \n
  282. \n
  283. \n
  284. \n
  285. \n
  286. \n
  287. \n
  288. \n
  289. \n
  290. \n
  291. \n
  292. \n
  293. \n
  294. \n
  295. \n
  296. \n
  297. \n
  298. \n
  299. \n
  300. \n
  301. \n
  302. \n
  303. \n
  304. \n
  305. \n
  306. \n
  307. \n
  308. \n
  309. \n
  310. \n
  311. \n
  312. \n
  313. \n
  314. \n
  315. \n
  316. \n
  317. \n
  318. \n
  319. \n
  320. \n
  321. \n
  322. \n
  323. \n
  324. \n
  325. \n
  326. \n
  327. \n
  328. \n
  329. \n
  330. \n
  331. \n
  332. \n
  333. \n