SlideShare ist ein Scribd-Unternehmen logo
1 von 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.

Weitere ähnliche Inhalte

Was ist angesagt?

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
 

Was ist angesagt? (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
 

Andere mochten auch

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
 

Andere mochten auch (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
 

Ähnlich wie 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
 

Ähnlich wie 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
 

Kürzlich hochgeladen

Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider  Progress from Awareness to Implementation.pptxTales from a Passkey Provider  Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider Progress from Awareness to Implementation.pptxFIDO Alliance
 
ERP Contender Series: Acumatica vs. Sage Intacct
ERP Contender Series: Acumatica vs. Sage IntacctERP Contender Series: Acumatica vs. Sage Intacct
ERP Contender Series: Acumatica vs. Sage IntacctBrainSell Technologies
 
How we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdfHow we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdfSrushith Repakula
 
ASRock Industrial FDO Solutions in Action for Industrial Edge AI _ Kenny at A...
ASRock Industrial FDO Solutions in Action for Industrial Edge AI _ Kenny at A...ASRock Industrial FDO Solutions in Action for Industrial Edge AI _ Kenny at A...
ASRock Industrial FDO Solutions in Action for Industrial Edge AI _ Kenny at A...FIDO Alliance
 
AI mind or machine power point presentation
AI mind or machine power point presentationAI mind or machine power point presentation
AI mind or machine power point presentationyogeshlabana357357
 
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...FIDO Alliance
 
Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...
Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...
Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...Skynet Technologies
 
Where to Learn More About FDO _ Richard at FIDO Alliance.pdf
Where to Learn More About FDO _ Richard at FIDO Alliance.pdfWhere to Learn More About FDO _ Richard at FIDO Alliance.pdf
Where to Learn More About FDO _ Richard at FIDO Alliance.pdfFIDO Alliance
 
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdfLinux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdfFIDO Alliance
 
Easier, Faster, and More Powerful – Notes Document Properties Reimagined
Easier, Faster, and More Powerful – Notes Document Properties ReimaginedEasier, Faster, and More Powerful – Notes Document Properties Reimagined
Easier, Faster, and More Powerful – Notes Document Properties Reimaginedpanagenda
 
Design Guidelines for Passkeys 2024.pptx
Design Guidelines for Passkeys 2024.pptxDesign Guidelines for Passkeys 2024.pptx
Design Guidelines for Passkeys 2024.pptxFIDO Alliance
 
The Metaverse: Are We There Yet?
The  Metaverse:    Are   We  There  Yet?The  Metaverse:    Are   We  There  Yet?
The Metaverse: Are We There Yet?Mark Billinghurst
 
Google I/O Extended 2024 Warsaw
Google I/O Extended 2024 WarsawGoogle I/O Extended 2024 Warsaw
Google I/O Extended 2024 WarsawGDSC PJATK
 
The Value of Certifying Products for FDO _ Paul at FIDO Alliance.pdf
The Value of Certifying Products for FDO _ Paul at FIDO Alliance.pdfThe Value of Certifying Products for FDO _ Paul at FIDO Alliance.pdf
The Value of Certifying Products for FDO _ Paul at FIDO Alliance.pdfFIDO Alliance
 
Portal Kombat : extension du réseau de propagande russe
Portal Kombat : extension du réseau de propagande russePortal Kombat : extension du réseau de propagande russe
Portal Kombat : extension du réseau de propagande russe中 央社
 
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptxHarnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptxFIDO Alliance
 
WebRTC and SIP not just audio and video @ OpenSIPS 2024
WebRTC and SIP not just audio and video @ OpenSIPS 2024WebRTC and SIP not just audio and video @ OpenSIPS 2024
WebRTC and SIP not just audio and video @ OpenSIPS 2024Lorenzo Miniero
 
Working together SRE & Platform Engineering
Working together SRE & Platform EngineeringWorking together SRE & Platform Engineering
Working together SRE & Platform EngineeringMarcus Vechiato
 
WebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM PerformanceWebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM PerformanceSamy Fodil
 

Kürzlich hochgeladen (20)

Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider  Progress from Awareness to Implementation.pptxTales from a Passkey Provider  Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
 
ERP Contender Series: Acumatica vs. Sage Intacct
ERP Contender Series: Acumatica vs. Sage IntacctERP Contender Series: Acumatica vs. Sage Intacct
ERP Contender Series: Acumatica vs. Sage Intacct
 
How we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdfHow we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdf
 
ASRock Industrial FDO Solutions in Action for Industrial Edge AI _ Kenny at A...
ASRock Industrial FDO Solutions in Action for Industrial Edge AI _ Kenny at A...ASRock Industrial FDO Solutions in Action for Industrial Edge AI _ Kenny at A...
ASRock Industrial FDO Solutions in Action for Industrial Edge AI _ Kenny at A...
 
AI mind or machine power point presentation
AI mind or machine power point presentationAI mind or machine power point presentation
AI mind or machine power point presentation
 
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
 
Overview of Hyperledger Foundation
Overview of Hyperledger FoundationOverview of Hyperledger Foundation
Overview of Hyperledger Foundation
 
Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...
Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...
Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...
 
Where to Learn More About FDO _ Richard at FIDO Alliance.pdf
Where to Learn More About FDO _ Richard at FIDO Alliance.pdfWhere to Learn More About FDO _ Richard at FIDO Alliance.pdf
Where to Learn More About FDO _ Richard at FIDO Alliance.pdf
 
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdfLinux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
 
Easier, Faster, and More Powerful – Notes Document Properties Reimagined
Easier, Faster, and More Powerful – Notes Document Properties ReimaginedEasier, Faster, and More Powerful – Notes Document Properties Reimagined
Easier, Faster, and More Powerful – Notes Document Properties Reimagined
 
Design Guidelines for Passkeys 2024.pptx
Design Guidelines for Passkeys 2024.pptxDesign Guidelines for Passkeys 2024.pptx
Design Guidelines for Passkeys 2024.pptx
 
The Metaverse: Are We There Yet?
The  Metaverse:    Are   We  There  Yet?The  Metaverse:    Are   We  There  Yet?
The Metaverse: Are We There Yet?
 
Google I/O Extended 2024 Warsaw
Google I/O Extended 2024 WarsawGoogle I/O Extended 2024 Warsaw
Google I/O Extended 2024 Warsaw
 
The Value of Certifying Products for FDO _ Paul at FIDO Alliance.pdf
The Value of Certifying Products for FDO _ Paul at FIDO Alliance.pdfThe Value of Certifying Products for FDO _ Paul at FIDO Alliance.pdf
The Value of Certifying Products for FDO _ Paul at FIDO Alliance.pdf
 
Portal Kombat : extension du réseau de propagande russe
Portal Kombat : extension du réseau de propagande russePortal Kombat : extension du réseau de propagande russe
Portal Kombat : extension du réseau de propagande russe
 
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptxHarnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
 
WebRTC and SIP not just audio and video @ OpenSIPS 2024
WebRTC and SIP not just audio and video @ OpenSIPS 2024WebRTC and SIP not just audio and video @ OpenSIPS 2024
WebRTC and SIP not just audio and video @ OpenSIPS 2024
 
Working together SRE & Platform Engineering
Working together SRE & Platform EngineeringWorking together SRE & Platform Engineering
Working together SRE & Platform Engineering
 
WebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM PerformanceWebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM Performance
 

Building and Distributing PostgreSQL Extensions Without Learning C

Hinweis der Redaktion

  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