<?xml version="1.0" encoding="UTF-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us"><id>https://blog.pgxn.org/</id><title>⚙️ PGXN Blog</title><updated>2026-09-08T22:12:29Z</updated><link rel="self" type="application/atom+xml" href="https://blog.pgxn.org/feed.xml"/><link rel="alternate" type="text/html" href="https://blog.pgxn.org/"/><author><name>The PGXN Maintainers</name></author><generator uri="https://gohugo.io/" version="0.167.0">Hugo</generator><entry><id>https://blog.pgxn.org/post/827227334118604800</id><title type="html">Inter-documentation and image links now work on PGXN</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2026/relative-links/"/><updated>2026-10-07T16:13:48Z</updated><published>2026-09-08T22:12:29Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="images" label="Images"/><category scheme="https://blog.pgxn.org/tags" term="links" label="Links"/><category scheme="https://blog.pgxn.org/tags" term="site" label="Site"/><summary type="html">From the Department of It’s About Time: Inter-documentation and image links
now work on PGXN.</summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Way back in 2015, I opened a <a href="https://github.com/pgxn/pgxn-api/issues/38" title="Teach Parser to resolve doc links to the .html files they generate">PGXN API issue</a> to allow links between documents
rendered by PGXN to work. In 2024 I followed up with <a href="https://github.com/pgxn/pgxn-api/issues/40" title="Teach Parser to resolve relative Image Links">another issue</a> to enable
relative links to images to work. I mean, everyone wants this, right? It&rsquo;s how
your favorite source code sit works.</p>
<p>Now so does PGXN. As of today, links between documents and to images work.
Check it out in the newly-released <a href="https://pgxn.org/dist/chdb/0.1.1/" title="chdb 0.1.1 on PGXN">chdb extension docs</a>, which both link to
the <code>chdb</code> and <code>chdb_hook</code> docs and to some nice benchmark graphs.</p>
<p>Or see the <a href="https://pgxn.org/dist/apacheage/1.4.0/" title="ApacheAge 1.4.0 on PGXN">Apache Age docs</a>, which includes not only documentation images but
also decorative SVGs for the headers.</p>
<p>I&rsquo;m gradually reindexing all of the extensions so that any other documentation
with such links work work; it should be done by tomorrow. Then, at along last,
your extensions can look their very best.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/803014330152042496</id><title type="html">Improved Markdown Parsing</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2025/improved-markdown-parsing/"/><updated>2026-10-07T16:13:48Z</updated><published>2025-12-15T15:55:59Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="markdown" label="Markdown"/><category scheme="https://blog.pgxn.org/tags" term="discount" label="Discount"/><category scheme="https://blog.pgxn.org/tags" term="tables" label="Tables"/><summary type="html"><![CDATA[<p>Quick announcement to say that I&rsquo;ve replaced the ancient markdown parser with
a new one, <a href="https://www.pell.portland.or.us/~orc/Code/discount/">discount</a>, which supports tables, code fences, definition lists,
and more. I reindexed <a href="https://pgxn.org/dist/pg_clickhouse" title="pg_clickhouse on PGXN">pg_clickhouse</a> this morning and it&rsquo;s sooo nice to see
<a href="https://pgxn.org/dist/pg_clickhouse/0.1.0/#Test.Case:.TPC-H">the table</a> properly formatted.</p>
<p>New uploads will use this parser for Markdown (but not MultiMarkdown) files
from now on. I think I&rsquo;ll start working on reindexing all existing extensions,
too. Give me a holler if you don&rsquo;t see an improvement in your extensions in
the next few days.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Quick announcement to say that I&rsquo;ve replaced the ancient markdown parser with
a new one, <a href="https://www.pell.portland.or.us/~orc/Code/discount/">discount</a>, which supports tables, code fences, definition lists,
and more. I reindexed <a href="https://pgxn.org/dist/pg_clickhouse" title="pg_clickhouse on PGXN">pg_clickhouse</a> this morning and it&rsquo;s sooo nice to see
<a href="https://pgxn.org/dist/pg_clickhouse/0.1.0/#Test.Case:.TPC-H">the table</a> properly formatted.</p>
<p>New uploads will use this parser for Markdown (but not MultiMarkdown) files
from now on. I think I&rsquo;ll start working on reindexing all existing extensions,
too. Give me a holler if you don&rsquo;t see an improvement in your extensions in
the next few days.</p>
<p><img src="/2025/improved-markdown-parsing/discount_table.png" alt="Screenshot of a Discount-created table on PGXN"></p>
]]></content></entry><entry><id>https://blog.pgxn.org/2024/new-pgxn-mastodon-account/</id><title type="html">New PGXN Mastodon Account</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2024/new-pgxn-mastodon-account/"/><updated>2026-10-07T16:13:48Z</updated><published>2024-12-01T16:03:09Z</published><author><name>David E. Wheeler</name></author><summary type="html"><![CDATA[Sadly, the home of the PGXN Mastodon bot for the last two years,
<a href="https://botsin.space/">botsin.space</a> is shutting down. I&rsquo;ve created a new
account, <a href="https://mastodon.social/@pgxn">@pgxn@mastodon.social</a> and moved all
the followers. Please give it a follow if you didn&rsquo;t follow the old account,
and stay up-to-date on the latest PGXN releases!]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[Sadly, the home of the PGXN Mastodon bot for the last two years,
<a href="https://botsin.space/">botsin.space</a> is shutting down. I&rsquo;ve created a new
account, <a href="https://mastodon.social/@pgxn">@pgxn@mastodon.social</a> and moved all
the followers. Please give it a follow if you didn&rsquo;t follow the old account,
and stay up-to-date on the latest PGXN releases!]]></content></entry><entry><id>https://blog.pgxn.org/post/745588549026447361</id><title type="html">Feedback Wanted: Meta Spec Sketch</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2024/updated-meta-spec-sketch/"/><updated>2026-10-07T16:13:48Z</updated><published>2024-03-21T00:00:00Z</published><author><name>David E. Wheeler</name></author><summary type="html">New post up on Just a Theory, &lt;a href="https://justatheory.com/2024/03/rfc-pgxn-metadata-sketch/">RFC: PGXN Metadata Sketch&lt;/a>, seeking feedback on
a slew of ideas to update the &lt;a href="https://pgxn.org/spec/">PGXN Meta Spec&lt;/a>. Help us to help make the
future of PGXN and the broader extension ecosystem better!</summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve">New post up on Just a Theory, &lt;a href="https://justatheory.com/2024/03/rfc-pgxn-metadata-sketch/">RFC: PGXN Metadata Sketch&lt;/a>, seeking feedback on
a slew of ideas to update the &lt;a href="https://pgxn.org/spec/">PGXN Meta Spec&lt;/a>. Help us to help make the
future of PGXN and the broader extension ecosystem better!</content></entry><entry><id>https://blog.pgxn.org/post/743059495415119873</id><title type="html">Recent PGXN Improvements</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2024/recent-work/"/><updated>2026-10-07T16:13:48Z</updated><published>2024-02-22T00:00:00Z</published><author><name>David E. Wheeler</name></author><summary type="html"><![CDATA[<p>One of the perks of my <a href="https://tembo.io/blog/welcoming-david-wheeler">new gig at Tembo</a> is that I have more time to work on
PGXN. In the last ten years I&rsquo;ve had very little time to give, so things have
stagnated. The <a href="https://github.com/pgxn/pgxn-api">API</a>, for example, hasn&rsquo;t seen a meaningful update since 2016!</p>
<p>But that&rsquo;s all changed now, and every bit of the PGXN architecture has
experienced a fair bit of TLC in the last few weeks. A quick review.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>One of the perks of my <a href="https://tembo.io/blog/welcoming-david-wheeler">new gig at Tembo</a> is that I have more time to work on
PGXN. In the last ten years I&rsquo;ve had very little time to give, so things have
stagnated. The <a href="https://github.com/pgxn/pgxn-api">API</a>, for example, hasn&rsquo;t seen a meaningful update since 2016!</p>
<p>But that&rsquo;s all changed now, and every bit of the PGXN architecture has
experienced a fair bit of TLC in the last few weeks. A quick review.</p>
<h2 id="pgxn-manager">PGXN Manager</h2>
<p><a href="https://manager.pgxn.org">PGXN Manager</a> provides user registration and extension release services. It
maintains the root registry of the project, ensuring the consistency of
download formatting and naming (<code>$extension-$version.zip</code> if you&rsquo;re
wondering). <a href="https://blog.pgxn.org/post/709635160523620352/hello-mastodon">Last year</a> I added a <a href="https://www.postgresql.org/docs/current/sql-notify.html">LISTEN/NOTIFY</a> queue for publishing new
releases to Twitter and Mastodon, replacing the old Twitter posting on upload.
It has worked quite well (although Twitter killed the API so we no longer post
<a href="https://twitter.com/pgxn">there</a>), but has sometimes disappeared for days or weeks at a time. I&rsquo;ve
futzed with it over the past year, but last weekend I think I finally got to
the bottom of the problem.</p>
<p>As a result, the <a href="https://mastodon.social/@pgxn">PGXN Mastodon account</a> should say much more up-to-date than
it sometimes has. I&rsquo;ve also added additional logging capability, because
sometimes the posts fail and I need better clarity into what failed and how so
it, too, can be fixed. So messaging should continue to become more reliable.
Oh, and testing and unstable releases are now prominently marked as such in
the posts (or will be, next time someone uploads a non-stable release).</p>
<p>I also did a little work on the formatting of the <a href="https://manager.pgxn.org/howto">How To</a> doc, upgrading to
<a href="https://fletcherpenney.net/multimarkdown/">MultiMarkdown</a> 6 and removing some post-processing that appended <a href="https://github.com/pgxn/pgxn-manager/issues/76">unwanted
backslashes</a> to the ends of code lines.</p>
<h2 id="pgxn-api">PGXN API</h2>
<p>PGXN API (<a href="https://github.com/pgxn/pgxn-api/wiki">docs</a>) has been the least loved part of the architecture, but all
that changed in the last couple weeks. I finally got over my trepidation and
got it working where I could hack on it again. Turns out, the pending issues
and to-dos — some dating back to 2011! — weren&rsquo;t so difficult to take on as I
had feared. In the last few weeks, API has finally come unstuck and evolved:</p>
<ul>
<li>
<p>Switched the parsing of Markdown documents from the quite old
<a href="https://metacpan.org/pod/Text::Markdown">Text::Markdown</a> module to <a href="https://metacpan.org/pod/CommonMark">CommonMark</a>, which is a thin wrapper around
the well-maintained and modern <a href="https://github.com/commonmark/cmark/">cmark library</a>. The main way in which this
change will be visible is in the support for fenced code blocks. Compare
the <a href="https://pgxn.org/dist/pg_later/0.0.14/README.html">pg_later 0.0.14 README</a>, formatted with the old parser, with
<a href="https://pgxn.org/dist/pg_later/0.1.0/README.html">pg_later 0.1.0 README</a>, formatted with the new parser.</p>
</li>
<li>
<p>Updated the full text indexer to index the file identified in the
<code>META.json</code> as the documentation for an extension even if the file name is
different from the extension (<a href="https://github.com/pgxn/pgxn-api/issues/10">issue 10</a>), including the README.</p>
<p>Furthermore, if the indexer finds no documentation for the extension,
either in its metadata or an appropriately-named file, it falls back on
the README (<a href="https://github.com/pgxn/pgxn-api/issues/12">issue 12</a>).</p>
<p>These two fixes, the oldest and most significant of the whole system,
greatly increase the accuracy of documentation search, because so many
extensions have no docs other than the README. Prior to this fix, for
example, a <a href="https://pgxn.org/search?q=later&amp;in=docs">search for &ldquo;later&rdquo;</a> failed to turn up <a href="https://pgxn.org/dist/pg_later/">pg_later</a>, and now it&rsquo;s
appropriately the first result.</p>
</li>
<li>
<p>The indexer now indexes releases marked &ldquo;testing&rdquo; or &ldquo;unstable&rdquo; in the
metadata if there is no stable release (<a href="https://github.com/pgxn/pgxn-api/issues/2">issue 2</a>!). This makes new
extensions under development easier to find on the site than previously,
when only stable releases were indexed. However, once a stable release is
made, no more testing or unstable releases will be indexed. I believe this
is more intuitive behavior.</p>
</li>
<li>
<p>Also fixed some permissions issue, ensuring that source code unzipped for
browsing is accessible to the app. In other words, a zip file without READ
permission would trigger a not found response; the API now ensures that
all files and directories are accessible to the application.</p>
</li>
<li>
<p>As a bonus, the API now also recognizes plain text files (<code>.txt</code> and
<code>.text</code>) as documentation and indexes their contents and adds links for
display on the site. Previously plain text files other than READMEs were
ignored (<a href="https://github.com/pgxn/pgxn-api/issues/13">issue 13</a>).</p>
</li>
</ul>
<p>I&rsquo;m quite pleased with some of these fixes, and relieved to have finally
cleared out the karmic overhang of the backlog. But the indexing and HTML
rendering changes, in particular, are tactical: until the entire registry can
be re-indexed, only new uploads will benefit from the indexing improvements. I
hope to find time to work on the indexing project soon.</p>
<h2 id="pgxn-site">PGXN Site</h2>
<p>The <a href="https://github.com/pgxn/pgxn-site">pgxn-site</a> project powers the main site, <a href="https://pgxn.org">pgxn.org</a>, and has seen 7 new
releases since the beginning of the year! Notable changes:</p>
<ul>
<li>
<p>Changed the default search index from Documentation too Distributions,
because most release include no docs other than a READMe, which was
indexed as part of the distribution and not the extension contained in a
distribution. As a result, searches return more relevant and comprehensive
results. However, I say &ldquo;was indexed&rdquo; because, as described above, the
PGXN API has since been updated to appropriately identify and index
READMEs as extension documentation. So this change might get reverted at
some point, but not before the entire registry can be re-indexed.</p>
</li>
<li>
<p>Changed the link to the How To in the footer from the somewhat opaque
&ldquo;Release It!&rdquo; to &ldquo;Release on PGXN&rdquo;. Should make it clearer what it is.
Also, have you considered releasing your extension on PGXN? You should!</p>
</li>
<li>
<p>Fixed &ldquo;not found&rdquo; errors for links to files with uppercase letters, such
as <a href="https://pgxn.org/dist/vectorize/vector-serve/README.html">/dist/vectorize/vector-serve/README.html</a>.</p>
</li>
<li>
<p>Tweaked the CSS a bit to improve list item alignment in the &ldquo;Contents&rdquo; of
doc pages (like <a href="https://pgxn.org/extension/semver">semver</a>), as well as the spacing between definition
lists, as in the <a href="https://pgxn.org/faq/">FAQ</a>.</p>
</li>
<li>
<p>Switched the parsing and formatting of the static pages (<a href="https://pgxn.org/about/">about</a>, <a href="https://pgxn.org/art/">art</a>,
<a href="https://pgxn.org/faq/">FAQ</a>, <a href="https://pgxn.org/feedback/">feedback</a>, and <a href="https://pgxn.org/mirroring/">mirroring</a>) from the quite ancient
<a href="https://metacpan.org/pod/Text::MultiMarkdown">Text::MultiMarkdown</a> module to <a href="https://fletcherpenney.net/multimarkdown/">MultiMarkdown</a> 6.</p>
</li>
<li>
<p>Oh, and I fixed or removed a bunch of outdated links from those pages, and
replaced all relevant HTTP URLs with HTTPS URLs. Then had to revert those
changes from a bunch of SVG files, where
<code>xmlns=&quot;http://www.w3.org/2000/svg&quot;</code> is relevant but
<code>xmlns=&quot;https://www.w3.org/2000/svg&quot;</code> is not. 🤦🏻‍♂️</p>
</li>
</ul>
<h2 id="grab-bag">Grab Bag</h2>
<p>I&rsquo;ve made a number of more iterative improvements, as well, mostly to the
maintainability of PGXN projects:</p>
<ul>
<li>
<p>The GitHub repositories for all of these projects have been updated with
comprehensive test and release workflows, as well as improved Perl release
configuration.</p>
</li>
<li>
<p>The <a href="https://metacpan.org/dist/WWW-PGXN">WWW::PGXN</a>, <a href="https://metacpan.org/dist/PGXN-API-Searcher">PGXN::API::Searcher</a> Perl modules have been updated with
test and release workflows and improved Perl release configuration, as
well.</p>
</li>
<li>
<p>The <code>libexec</code> configuration of the <a href="https://formulae.brew.sh/formula/pgxnclient">PGXN Client Homebrew formula</a> has been
<a href="https://github.com/Homebrew/homebrew-core/pull/163685">fixed</a>, allowing <a href="https://metacpan.org/dist/PGXN-Meta-Validator">PGXN::Meta::Validator</a> to install itself in the proper
place to allow <code>pgxn validate-meta</code> to work properly on Homebrew
managed-systems (like mine!)</p>
</li>
<li>
<p>The <a href="https://github.com/pgxn/docker-pgxn-tools/">pgxn/pgxn-tools Docker image</a>, which saw some fixes and improvements
<a href="https://blog.pgxn.org/post/741049567045468160/pgxn-tools-v4">last month</a>, has continued to evolve, as well. Recent improvements
include support for <a href="https://www.postgresql.org/docs/current/regress-tap.html">PostgreSQL TAP tests</a> (not to be confused with
<a href="https://pgtap.org/">pgTAP</a>, already supported), finer control over managing the Postgres
cluster — or multiple clusters — by setting <code>NO_CLUSTER=</code> to prevent
<code>pg-start</code> from creating the cluster. Both these improvements better
enable multi-cluster testing.</p>
</li>
</ul>
<h2 id="the-end">The End</h2>
<p>I think that about covers it. Although I&rsquo;m <a href="https://justatheory.com/2024/02/extension-metadata-typology/">thinking</a> and <a href="https://tembo.io/blog/pgxn-ecosystem-jobs">writing</a> quite a
lot about what&rsquo;s next for the Postgres extension ecosystem, I expect to
continue tending to the care and feeding and improvement of PGXN as well. To
the future!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/741225763692494848</id><title type="html">Presentation: Introduction to the PGXN</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2024/presentation-introduction-to-the-pgxn/"/><updated>2026-10-07T16:13:48Z</updated><published>2024-02-02T15:32:53Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="software-architecture" label="Software Architecture"/><category scheme="https://blog.pgxn.org/tags" term="presentation" label="Presentation"/><category scheme="https://blog.pgxn.org/tags" term="pgxn" label="PGXN"/><summary type="html"><![CDATA[<p><a href="https://tembo.io/blog/pgxn-architecture"><img src="/2024/presentation-introduction-to-the-pgxn/architecture.png" alt="Presentation: Introduction to the PGXN Architecture | Tembo"></a></p>
<p>The Tembo blog has posted a presentation on the PGXN architecture. There
hasn&rsquo;t been much coverage of it since a few posts here on the PGXN blog back
in 2012, so worth a revisit &mdash; and a fair bit changed in the interim, as
well!</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p><a href="https://tembo.io/blog/pgxn-architecture"><img src="/2024/presentation-introduction-to-the-pgxn/architecture.png" alt="Presentation: Introduction to the PGXN Architecture | Tembo"></a></p>
<p>The Tembo blog has posted a presentation on the PGXN architecture. There
hasn&rsquo;t been much coverage of it since a few posts here on the PGXN blog back
in 2012, so worth a revisit &mdash; and a fair bit changed in the interim, as
well!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/741049567045468160</id><title type="html">PGXN Tools Docker Image Updated</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2024/pgxn-tools-v4/"/><updated>2026-10-07T16:13:48Z</updated><published>2024-01-31T16:52:19Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="pgxn" label="PGXN"/><category scheme="https://blog.pgxn.org/tags" term="docker" label="Docker"/><category scheme="https://blog.pgxn.org/tags" term="github-actions" label="GitHub Actions"/><category scheme="https://blog.pgxn.org/tags" term="github-workflows" label="GitHub Workflows"/><category scheme="https://blog.pgxn.org/tags" term="release" label="Release"/><category scheme="https://blog.pgxn.org/tags" term="bundle" label="Bundle"/><summary type="html"><![CDATA[<p>Just a quick note to highlight some bug fixes and improvements in the
<a href="https://github.com/pgxn/docker-pgxn-tools/">pgxn/pgxn-tools Docker image</a> in the last few weeks.</p>
<p>v1.4.0 adds the <code>GIT_BUNDLE_OPTS</code> and <code>ZIP_BUNDLE_OPTS</code> environment variables.
The former is passed to <code>git archive</code> and the latter to zip, and let
additional options be passed to those commands. For example, the <a href="https://pgxn.org/dist/vectorize/">vectorize
extension</a>&rsquo;s <a href="https://github.com/tembo-io/pg_vectorize/blob/v0.9.0/.github/workflows/pgxn-release.yml">release workflow</a> sets <code>GIT_BUNDLE_OPTS: --add-file META.json</code>
because git archive archives only checked-in files, and <code>META.json</code> is not
checked in but <a href="https://github.com/tembo-io/pg_vectorize/blob/v0.9.0/Makefile#L17-L18">generated from</a> <code>META.json.in</code>.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Just a quick note to highlight some bug fixes and improvements in the
<a href="https://github.com/pgxn/docker-pgxn-tools/">pgxn/pgxn-tools Docker image</a> in the last few weeks.</p>
<p>v1.4.0 adds the <code>GIT_BUNDLE_OPTS</code> and <code>ZIP_BUNDLE_OPTS</code> environment variables.
The former is passed to <code>git archive</code> and the latter to zip, and let
additional options be passed to those commands. For example, the <a href="https://pgxn.org/dist/vectorize/">vectorize
extension</a>&rsquo;s <a href="https://github.com/tembo-io/pg_vectorize/blob/v0.9.0/.github/workflows/pgxn-release.yml">release workflow</a> sets <code>GIT_BUNDLE_OPTS: --add-file META.json</code>
because git archive archives only checked-in files, and <code>META.json</code> is not
checked in but <a href="https://github.com/tembo-io/pg_vectorize/blob/v0.9.0/Makefile#L17-L18">generated from</a> <code>META.json.in</code>.</p>
<p>v1.4.1 <a href="https://github.com/pgxn/docker-pgxn-tools/commit/1114aed">fixes an issue</a> where <code>git archive</code> was never actually used to build a
release zip archive. This changed at some point without noticing due to the
introduction of the <code>safe.directory</code> configuration in recent versions of Git.
Inside the container the directory was never trusted, and the <code>pgxn-bundle</code>
command caught the error, decided it wasn&rsquo;t working with a Git repository, and
used the <code>zip</code> command, instead.</p>
<p>As a result, a number of recent releases have included files they shouldn&rsquo;t,
such as the contents of the <code>.git</code> directory. <a href="https://github.com/pgxn/docker-pgxn-tools/commit/1114aed">The fix</a>
<a href="https://stackoverflow.com/a/73100228/79202">disables</a> <code>safe.directory</code> so that the repository directory is always trusted
inside the container, as needed in GitHub actions.</p>
<p>If you use <code>pgxn-bundle</code> in a GitHub workflow, be aware that recent releases
(since November 2021 at least) include stuff that it should not, including the
<code>.git</code> directory (here&rsquo;s <a href="https://gist.github.com/theory/93c93571200aad02e93170c6d2c93cbe">a list</a>) and patterns excluded in <code>.gitattributes</code>.
Your next release should be cleaner.</p>
<p>v1.4.1 also allows the setting of the <code>PROFILE</code> environment variable, so that
the default <code>PROFILE=--Werror</code> can be overridden.</p>
<p>v1.4.2 adds (and v1.4.3 improves, see below) <code>git-archive-all</code> to the image,
and the <code>GIT_ARCHIVE_CMD</code> environment variable to tell <code>pgxn-bundle</code> which archive
command to use, either <code>archive</code> or <code>archive-all</code>. The latter is useful for
repositories that use Git submodules and need to include their contents in the
release, as demonstrated in <a href="https://github.com/plv8/plv8/pull/570/files#diff-4e4745c3f39dff4c307faf79bfa1242cc5ba2eb5947bebdcddc8d97eeb40685fR15">this plv8 pull request</a>.</p>
<p>v1.4.2 also adds <a href="https://cmake.org/"><code>cmake</code></a> and the <code>libarchive-tools</code> package to the image.
The latter bundles in <a href="https://libarchive.org/">libarchive</a> tools like <code>bsdtar</code> and <code>bsdcpio</code>, which
might be useful for editing Zip files in place.</p>
<p>v1.4.3 passes the <code>--force-submodules</code> option to <code>git-archive-all</code>, because
otherwise submodules weren&rsquo;t included in the bundle.</p>
<p>All of this should just start showing up in your GitHub workflows as long as
they use <code>container: pgxn/pgxn-tools</code> or <code>container: pgxn/pgxn-tools@v1</code>.</p>
<p>Questions? Post em in the <a href="https://postgresteam.slack.com/archives/C056ZA93H1A">#extensions</a> channel on the <a href="https://pgtreats.info/slack-invite">Postgres Slack</a> or to
the attention of the <a href="https://mastodon.social/@pgxn">PGXN Mastodon bot</a>.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/709635160523620352</id><title type="html">Hello Mastodon 🐘</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2023/hello-mastodon/"/><updated>2026-10-07T16:13:48Z</updated><published>2023-02-18T22:53:46Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="pgxn" label="PGXN"/><category scheme="https://blog.pgxn.org/tags" term="mastodon" label="Mastodon"/><category scheme="https://blog.pgxn.org/tags" term="twitter" label="Twitter"/><category scheme="https://blog.pgxn.org/tags" term="postgresql" label="PostgreSQL"/><category scheme="https://blog.pgxn.org/tags" term="listen/notify" label="LISTEN/NOTIFY"/><summary type="html"><![CDATA[<p>Hey all you Postgres people out there! Just wanted to make a quick post
regarding the PGXN <a href="https://twitter.com/pgxn">twitter bot</a>. In light of the recent announcements by
Twitter to charge for API use, I updated <a href="https://manager.pgxn.org">PGXN Manager</a> to also post to
Mastodon! If you&rsquo;re have joined the exodus to the Fediverse, give it a follow:</p>
<p><a href="https://mastodon.social/@pgxn">@pgxn@mastodon.social</a></p>
<p>In fact, I made some time to rewrite the notification bits of the manager
service. Previously there was just some code jammed into the upload controller
that would make an API call to Twitter. I ripped that out, and added a trigger
to the distributions table that posts a a <a href="https://www.postgresql.org/docs/current/sql-notify.html">LISTEN/NOTIFY</a> message.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Hey all you Postgres people out there! Just wanted to make a quick post
regarding the PGXN <a href="https://twitter.com/pgxn">twitter bot</a>. In light of the recent announcements by
Twitter to charge for API use, I updated <a href="https://manager.pgxn.org">PGXN Manager</a> to also post to
Mastodon! If you&rsquo;re have joined the exodus to the Fediverse, give it a follow:</p>
<p><a href="https://mastodon.social/@pgxn">@pgxn@mastodon.social</a></p>
<p>In fact, I made some time to rewrite the notification bits of the manager
service. Previously there was just some code jammed into the upload controller
that would make an API call to Twitter. I ripped that out, and added a trigger
to the distributions table that posts a a <a href="https://www.postgresql.org/docs/current/sql-notify.html">LISTEN/NOTIFY</a> message.</p>
<p>Then I wrote a little service that checks for new notifications every 5
seconds and dispatches to one mor more configured consumers. Today there are
just two: Twitter and Mastodon; in the future the might be more, especially
since I also added triggers to the users and mirrors tables, so we could have
the bot announce new mirrors and users. Should be pretty easy to do, might put
in the time in the next few weeks.</p>
<p>Oh, and for fun, I also added some emoji to the Mastodon posts, as well as the
abstract from new releases. Makes the messages more informative than on
Twitter. Of course, we could probably do the same, there, especially since the
post length was extended to 280 characters sometime after the original bot. I
have in mind to make a little library that allows the customization of
messages via configuration.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/655912318549606400</id><title type="html">Password Storage Update</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2021/password-storage-update/"/><updated>2026-10-07T16:13:48Z</updated><published>2021-07-05T23:12:12Z</published><author><name>David E. Wheeler</name></author><summary type="html"><![CDATA[Just a quick note to say that <a href="https://manager.pgxn.org/">PGXN Manager</a> has been updated with more secure
password storage. Existing passwords are unmodified, but the next time you
change your password, it will be upgraded to a more robust password hashing
algorithm that&rsquo;s more resistant to attacks. I recommend everyone update their
passwords. If you have a PGXN Manager account, just hit <a href="https://manager.pgxn.org/account/forgotten">password reset</a>,
enter your username or email address, and check your mail for a reset link.
Once you update your password, it will be stored in the new, more secure
format.]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[Just a quick note to say that <a href="https://manager.pgxn.org/">PGXN Manager</a> has been updated with more secure
password storage. Existing passwords are unmodified, but the next time you
change your password, it will be upgraded to a more robust password hashing
algorithm that&rsquo;s more resistant to attacks. I recommend everyone update their
passwords. If you have a PGXN Manager account, just hit <a href="https://manager.pgxn.org/account/forgotten">password reset</a>,
enter your username or email address, and check your mail for a reset link.
Once you update your password, it will be stored in the new, more secure
format.]]></content></entry><entry><id>https://blog.pgxn.org/post/651216661677064192</id><title type="html">A Few Belated PGXN Updates</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2021/a-few-belated-pgxn-updates/"/><updated>2026-10-07T16:13:48Z</updated><published>2021-05-15T03:16:44Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="pgxn" label="PGXN"/><category scheme="https://blog.pgxn.org/tags" term="upgrade" label="Upgrade"/><category scheme="https://blog.pgxn.org/tags" term="tls" label="TLS"/><category scheme="https://blog.pgxn.org/tags" term="retina" label="Retina"/><summary type="html"><![CDATA[<p>The last couple weeks I&rsquo;ve returned to PGXN and made a few updates. Nothing
huge, but all long overdue.</p>
<p>First up, <a href="https://manager.pgxn.org">PGXN Manager</a> is all TSL now. No public HTTP-only site. What
started as a convenient division of labor (http for the public site and https
for the authenticated site) turned out to be quite irksome &mdash; especially
since some browsers wouldn&rsquo;t load the non-TLS site at all anymore. So I did
away with it, and now it&rsquo;s all TLS, authenticated and not. (The API and search
sites have been TLS for a while now.)</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>The last couple weeks I&rsquo;ve returned to PGXN and made a few updates. Nothing
huge, but all long overdue.</p>
<p>First up, <a href="https://manager.pgxn.org">PGXN Manager</a> is all TSL now. No public HTTP-only site. What
started as a convenient division of labor (http for the public site and https
for the authenticated site) turned out to be quite irksome &mdash; especially
since some browsers wouldn&rsquo;t load the non-TLS site at all anymore. So I did
away with it, and now it&rsquo;s all TLS, authenticated and not. (The API and search
sites have been TLS for a while now.)</p>
<p>I also fixed a few long-standing bugs in PGXN Manager, most of which weren&rsquo;t
visible to end-users, but have annoyed me over the years. A few silly server
errors, some instances of uploads failing for anything other than zip files,
that sort of thing.</p>
<p>Oh, and if you&rsquo;re a extension author, PGXN Manger now allows updates to old
distribution versions to be uploaded. Previously it only allowed a new version
to be greater than all previous versions. Now it will allow a new X.Y.Z
version if X.Y previously existed and the new .Z is greater, and a new X.Y
version if X previously existed and the new .Y is greater than any previous
X.Y. To get this to work properly, I also dropped the check for versions of
extensions in the uploaded files. It would just be too complicated to add a
bunch of rules more likely to annoy than not. So it now only enforces patterns
for distribution release versions. I trust extension authors not to then lower
extension versions on new releases &mdash; that would just be silly, and not
helpful to your users.</p>
<p>As part of this work, I also revamped the management of the PGXN server. Back
in 2010 I wrote Capistrano files to manage the server, but have long ceased to
use them, as they ceased to work. I&rsquo;ve now removed all that detritus from the
PGXN Manager, API, and Site repositories, and replaced them all with a single
new repository, <a href="https://github.com/pgxn/pgxn-ops">pgxn-ops</a>, which contains Ansible playbooks to manage all the
services. They don&rsquo;t deal with a lot of the server-side stuff, which depesz
handles separately, but they now make it much easier to build and deploy a new
release, restart it remotely, manage passwords, etc.</p>
<p>And finally, I&rsquo;ve updated the <a href="https://pgxn.org/">PGXN search site</a>. In addition to fixing a few
long-standing minor annoyances (borders around images linked in documentation,
broken links, etc.), I also updated most of the graphics to be
retina-friendly. So it should look a lot sharper on your hi-res screens now.
Check it out!</p>
<p>Next up I think I&rsquo;d like to make the search site more mobile-friendly, and
then perhaps I&rsquo;ll finally go back and attack the terrible search provided by
the API. I&rsquo;ll try to do it in less than five years this time.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/128057409023</id><title type="html">PGXN Manager Upgraded</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2015/pgxn-manager-upgraded/"/><updated>2026-10-07T16:13:48Z</updated><published>2015-08-31T21:48:54Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="release" label="Release"/><category scheme="https://blog.pgxn.org/tags" term="upgrade" label="Upgrade"/><category scheme="https://blog.pgxn.org/tags" term="versions" label="Versions"/><summary type="html"><![CDATA[I took a little time this summer to finally address some nagging issues in
<a href="https://manager.pgxn.org/">PGXN Manager</a>, the site to which extensions are uploaded and added to the
<a href="https://master.pgxn.org/">master repository</a>. Most of the changes were internal, improving the
interface through which I sometimes have to re-index a release. There are also
a couple of minor changes to the sample <code>Makefile</code> in the <a href="https://manager.pgxn.org/howto">How To</a>. But the
most important change for new releases going forward is that version ordering
is now enforced. That means two things:]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I took a little time this summer to finally address some nagging issues in
<a href="https://manager.pgxn.org/">PGXN Manager</a>, the site to which extensions are uploaded and added to the
<a href="https://master.pgxn.org/">master repository</a>. Most of the changes were internal, improving the
interface through which I sometimes have to re-index a release. There are also
a couple of minor changes to the sample <code>Makefile</code> in the <a href="https://manager.pgxn.org/howto">How To</a>. But the
most important change for new releases going forward is that version ordering
is now enforced. That means two things:</p>
<ul>
<li>Distribution versions <strong>must</strong> be greater than the version of the previous
release. You can no longer release <code>v1.2.0</code> today and <code>v1.1.0</code> tomorrow.</li>
<li>Extension versions <strong>must</strong> be greater than or equal to versions in previous
releases. It&rsquo;s pretty common to have a new release version but have embedded
extensions be the same version as before. But they can&rsquo;t be any less than
before.</li>
</ul>
<p>The changes have been applied to the reindexing code, as well, which also
prevents versions from being greater than in subsequent releases. That won&rsquo;t
come up very often&mdash;most of the time, I end up reindexing something that has
just been released. But in the future I expect to add an interface for release
managers to <a href="https://github.com/pgxn/pgxn-manager/issues/49">reindex their own distributions</a>, so it may be that someone
reindexes a release that&rsquo;s a few versions old. Or not. But just in case, it&rsquo;ll
be handled.</p>
<p>So what are you going to release on PGXN today? <a href="https://manager.pgxn.org/howto">Get to it!</a>.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/116087351668</id><title type="html">PGXN Release Badges</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2015/badges/"/><updated>2026-10-07T16:13:48Z</updated><published>2015-04-11T04:34:08Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="badges" label="Badges"/><category scheme="https://blog.pgxn.org/tags" term="gemfury" label="Gemfury"/><category scheme="https://blog.pgxn.org/tags" term="travis-ci" label="Travis CI"/><category scheme="https://blog.pgxn.org/tags" term="coveralls" label="Coveralls"/><category scheme="https://blog.pgxn.org/tags" term="github" label="GitHub"/><summary type="html"><![CDATA[<p>Looks like it&rsquo;s been close to two years since my last post on the PGXN blog.
Apologies for that. I&rsquo;ve thought for a while maybe I should organize an
&ldquo;extension of the week&rdquo; series or something. Would there be interest in such a
thing?</p>
<p>Meanwhile, I&rsquo;m finally getting back to posting to report on a fun thing you
can now do with your PGXN distributions. Thanks to the <a href="https://badge.fury.io">Version Badge</a> service
from the nice folks at <a href="https://gemfury.com/">Gemfury</a>, you can badge your distributions! Badges
look like this:</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Looks like it&rsquo;s been close to two years since my last post on the PGXN blog.
Apologies for that. I&rsquo;ve thought for a while maybe I should organize an
&ldquo;extension of the week&rdquo; series or something. Would there be interest in such a
thing?</p>
<p>Meanwhile, I&rsquo;m finally getting back to posting to report on a fun thing you
can now do with your PGXN distributions. Thanks to the <a href="https://badge.fury.io">Version Badge</a> service
from the nice folks at <a href="https://gemfury.com/">Gemfury</a>, you can badge your distributions! Badges
look like this:</p>
<p><a href="https://badge.fury.io/pg/semver"><img src="https://badge.fury.io/pg/semver.svg" alt="PGXN version"></a></p>
<p>You&rsquo;ve no doubt seem similar badges for Ruby, Perl, and Python modules. Now
the fun comes to PGXN. Want in? Assuming you have a distribution named
<code>pgfoo</code>, just put code like this into the README file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">[<span class="nt">![PGXN version</span>](<span class="na">https://badge.fury.io/pg/pgfoo.svg</span>)](https://badge.fury.io/pg/pgfoo)
</span></span></code></pre></div><p>This is <a href="https://daringfireball.net/projects/markdown/">Markdown</a> format; use the syntax appropriate to your preferred README
format to get the badge to show up on GitHub and PGXN.</p>
<p>That&rsquo;s it! The badge will show the current releases version on PGXN, and the
button will link through to PGXN.</p>
<p>Use <a href="https://travis-ci.org">Travis CI</a>? You can badge your build status, too, as I&rsquo;ve done for
<a href="https://pgxn.org/dist/pgtap">pgTAP</a>, like this:</p>
<p><a href="https://travis-ci.org/theory/pgtap"><img src="https://travis-ci.org/theory/pgtap.png" alt="Build Status"></a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">[<span class="nt">![Build Status</span>](<span class="na">https://travis-ci.org/theory/pgtap.png</span>)](https://travis-ci.org/theory/pgtap)
</span></span></code></pre></div><p><a href="https://coveralls.io">Coveralls</a> provides patches, too. I&rsquo;ve used them for <a href="https://github.com/theory/sqitch">Sqitch</a>, though I&rsquo;ve
not yet taken the time figure out how to do coverage testing with PostgreSQL
extensions. If you have, you can badge your current coverage like so:</p>
<p><a href="https://coveralls.io/r/theory/sqitch"><img src="https://coveralls.io/repos/theory/sqitch/badge.svg" alt="Coverage Status"></a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">[<span class="nt">![Coverage Status</span>](<span class="na">https://coveralls.io/repos/theory/sqitch/badge.svg</span>)](https://coveralls.io/r/theory/sqitch)
</span></span></code></pre></div><p>So get badging, and show off your PGXN distributions GitHub and elsewhere!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/53477365497</id><title type="html">How To, Example Makefile Updated</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2013/howto-updated/"/><updated>2026-10-07T16:13:48Z</updated><published>2013-06-21T00:28:13Z</published><author><name>David E. Wheeler</name></author><summary type="html"><![CDATA[I deployed a new version of <a href="https://manager.pgxn.org/">Manager</a> last week, and it included a major
update to the <a href="https://manager.pgxn.org/howto">How To</a>. A lot of the changes are narrative: I tried to keep
things shorter and more to-the-point. But from a technical point of view,
perhaps the most important improvements are in the <code>Makefile</code> example. Thanks
to ongoing work by <a href="https://www.linkedin.com/in/cedricvillemain">Cédric Villemain</a>, it now tries harder to stay out of your
way, while integrating better with PostgreSQL&rsquo;s own <code>make</code> targets.]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I deployed a new version of <a href="https://manager.pgxn.org/">Manager</a> last week, and it included a major
update to the <a href="https://manager.pgxn.org/howto">How To</a>. A lot of the changes are narrative: I tried to keep
things shorter and more to-the-point. But from a technical point of view,
perhaps the most important improvements are in the <code>Makefile</code> example. Thanks
to ongoing work by <a href="https://www.linkedin.com/in/cedricvillemain">Cédric Villemain</a>, it now tries harder to stay out of your
way, while integrating better with PostgreSQL&rsquo;s own <code>make</code> targets.</p>
<p>The main changes:</p>
<ul>
<li>The setting of <code>$EXTENSION</code> and <code>$EXTVERSION</code> is now done by reading the
<code>META.JSON</code> file.</li>
<li>Thanks to the <code>?=</code> operator, <code>$PG_CONFIG</code> can now be passed as a param as
well as an environment variable. That is, you can do
<code>PG_CONFIG=/my/pg_config make</code> or <code>make PG_CONFIG=/my/pg_config</code>.</li>
<li>New targets are defined only after PGXS is loaded, so that targets can be
overridden if necessary.</li>
<li>A new <code>dist</code> target creates a PGXN-ready zip file, assuming that your code
is maintained in Git.</li>
</ul>
<p>Have a look at the <a href="https://github.com/theory/pg-semver/compare/v0.3.0%E2%80%A6v0.4.0#diff-3">resulting diff for semver</a> to get an idea how you might
want to update your own <code>Makefile</code>s.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/53377271882</id><title type="html">PGXN Distribution Prerelease Versions Fixed</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2013/semver-fix/"/><updated>2026-10-07T16:13:48Z</updated><published>2013-06-19T19:24:10Z</published><author><name>David E. Wheeler</name></author><summary type="html"><![CDATA[A couple years ago, the <a href="https://semver.org/">Semantic Version</a> specification was <a href="https://github.com/mojombo/semver/commit/05a00df02db3d4ba83e0caaff6b31c10c77f7d3d">updated</a> to
require a hyphen between the &ldquo;patch version&rdquo; and the &ldquo;prerelease version.&rdquo;
This despite the fact that v1.0.0 of the spec had been published on the site
for a while (see all the gory issues <a href="https://github.com/mojombo/semver/issues/49#issuecomment-19705479">here</a>). Naturally, I had already
implemented that format for PGXN in a <a href="https://metacpan.org/module/SemVer">Perl module</a> and <a href="https://pgxn.org/extension/semver/">Postgres data type</a>.
Since we already had some PGXN extensions with the non-hyphenated format, and
I knew supporting the hyphen would break them, I held off updating those
implementations for a year or so.]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>A couple years ago, the <a href="https://semver.org/">Semantic Version</a> specification was <a href="https://github.com/mojombo/semver/commit/05a00df02db3d4ba83e0caaff6b31c10c77f7d3d">updated</a> to
require a hyphen between the &ldquo;patch version&rdquo; and the &ldquo;prerelease version.&rdquo;
This despite the fact that v1.0.0 of the spec had been published on the site
for a while (see all the gory issues <a href="https://github.com/mojombo/semver/issues/49#issuecomment-19705479">here</a>). Naturally, I had already
implemented that format for PGXN in a <a href="https://metacpan.org/module/SemVer">Perl module</a> and <a href="https://pgxn.org/extension/semver/">Postgres data type</a>.
Since we already had some PGXN extensions with the non-hyphenated format, and
I knew supporting the hyphen would break them, I held off updating those
implementations for a year or so.</p>
<p>I finally broke down and updated them, though, to be fully compliant with the
official v1.0.0 spec, and pushed the changes to the PGXN server a few months
ago. And I was right: it <em>did</em> break things. Links broke, downloads failed,
and my mailbox filled up with error messages. I triaged the worst of the
issues, but since then, accessing PGXN distributions with prerelease versions
has not worked so well.</p>
<p>Until last night. I finally got some tuits in the last month to dig into this
issue and figure out how to fix it. As a result, there are new releases of
<a href="https://manager.pgxn.org/">PGXN Manager</a>, <a href="https://api.pgxn.org/">the API</a>, and <a href="https://pgxn.org/">the site</a>, as well as <a href="https://metacpan.org/module/PGXN::Meta::Validator">the <code>META.json</code>
validator</a> that should keep things consistent and working well going forward.
Assuming there are no more backwards-incompatible changes to semantic
versions, of course.</p>
<p>But that still didn&rsquo;t solve the problem of existing distributions with the old
format of semantic version. Alas, I had to download them all, modify them, and
re-index them. This means that, if you had URLs to prerelease versions laying
around, they won&rsquo;t work anymore. Fortunately, they&rsquo;re prerelease versions, so
I wouldn&rsquo;t expect many to have them lying around anyway.</p>
<p>Still in the interest of disclosure (and because their SHA1s have changed),
here&rsquo;s a list of the changed distributions:</p>
<ul>
<li>pgmp 1.0.0-b2 changed to <a href="https://pgxn.org/dist/pgmp/1.0.0-b2/">1.0.0-b2</a></li>
<li>pgmp 1.0.0-b3 changed to <a href="https://pgxn.org/dist/pgmp/1.0.0-b3/">1.0.0-b3</a></li>
<li>plv8 1.1.0beta1 changed to <a href="https://pgxn.org/dist/plv8/1.1.0-beta1/">1.1.0-beta1</a></li>
<li>pgvihash 1.0.0dirty changed to <a href="https://pgxn.org/dist/pgvihash/1.0.0-dirty/">1.0.0-dirty</a></li>
<li>madlib 0.3.0alpha1 changed to <a href="https://pgxn.org/dist/madlib/0.3.0-alpha1/">0.3.0-alpha1</a></li>
<li>madlib 0.5.0release1 changed to <a href="https://pgxn.org/dist/madlib/0.5.0-release1/">0.5.0-release1</a></li>
<li>madlib 0.6.0release1 changed to <a href="https://pgxn.org/dist/madlib/0.6.0-release1/">0.6.0-release1</a></li>
<li>multicorn 1.0.0beta1 changed to <a href="https://pgxn.org/dist/multicorn/1.0.0-beta1/">1.0.0-beta1</a></li>
<li>pg_repack 1.1.8alpha1 changed to <a href="https://pgxn.org/dist/pg_repack/1.1.8-alpha1/">1.1.8-alpha1</a></li>
<li>pg_repack 1.1.8beta1 changed to <a href="https://pgxn.org/dist/pg_repack/1.1.8-beta1/">1.1.8-beta1</a></li>
<li>pg_repack 1.1.8beta2 changed to <a href="https://pgxn.org/dist/pg_repack/1.1.8-beta2/">1.1.8-beta2</a></li>
</ul>
<p>Apologies for the dead links, and for taking so long to get this stuff cleaned
up.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/21171605888</id><title type="html">pgxn-utils 0.1.4 is out!</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2012/pgxn-utils-014-is-out/"/><updated>2026-10-07T16:13:48Z</updated><published>2012-04-15T21:44:12Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="pgxn" label="PGXN"/><category scheme="https://blog.pgxn.org/tags" term="utils" label="Utils"/><category scheme="https://blog.pgxn.org/tags" term="automation" label="Automation"/><category scheme="https://blog.pgxn.org/tags" term="release" label="Release"/><summary type="html"><![CDATA[<p><em>by Dickson S. Guedes</em></p>
<p>Hi everybody!</p>
<p>I&rsquo;m proud to tell you that a new version of <a href="https://github.com/guedes/pgxn-utils">PGXN Utils</a> was released with
this new features:</p>
<ul>
<li>Git support</li>
<li>Templates and custom templates</li>
<li><a href="https://pgxnclient.projects.postgresql.org/">PGXN Client</a> integration</li>
</ul>
<h2 id="git-support">Git support</h2>
<p>You can start a new extension with or without version control. By default
<code>pgxn-utils</code> supports <a href="https://git-scm.org">git</a> but it will not create a repository unless you use
<code>--git</code> option in the skeleton task.</p>
<p>You can try:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> pgxn-utils skeleton my_cool_versioned_extension --git
</span></span></code></pre></div><p>When you create a new extension with git support in addition to creating the
skeleton, <code>pgxn-utils</code> will initialize a git repository and create the initial
commit.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p><em>by Dickson S. Guedes</em></p>
<p>Hi everybody!</p>
<p>I&rsquo;m proud to tell you that a new version of <a href="https://github.com/guedes/pgxn-utils">PGXN Utils</a> was released with
this new features:</p>
<ul>
<li>Git support</li>
<li>Templates and custom templates</li>
<li><a href="https://pgxnclient.projects.postgresql.org/">PGXN Client</a> integration</li>
</ul>
<h2 id="git-support">Git support</h2>
<p>You can start a new extension with or without version control. By default
<code>pgxn-utils</code> supports <a href="https://git-scm.org">git</a> but it will not create a repository unless you use
<code>--git</code> option in the skeleton task.</p>
<p>You can try:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> pgxn-utils skeleton my_cool_versioned_extension --git
</span></span></code></pre></div><p>When you create a new extension with git support in addition to creating the
skeleton, <code>pgxn-utils</code> will initialize a git repository and create the initial
commit.</p>
<p>Once you have your extension in a git repository your <code>bundle</code> will use only
the committed files to create the archive, but if your repository is dirty
then <code>pgxn-utils</code> will suggest that you to commit or stash your changes before
bundling.</p>
<p>You must be careful with new files not added to repository, because they will
<strong>not</strong> be archived.</p>
<h2 id="default-templates">Default templates</h2>
<p>There are three default templates: <code>sql</code>, <code>c</code> and <code>fdw</code>. If you call
<code>skeleton</code> without specifying a template, <code>sql</code> is the default. But if your
extension will supply some C modules or you will create a FDW, you can create
the extension calling <code>skeleton</code> with a <code>--template</code> option.</p>
<p>Try:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> pgxn-utils skeleton my_cool_c_extension --template<span class="o">=</span>c
</span></span><span class="line"><span class="cl"><span class="gp">$</span> pgxn-utils skeleton my_cool_fdw_extension --template<span class="o">=</span>fdw
</span></span></code></pre></div><p>The templates contain example code and some links to PostgreSQL documentation
that will try to help you to start coding. SQL and C templates contains some
test examples, and the example code will compile and pass <code>make installcheck</code>.
However, this code is intended to be an example, and you must write your own
tests and code.</p>
<h2 id="custom-templates">Custom templates</h2>
<p>If you don&rsquo;t like the templates provided by <code>pgxn-utils</code> you can create you
own. Just create a directory with at least a <code>META.json</code> or <code>META.json.tt</code>
file and then use your directory as argument to the <code>--template</code> option.</p>
<p>To know how create your own template, see the examples in the <a href="https://github.com/guedes/pgxn-utils/tree/master/lib/pgxn_utils/templates">templates
directory</a>.</p>
<h2 id="pgxn-client-integration">PGXN Client integration</h2>
<p>If you have <a href="https://pgxnclient.projects.postgresql.org/">PGXN client</a> installed you can change the command line from
<code>pgxn-utils some_task</code> to <code>pgxn some_task</code> and this will save you some typing.</p>
<p>See:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> <span class="nb">cd</span> /tmp
</span></span><span class="line"><span class="cl"><span class="gp">$</span> pgxn skeleton --help
</span></span><span class="line"><span class="cl"><span class="go">PGXN Utils version: 0.1.4
</span></span></span><span class="line"><span class="cl"><span class="go">Usage:
</span></span></span><span class="line"><span class="cl"><span class="go">  pgxn skeleton extension_name
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="go">Options:
</span></span></span><span class="line"><span class="cl"><span class="go">      [--git]                            # Initialize a git repository after create the extension
</span></span></span><span class="line"><span class="cl"><span class="go">  -a, [--abstract=ABSTRACT]              # Defines a short description to abstract
</span></span></span><span class="line"><span class="cl"><span class="go">  -p, [--target=TARGET]                  # Define the target directory
</span></span></span><span class="line"><span class="cl"><span class="go">                                          # Default: .
</span></span></span><span class="line"><span class="cl"><span class="go">      [--template=TEMPLATE]              # The template that will be used to create the extension. Expected values are: sql, c, fdw
</span></span></span><span class="line"><span class="cl"><span class="go">                                          # Default: sql
</span></span></span><span class="line"><span class="cl"><span class="go">  -r, [--release-status=RELEASE_STATUS]  # Initial extension&#39;s release status
</span></span></span><span class="line"><span class="cl"><span class="go">  -d, [--description=DESCRIPTION]        # A long text that contains more information about extension
</span></span></span><span class="line"><span class="cl"><span class="go">  -b, [--generated-by=GENERATED_BY]      # Name of extension&#39;s generator
</span></span></span><span class="line"><span class="cl"><span class="go">  -l, [--license=LICENSE]                # The extension license
</span></span></span><span class="line"><span class="cl"><span class="go">  -t, [--tags=one two three]             # Defines extension&#39;s tags
</span></span></span><span class="line"><span class="cl"><span class="go">  -v, [--version=VERSION]                # Initial version
</span></span></span><span class="line"><span class="cl"><span class="go">  -m, [--maintainer=MAINTAINER]          # Maintainer&#39;s name &lt;maintainer@email&gt;
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="go">Creates an extension skeleton in current directory
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="gp">$</span> pgxn skeleton <span class="nb">test</span>
</span></span><span class="line"><span class="cl"><span class="go">      create  test
</span></span></span><span class="line"><span class="cl"><span class="go">      create  test/test.control
</span></span></span><span class="line"><span class="cl"><span class="go">      create  test/.gitignore
</span></span></span><span class="line"><span class="cl"><span class="go">      create  test/.template
</span></span></span><span class="line"><span class="cl"><span class="go">      create  test/META.json
</span></span></span><span class="line"><span class="cl"><span class="go">      create  test/Makefile
</span></span></span><span class="line"><span class="cl"><span class="go">      create  test/README.md
</span></span></span><span class="line"><span class="cl"><span class="go">      create  test/doc/test.md
</span></span></span><span class="line"><span class="cl"><span class="go">      create  test/sql/test.sql
</span></span></span><span class="line"><span class="cl"><span class="go">      create  test/sql/uninstall_test.sql
</span></span></span><span class="line"><span class="cl"><span class="go">      create  test/test/expected/base.out
</span></span></span><span class="line"><span class="cl"><span class="go">      create  test/test/sql/base.sql
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="gp">$</span> <span class="nb">cd</span> test/
</span></span><span class="line"><span class="cl"><span class="gp">$</span> pgxn bundle
</span></span><span class="line"><span class="cl"><span class="go">          run  make distclean from &#34;.&#34;
</span></span></span><span class="line"><span class="cl"><span class="go">      create  /tmp/test-0.0.1.zip
</span></span></span></code></pre></div><p>I hope you enjoy this version. &ldquo;:)</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/20908326485</id><title type="html">Lose USE_PGXS in your Makefiles</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2012/lose-use-pgxs/"/><updated>2026-10-07T16:13:48Z</updated><published>2012-04-11T16:36:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="extension" label="Extension"/><category scheme="https://blog.pgxn.org/tags" term="use_pgxs" label="USE_PGXS"/><category scheme="https://blog.pgxn.org/tags" term="makefile" label="Makefile"/><category scheme="https://blog.pgxn.org/tags" term="make" label="make"/><category scheme="https://blog.pgxn.org/tags" term="pg_config" label="pg_config"/><category scheme="https://blog.pgxn.org/tags" term="contrib" label="contrib"/><summary type="html"><![CDATA[<p>Traditionally, folks have created extensions for PostgreSQL by copying one of
the <a href="https://www.postgresql.org/docs/current/static/contrib.html">contrib modules</a> and hacking it into something new. One of the things
that comes along for the ride is the <code>Makefile</code> (<a href="https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=contrib/isn/Makefile;hb=HEAD">example</a>). As a result,
there are a lot of third-party extensions that use the <code>USE_PGXS</code> variable.</p>
<p>A bit of background. The core contrib extensions generally rely on a relative
path to include the core <code>Makefile</code>s needed to build the extension. Because
they ship with the core distribution, they can generally expect that the core
has already been compiled, the necessary <code>Makefile</code>s have been created, and
that they should be built against them. All the assumptions are that the
extensions should be built against the source tree in which they are
distributed. So there is no need to use <a href="https://www.postgresql.org/docs/current/static/app-pgconfig.html"><code>pg_config</code></a> to find <a href="https://www.postgresql.org/docs/current/static/extend-pgxs.html">PGXS</a>; it
already knows where to find what it needs.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Traditionally, folks have created extensions for PostgreSQL by copying one of
the <a href="https://www.postgresql.org/docs/current/static/contrib.html">contrib modules</a> and hacking it into something new. One of the things
that comes along for the ride is the <code>Makefile</code> (<a href="https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=contrib/isn/Makefile;hb=HEAD">example</a>). As a result,
there are a lot of third-party extensions that use the <code>USE_PGXS</code> variable.</p>
<p>A bit of background. The core contrib extensions generally rely on a relative
path to include the core <code>Makefile</code>s needed to build the extension. Because
they ship with the core distribution, they can generally expect that the core
has already been compiled, the necessary <code>Makefile</code>s have been created, and
that they should be built against them. All the assumptions are that the
extensions should be built against the source tree in which they are
distributed. So there is no need to use <a href="https://www.postgresql.org/docs/current/static/app-pgconfig.html"><code>pg_config</code></a> to find <a href="https://www.postgresql.org/docs/current/static/extend-pgxs.html">PGXS</a>; it
already knows where to find what it needs.</p>
<p>But as extensions, there is still the possibility that one might want to build
them against an existing installation of PostgreSQL, or an older version than
the source with which they&rsquo;re distributed. So the core hackers provided the
<code>USE_PGXS</code> variable so that one can in effect tell <code>make</code>, &ldquo;Don&rsquo;t build
against the local source tree, but find PGXS for some other install and build
against that, instead.&rdquo; It was expected to be exceptional, since most folks
would build against the local source tree, and not a big deal to make anyone
else build it with:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">make <span class="nv">USE_PGXS</span><span class="o">=</span><span class="m">1</span>
</span></span></code></pre></div><p>Today things are different. There is a growing ecosystem of third party
extensions on <a href="https://pgxn.org/">PGXN</a>, <a href="https://pgfoundry.org/">pgFoundry</a>, <a href="https://github.com/">GitHub</a>, and <a href="https://bitbucket.org/">Bitbucket</a>, and obviously
they&rsquo;re not distributed with the PostgreSQL core. For these extensions, there
is no surrounding PostgreSQL source code to automatically include, so they
<em>must</em> use <code>pg_config</code> to find PGXS in order build.</p>
<p>Yet, there are quite a few third-party extensions that nevertheless assume
that they are in the <code>contrib</code> directory of the PostgreSQL source code
distribution, and so still have the <code>USE_PGXS</code> variable. The <a href="https://api.pgxn.org/src/twitter_fdw/twitter_fdw-1.0.0/Makefile">twitter_ftw
1.0.0 <code>Makefile</code></a> is a recent example. Just like core extensions, it has this
code:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="err">ifdef</span> <span class="err">USE_PGXS</span>
</span></span><span class="line"><span class="cl"><span class="nv">PG_CONFIG</span> <span class="o">=</span> pg_config
</span></span><span class="line"><span class="cl"><span class="nv">PGXS</span> <span class="o">:=</span> <span class="k">$(</span>shell <span class="k">$(</span>PG_CONFIG<span class="k">)</span> --pgxs<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="err">include</span> <span class="k">$(</span><span class="nv">PGXS</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="err">else</span>
</span></span><span class="line"><span class="cl"><span class="nv">subdir</span> <span class="o">=</span> contrib/twitter_fdw
</span></span><span class="line"><span class="cl"><span class="nv">top_builddir</span> <span class="o">=</span> ../..
</span></span><span class="line"><span class="cl"><span class="err">include</span> <span class="k">$(</span><span class="nv">top_builddir</span><span class="k">)</span><span class="err">/src/Makefile.global</span>
</span></span><span class="line"><span class="cl"><span class="err">include</span> <span class="k">$(</span><span class="nv">top_srcdir</span><span class="k">)</span><span class="err">/contrib/contrib-global.mk</span>
</span></span><span class="line"><span class="cl"><span class="err">endif</span>
</span></span></code></pre></div><p>Because Hitoshi-san originally copied the <code>Makefile</code> from a core extension, it
still assumes it will be distributed in core by default. And as I said, there
are quite a few third-party extensions that exhibit this pattern.</p>
<p>And now the <a href="https://en.wikipedia.org/wiki/Public_service_announcement">PSA</a>: <strong>Please don&rsquo;t use <code>USE_PGXS</code> in PostgreSQL extension
<code>Makefile</code>s.</strong></p>
<p>Not only is it unnecessary, it makes no sense for third-party extensions. They
should <em>always</em> assume that they need to use <code>pg_config</code> to find PGXS. If you
have an extension <code>Makefile</code> with <code>USE_PGXS</code> like twitter_ftw 1.0.0 did, you
should change it to something like this (as Hitoshi-san did in the
<a href="https://api.pgxn.org/src/twitter_fdw/twitter_fdw-1.0.1/Makefile">twitter_ftw 1.0.1 <code>Makefile</code></a>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="nv">PG_CONFIG</span> <span class="o">=</span> pg_config
</span></span><span class="line"><span class="cl"><span class="nv">PGXS</span> <span class="o">:=</span> <span class="k">$(</span>shell <span class="k">$(</span>PG_CONFIG<span class="k">)</span> --pgxs<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="err">include</span> <span class="k">$(</span><span class="nv">PGXS</span><span class="k">)</span>
</span></span></code></pre></div><p>That&rsquo;s it. I am asking you to <em>make your <code>Makefile</code> simpler.</em></p>
<hr>
<p><em>An Aside</em></p>
<p>There is <em>one</em> situation in which you might need to include the core contrib
<code>Makefile</code>s. And it&rsquo;s pretty unusual. If you need to support PostgreSQL 8.1 or
earlier, <code>pg_config</code> will not be able to tell you where to find PGXS. So users
will have to copy the extension source directory into the PostgreSQL source
<code>contrib/</code> directory and build from there. They will need a way to tell <code>make</code>
<em>not</em> to use PGXS. In this one unusual case, I suggest you add a <code>NO_PGXS</code>
variable. <a href="https://api.pgxn.org/src/pgtap/pgtap-0.90.0/Makefile">pgTAP&rsquo;s <code>Makefile</code></a> provides an example. But honestly, very few
extensions need to support PostgreSQL 8.1 (the oldest release currently
supported by the core hackers is <em>8.3!</em>), so make use of this pattern only if
absolutely necessary.</p>
<p>Otherwise, please don&rsquo;t use <code>USE_PGXS</code>.</p>
<hr>
<p>If you want a complete guide to creating your extension <code>Makefile</code>, have a
look at the <a href="https://manager.pgxn.org/howto">PGXN Howto</a>, which includes some detailed examples that include
support for pre- and post-<a href="https://www.postgresql.org/docs/current/static/sql-createextension.html"><code>CREATE EXTENSION</code></a> support. The <a href="https://www.postgresql.org/docs/current/static/extend-pgxs.html">PGXS</a> docs
contain additional details about all the <code>Makefile</code> variables you can use to
simplify extension configuration and installation. Check &rsquo;em out.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/20596200256</id><title type="html">Only Stable Releases are Indexed</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2012/stable-only-indexed/"/><updated>2026-10-07T16:13:48Z</updated><published>2012-04-06T17:02:19Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="release" label="Release"/><category scheme="https://blog.pgxn.org/tags" term="status" label="Status"/><category scheme="https://blog.pgxn.org/tags" term="release-status" label="Release Status"/><category scheme="https://blog.pgxn.org/tags" term="stable" label="Stable"/><category scheme="https://blog.pgxn.org/tags" term="testing" label="Testing"/><category scheme="https://blog.pgxn.org/tags" term="unstable" label="Unstable"/><category scheme="https://blog.pgxn.org/tags" term="full-text-index" label="Full Text Index"/><category scheme="https://blog.pgxn.org/tags" term="fti" label="FTI"/><category scheme="https://blog.pgxn.org/tags" term="search-results" label="Search Results"/><category scheme="https://blog.pgxn.org/tags" term="adaptive-estimator" label="Adaptive Estimator"/><summary type="html"><![CDATA[I&rsquo;ve had a few reports over the last few months that folks uploaded new
extensions to <a href="https://pgxn.org/">PGXN</a> but they failed to show up in search results. For
example, as of right now, if you <a href="https://pgxn.org/search?q=distinct&amp;in=docs">search for &ldquo;distinct&rdquo;</a>, you get two results,
<a href="https://pgxn.org/dist/omnipitr/doc/internals.html">OmniPITR</a> and <a href="https://pgxn.org/dist/pgtap/doc/pgtap.html">pgTAP</a>. This despite the fact that the recently-released
<a href="https://pgxn.org/dist/adaptive_estimator/">adaptive_estimator</a> extension is <a href="https://pgxn.org/tag/distinct/">tagged &ldquo;distinct&rdquo;</a>. Author <a href="https://pgxn.org/user/tomasv">Tomas Vondra</a>
reported this issue, and it took me a couple of days to realize why it wasn&rsquo;t
showing up.]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;ve had a few reports over the last few months that folks uploaded new
extensions to <a href="https://pgxn.org/">PGXN</a> but they failed to show up in search results. For
example, as of right now, if you <a href="https://pgxn.org/search?q=distinct&amp;in=docs">search for &ldquo;distinct&rdquo;</a>, you get two results,
<a href="https://pgxn.org/dist/omnipitr/doc/internals.html">OmniPITR</a> and <a href="https://pgxn.org/dist/pgtap/doc/pgtap.html">pgTAP</a>. This despite the fact that the recently-released
<a href="https://pgxn.org/dist/adaptive_estimator/">adaptive_estimator</a> extension is <a href="https://pgxn.org/tag/distinct/">tagged &ldquo;distinct&rdquo;</a>. Author <a href="https://pgxn.org/user/tomasv">Tomas Vondra</a>
reported this issue, and it took me a couple of days to realize why it wasn&rsquo;t
showing up.</p>
<p>Here&rsquo;s the reason: Only &ldquo;stable&rdquo; releases are indexed. The <a href="https://pgxn.org/dist/adaptive_estimator/1.0.0/">first</a> and
<a href="https://pgxn.org/dist/adaptive_estimator/1.1.0/">second</a> adaptive_estimator releases both have their release status set to
&ldquo;testing&rdquo;. The PGXN API only indexes stable releases. The idea behind that is
that you want most folks to continue using stable releases while you work on
testing new versions. So when users search the site, only the latest stable
release will appear in search results. Similarly, installing an extension via
the <a href="https://pgxnclient.projects.postgresql.org/">PGXN client</a>, prefers the latest stable release by default. If you want
the most recent, you have to <a href="https://pgxnclient.projects.postgresql.org/usage.html#pgxn-install">specify the <code>--unstable</code> or <code>--testing</code> option</a>.</p>
<p>So, the upshot is, if you want your extension to appear in the full text
search results on the PGXN, site, release a stable version.</p>
<p>That said, I first noticed this issue a while ago, and filed <a href="https://github.com/pgxn/pgxn-api/issues/2">an issue</a> with
the idea that, if an extension is uploaded that is not stable, but there are
no stable versions, then the extension should be indexed, anyway. The idea
here is that, when you first upload it, you don&rsquo;t have any existing users to
keep on a stable release anyway, because there <em>is no stable release.</em> So
perhaps we should go ahead and index it. A non-stable release would only be
omitted from the index if there was an existing stable release.</p>
<p>Thoughts?</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/15710159951</id><title type="html">PGXN Has a New Home</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2012/pgxn-moved/"/><updated>2026-10-07T16:13:48Z</updated><published>2012-01-12T04:51:04Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="server" label="Server"/><category scheme="https://blog.pgxn.org/tags" term="postgresql" label="PostgreSQL"/><category scheme="https://blog.pgxn.org/tags" term="depesz" label="depesz"/><category scheme="https://blog.pgxn.org/tags" term="community" label="Community"/><category scheme="https://blog.pgxn.org/tags" term="migration" label="Migration"/><summary type="html"><![CDATA[<p>Day before yesterday, I finally got all of <a href="https://pgxn.org/">PGXN</a> moved to a new server. I had
been using a small server owned by my company, <a href="https://kineticode.com/">Kineticode</a>, and hosted by
<a href="https://commandprompt.com/">Command Prompt</a>. That was fine for a while, but CMD was needing its rack
space back, and what with my <a href="https://justatheory.com/autobiographical/iovationeering.html">new job</a>, I was shutting down Kineticode, too.
It was time to move PGXN elsewhere.</p>
<p>For a while, I got a lot of support and assistance towards moving PGXN to a
<a href="https://www.postgresql.org/">PostgreSQL</a> community server. <a href="https://pgsnake.blogspot.com/">Dave</a>, <a href="https://blog.hagander.net/">Magnus</a>, and <a href="https://www.kaltenbrunner.cc/blog/">Stefan</a> kindly spun up a
VM for me, and gave me permission to install Perl modules from <a href="https://cpan.org">CPAN</a>,
provided I supply them with a script to report to Nagios when Perl modules
were out of date, which of course I did. This was necessary because I built
PGXN with some pretty recent versions of CPAN modules that are not yet
available in Debian stable. I was looking forward to getting things running
and integrating with the community authentication service.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Day before yesterday, I finally got all of <a href="https://pgxn.org/">PGXN</a> moved to a new server. I had
been using a small server owned by my company, <a href="https://kineticode.com/">Kineticode</a>, and hosted by
<a href="https://commandprompt.com/">Command Prompt</a>. That was fine for a while, but CMD was needing its rack
space back, and what with my <a href="https://justatheory.com/autobiographical/iovationeering.html">new job</a>, I was shutting down Kineticode, too.
It was time to move PGXN elsewhere.</p>
<p>For a while, I got a lot of support and assistance towards moving PGXN to a
<a href="https://www.postgresql.org/">PostgreSQL</a> community server. <a href="https://pgsnake.blogspot.com/">Dave</a>, <a href="https://blog.hagander.net/">Magnus</a>, and <a href="https://www.kaltenbrunner.cc/blog/">Stefan</a> kindly spun up a
VM for me, and gave me permission to install Perl modules from <a href="https://cpan.org">CPAN</a>,
provided I supply them with a script to report to Nagios when Perl modules
were out of date, which of course I did. This was necessary because I built
PGXN with some pretty recent versions of CPAN modules that are not yet
available in Debian stable. I was looking forward to getting things running
and integrating with the community authentication service.</p>
<p>I got the server built, and everything was working reasonably well. Magnus and
I were just working out some issues with the proxy server configuration, and I
was starting to think about how to migrate the data over. But first, I decided
to refactor the Perl module script to use a <a href="https://metacpan.org/module/ExtUtils::Installed">more efficient implementation</a>. I
fired it off and piped its output to the <code>cpan</code> utility to just get everything
updated. Unfortunately, unlike my first implementation, which reported only on
CPAN-installed modules, this version of the script also reported when
Debian-installed modules were out-of-date. And since I have my CPAN build
configuration set up to remove previous installations, I upgraded all those
modules, replacing them with new versions.</p>
<p>Well, this was a major fuckup on my part. Turns out there&rsquo;s no simple way to
restore Debian-distributed versions of the modules without rebuilding the
entire system. Worse, this was exactly the sort of thing the community
sysadmins feared. They have to maintain a <em>lot</em> of servers. So they naturally
prefer that they all be as similar as possible. The new PGXN server had been
<em>mostly</em> similar to what they had before, and Dave and company had been
willing to compromise quite a bit to get PGXN going, but I, unfortunately,
demonstrated how easy it is to ruin the whole thing.</p>
<p>So we decided that a community server isn&rsquo;t the right place for PGXN. At least
not yet. Perhaps in a year or two the Debian distribution will be updated to
have all the prerequisites I need. Better yet, maybe someone create a PGXN
debian distribution! (Volunteers welcomed.) Then I won&rsquo;t have to do anything
special and we can try again (without any <code>sudo</code> privileges for me!). But in
the meantime, I still needed to move things.</p>
<p>Fortunately, <a href="https://depesz.com/">depesz</a> came to the rescue. He has a very nice box hosting his
blog, <a href="https://explain.depesz.com/">explain.depesz.com</a>, and a few other things, and would I like to set
things up there? Depesz used <a href="https://www.perlbrew.pl/">perlbrew</a> to set up a Perl install just for the
PGXN system accounts, meaning I could install any Perl modules I needed
without interfering with the system Perl. And each account has its own
privileges to run the services it needs (<a href="https://manager.pgxn.org/">Manager</a>, <a href="https://api.pgxn.org/">API</a>, <a href="https://pgxn.org/">Site</a>)
without the risk of breaking anything else. A few days after getting access,
we had everything set up and ready to go. I pulled the trigger on Monday, and
it went of without a hitch.</p>
<p>My thanks to depesz for the server and all the assistance, not to mention his
<a href="https://www.pgxn.org/donors/">donation</a>! PGXN now has a very nice home where it can mature.</p>
<p>And as for the future, I have some thoughts about that, too.</p>
<ul>
<li>I&rsquo;d like to blog about the migration itself, and how easy it is (and isn&rsquo;t)
to build PGXN.</li>
<li>There are <a href="https://github.com/pgxn/pgxn-manager/issues">some</a> <a href="https://github.com/pgxn/pgxn-api/issues">bugs</a> to be fixed and <a href="https://github.com/pgxn/pgxn-manager/issues">minor improvements</a> to be
had. Interested in helping out?</li>
<li>I&rsquo;d love to hear your ideas about how to improve PGXN. What would make it
better? What doesn&rsquo;t work quite right for you now?</li>
</ul>
<p>And yes, now that this migration is finally done, I expect I&rsquo;ll have more time
to blog and work on PGXN going forward. Please leave your thoughts and ideas in
the comments. This thing is wide open to any kind of idea, and I would greatly
appreciate your feedback.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/13427961249</id><title type="html">PGXN Client 1.0 Released!</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/pgxn-client-10-released/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-11-28T00:34:34Z</published><author><name>Daniele Varrazzo</name></author><category scheme="https://blog.pgxn.org/tags" term="pgxn-client" label="PGXN Client"/><category scheme="https://blog.pgxn.org/tags" term="release" label="Release"/><summary type="html"><![CDATA[<p>Finally, here it is. Well tested, documented, and pampered. With the <a href="https://pgxnclient.projects.postgresql.org/">PGXN
Client</a> installing extensions from the <a href="https://pgxn.org/">PGXN Network</a> is a breeze:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> pgxn install semver
</span></span><span class="line"><span class="cl"><span class="gp">$</span> pgxn load semver
</span></span><span class="line"><span class="cl"><span class="gp">$</span> psql
</span></span><span class="line"><span class="cl"><span class="go">=# select &#39;foo&#39;::semver;
</span></span></span><span class="line"><span class="cl"><span class="go">ERROR:  bad semver value &#39;foo&#39;: expected number at foo
</span></span></span><span class="line"><span class="cl"><span class="go">LINE 1: select &#39;foo&#39;::semver;
</span></span></span></code></pre></div><p>Error! Meaning: success!</p>
<p>The number of extensions on PGXN is steadily increasing, so we hope the client
will make their adoption even easier.</p>
<p>The client is now extensible: either writing in Python to reuse some of the
other commands implementation or writing new self-contained scripts in any
language to be invoked by the pgxn commands dispatcher. The first client
extensions are already available: <a href="https://github.com/guedes/pgxn-utils">PGXN Utils</a> and <a href="https://github.com/pgxn/pgxn-meta-validator/">PGXN:Meta:Validator</a> are
targeted for easier development of new extensions.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Finally, here it is. Well tested, documented, and pampered. With the <a href="https://pgxnclient.projects.postgresql.org/">PGXN
Client</a> installing extensions from the <a href="https://pgxn.org/">PGXN Network</a> is a breeze:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> pgxn install semver
</span></span><span class="line"><span class="cl"><span class="gp">$</span> pgxn load semver
</span></span><span class="line"><span class="cl"><span class="gp">$</span> psql
</span></span><span class="line"><span class="cl"><span class="go">=# select &#39;foo&#39;::semver;
</span></span></span><span class="line"><span class="cl"><span class="go">ERROR:  bad semver value &#39;foo&#39;: expected number at foo
</span></span></span><span class="line"><span class="cl"><span class="go">LINE 1: select &#39;foo&#39;::semver;
</span></span></span></code></pre></div><p>Error! Meaning: success!</p>
<p>The number of extensions on PGXN is steadily increasing, so we hope the client
will make their adoption even easier.</p>
<p>The client is now extensible: either writing in Python to reuse some of the
other commands implementation or writing new self-contained scripts in any
language to be invoked by the pgxn commands dispatcher. The first client
extensions are already available: <a href="https://github.com/guedes/pgxn-utils">PGXN Utils</a> and <a href="https://github.com/pgxn/pgxn-meta-validator/">PGXN:Meta:Validator</a> are
targeted for easier development of new extensions.</p>
<p>The client is <a href="https://pypi.python.org/pypi/pgxnclient">released on PyPI</a>, so installing is just:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> sudo easy_install pgxnclient
</span></span></code></pre></div><p>Complete documentation and further links are available from the <a href="https://pgxnclient.projects.postgresql.org/">project
homepage</a>.</p>
<p>Any feedback is welcome; you can contact me and all the other people behind
PGXN on the <a href="https://groups.google.com/group/pgxn-users/">PGXN User Group</a>. See you there!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/10166916693</id><title type="html">Postgres Open</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/postgres-open/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-09-13T16:40:24Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="postgres-open" label="Postgres Open"/><category scheme="https://blog.pgxn.org/tags" term="pgtap" label="pgTAP"/><category scheme="https://blog.pgxn.org/tags" term="fundraising" label="Fundraising"/><summary type="html"><![CDATA[<p>I&rsquo;m off to Chicago today for <a href="https://www.postgresopen.org/">Postgres Open</a>, a new PostgreSQL conference. I&rsquo;m
pleased that I&rsquo;ll be presenting &ldquo;<a href="https://postgresopen.org/2011/schedule/presentations/83/">Get Your Preferred Feature Developed!</a>&rdquo;</p>
<p>There are lots of developers out there who, like me, have ideas for projects
they want to work on for PostgreSQL, but don&rsquo;t have the free time to make it
happen. The idea for this talk is to pitch this fact to an audience of
organizations with a major investment in PostgreSQL and an interest in seeing
it improve. Perhaps one or more of them will look into sponsoring development
of something that interests them, or that they need. This way, interested
developers might get <em>paid</em> to work on projects that interest them, to the
benefit of the project, the community, and of course their sponsors.
Naturally, I&rsquo;ll be drawing on PGXN as an example of how this sort of thing can
work.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;m off to Chicago today for <a href="https://www.postgresopen.org/">Postgres Open</a>, a new PostgreSQL conference. I&rsquo;m
pleased that I&rsquo;ll be presenting &ldquo;<a href="https://postgresopen.org/2011/schedule/presentations/83/">Get Your Preferred Feature Developed!</a>&rdquo;</p>
<p>There are lots of developers out there who, like me, have ideas for projects
they want to work on for PostgreSQL, but don&rsquo;t have the free time to make it
happen. The idea for this talk is to pitch this fact to an audience of
organizations with a major investment in PostgreSQL and an interest in seeing
it improve. Perhaps one or more of them will look into sponsoring development
of something that interests them, or that they need. This way, interested
developers might get <em>paid</em> to work on projects that interest them, to the
benefit of the project, the community, and of course their sponsors.
Naturally, I&rsquo;ll be drawing on PGXN as an example of how this sort of thing can
work.</p>
<p>If you&rsquo;e like to learn more, tune in! The whole conference will be
live-streamed. Check <a href="https://www.postgresopen.org/" title="Postgres Open">the site</a> on Wednesday to get hooked up.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/9950473714</id><title type="html">PGXN Utils 0.1.3 Released!</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/pgxn-utils-013-released/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-09-08T06:39:00Z</published><author><name>Dickson S. Guedes</name></author><category scheme="https://blog.pgxn.org/tags" term="pgxn-utils" label="PGXN Utils"/><category scheme="https://blog.pgxn.org/tags" term="development" label="Development"/><category scheme="https://blog.pgxn.org/tags" term="utils" label="Utils"/><category scheme="https://blog.pgxn.org/tags" term="build" label="Build"/><category scheme="https://blog.pgxn.org/tags" term="bundle-extension" label="Bundle Extension"/><category scheme="https://blog.pgxn.org/tags" term="meta" label="Meta"/><summary type="html"><![CDATA[<p>Hello everyone!</p>
<p>I&rsquo;m proud to tell you that a new version of <a href="https://github.com/guedes/pgxn-utils">pgxn_utils</a> was released!</p>
<p>In this version some errors with OpenSSL was fixed (thanks @theory to report
then), and now you can release an extension to <a href="https://pgxn.org">PGXN</a> <a href="https://blog.pgxn.org/post/6883009649/pgxn-utils-0-1-2-released">in <strong>five steps</strong></a>
even using Ruby 1.8!</p>
<p>Another change was the executable&rsquo;s name that changed from <code>pgxn_utils</code> to
<code>pgxn-utils</code> for a close integration with next version of <a href="https://pgxnclient.projects.postgresql.org">PGXN Client</a>, but
some work need to be done, yet.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Hello everyone!</p>
<p>I&rsquo;m proud to tell you that a new version of <a href="https://github.com/guedes/pgxn-utils">pgxn_utils</a> was released!</p>
<p>In this version some errors with OpenSSL was fixed (thanks @theory to report
then), and now you can release an extension to <a href="https://pgxn.org">PGXN</a> <a href="https://blog.pgxn.org/post/6883009649/pgxn-utils-0-1-2-released">in <strong>five steps</strong></a>
even using Ruby 1.8!</p>
<p>Another change was the executable&rsquo;s name that changed from <code>pgxn_utils</code> to
<code>pgxn-utils</code> for a close integration with next version of <a href="https://pgxnclient.projects.postgresql.org">PGXN Client</a>, but
some work need to be done, yet.</p>
<p>I used <code>pgxn-utils</code> to release itself to <a href="https://pgxn.org/dist/pgxn_utils/">PGXN</a>!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> pgxn-utils release pgxn_utils-0.1.3.zip 
</span></span><span class="line"><span class="cl"><span class="go">Enter your PGXN username: guedes
</span></span></span><span class="line"><span class="cl"><span class="go">Enter your PGXN password: ***********************
</span></span></span><span class="line"><span class="cl"><span class="go">Trying to release pgxn_utils-0.1.3.zip ... released successfully!
</span></span></span><span class="line"><span class="cl"><span class="go">Visit: https://pgxn.org/dist/pgxn_utils/0.1.3/
</span></span></span></code></pre></div><p>Cool, eh? So, since the PGXN&rsquo;s mirrors were synced and you have <code>pgxn</code> client,
you could install <code>pgxn_utils</code> using:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">pgxn install pgxn_utils
</span></span></code></pre></div><p>If you don&rsquo;t have <code>pgxn</code> client you can install it using rubygems</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gem install pgxn_utils
</span></span></code></pre></div><p>Have fun!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/8742312488</id><title type="html">My presentation at the 2012 PDXPUG PGDay</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/video-building-and-distributing-extensions-without-c/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-08-10T18:58:09Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="pgxn" label="PGXN"/><category scheme="https://blog.pgxn.org/tags" term="pgday" label="PgDay"/><category scheme="https://blog.pgxn.org/tags" term="oscon" label="OSCON"/><category scheme="https://blog.pgxn.org/tags" term="c" label="C"/><category scheme="https://blog.pgxn.org/tags" term="extensions" label="Extensions"/><category scheme="https://blog.pgxn.org/tags" term="video" label="Video"/><summary type="html">&lt;figure>
&lt;figcaption>
My presentation at the 2012 PDXPUG PGDay. It covers the basics of how to
create useful extensions to PostgreSQL and distribute them on PGXN---without
needing to learn C.
&lt;/figcaption>
&lt;/figure></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve">&lt;figure>
&lt;figcaption>
My presentation at the 2012 PDXPUG PGDay. It covers the basics of how to
create useful extensions to PostgreSQL and distribute them on PGXN---without
needing to learn C.
&lt;/figcaption>
&lt;/figure>
</content></entry><entry><id>https://blog.pgxn.org/post/6883009649</id><title type="html">PGXN Utils 0.1.2 Released!</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/pgxn-utils-012-released/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-06-24T23:53:00Z</published><author><name>Dickson S. Guedes</name></author><category scheme="https://blog.pgxn.org/tags" term="build" label="Build"/><category scheme="https://blog.pgxn.org/tags" term="create-extension" label="Create Extension"/><category scheme="https://blog.pgxn.org/tags" term="utils" label="Utils"/><category scheme="https://blog.pgxn.org/tags" term="meta" label="Meta"/><category scheme="https://blog.pgxn.org/tags" term="release" label="Release"/><category scheme="https://blog.pgxn.org/tags" term="bundle" label="Bundle"/><summary type="html"><![CDATA[<p>Hello everyone!</p>
<p>I&rsquo;m proud to tell you that a new version of <a href="https://github.com/guedes/pgxn-utils">pgxn_utils</a> was released!</p>
<p>Now you can release a distribution to <a href="https://pgxn.org">PGXN</a> in <strong>five steps</strong>!</p>
<p><strong>First</strong>, install it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gem install pgxn_utils
</span></span></code></pre></div><p><strong>Second</strong>, create your extension, optionally overwrite some defaults:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">mkdir <span class="nv">$HOME</span>/extensions
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> <span class="nv">$HOME</span>/extensions
</span></span><span class="line"><span class="cl">pgxn_utils skeleton my_extension --maintainer <span class="s2">&#34;Dickson S. Guedes&#34;</span>
</span></span></code></pre></div><p><strong>Third</strong>, code!</p>
<p><strong>Fourth</strong>, bundle it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">pgxn_utils bundle my_extension
</span></span></span><span class="line"><span class="cl"><span class="go">Extension generated at: /home/guedes/extensions/my_extension-0.0.1.zip
</span></span></span></code></pre></div><p><strong>Fifth</strong>, release it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">pgxn_utils release my_extension-0.0.1.zip
</span></span></span><span class="line"><span class="cl"><span class="go">Enter your PGXN username: guedes
</span></span></span><span class="line"><span class="cl"><span class="go">Enter your PGXN password: ******
</span></span></span><span class="line"><span class="cl"><span class="go">Trying to release my_cool_extension-0.0.1.zip ... released successfully!
</span></span></span><span class="line"><span class="cl"><span class="go">Visit: https://manager.pgxn.org/distributions/my_cool_extension/0.0.1
</span></span></span></code></pre></div><p>Ah, you can export <code>PGXN_USER</code> and <code>PGXN_PASSWORD</code> if you are tired to type
your username and password every time.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Hello everyone!</p>
<p>I&rsquo;m proud to tell you that a new version of <a href="https://github.com/guedes/pgxn-utils">pgxn_utils</a> was released!</p>
<p>Now you can release a distribution to <a href="https://pgxn.org">PGXN</a> in <strong>five steps</strong>!</p>
<p><strong>First</strong>, install it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gem install pgxn_utils
</span></span></code></pre></div><p><strong>Second</strong>, create your extension, optionally overwrite some defaults:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">mkdir <span class="nv">$HOME</span>/extensions
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> <span class="nv">$HOME</span>/extensions
</span></span><span class="line"><span class="cl">pgxn_utils skeleton my_extension --maintainer <span class="s2">&#34;Dickson S. Guedes&#34;</span>
</span></span></code></pre></div><p><strong>Third</strong>, code!</p>
<p><strong>Fourth</strong>, bundle it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">pgxn_utils bundle my_extension
</span></span></span><span class="line"><span class="cl"><span class="go">Extension generated at: /home/guedes/extensions/my_extension-0.0.1.zip
</span></span></span></code></pre></div><p><strong>Fifth</strong>, release it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">pgxn_utils release my_extension-0.0.1.zip
</span></span></span><span class="line"><span class="cl"><span class="go">Enter your PGXN username: guedes
</span></span></span><span class="line"><span class="cl"><span class="go">Enter your PGXN password: ******
</span></span></span><span class="line"><span class="cl"><span class="go">Trying to release my_cool_extension-0.0.1.zip ... released successfully!
</span></span></span><span class="line"><span class="cl"><span class="go">Visit: https://manager.pgxn.org/distributions/my_cool_extension/0.0.1
</span></span></span></code></pre></div><p>Ah, you can export <code>PGXN_USER</code> and <code>PGXN_PASSWORD</code> if you are tired to type
your username and password every time.</p>
<p><a href="https://github.com/guedes/pgxn-utils">Check this out!</a>.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/5758832725</id><title type="html">PGXN Utils 0.1.1 Released!</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/pgxn-utils-011-released/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-05-23T04:53:30Z</published><author><name>Dickson S. Guedes</name></author><category scheme="https://blog.pgxn.org/tags" term="build" label="Build"/><category scheme="https://blog.pgxn.org/tags" term="create-extension" label="Create Extension"/><category scheme="https://blog.pgxn.org/tags" term="utils" label="Utils"/><category scheme="https://blog.pgxn.org/tags" term="bundle-extension" label="Bundle Extension"/><category scheme="https://blog.pgxn.org/tags" term="meta" label="Meta"/><category scheme="https://blog.pgxn.org/tags" term="readme" label="README"/><category scheme="https://blog.pgxn.org/tags" term="skeleton" label="Skeleton"/><summary type="html"><![CDATA[<p>Hello everyone!</p>
<p>This was a productive weekend that allowed me to work on some new features in
<a href="https://github.com/guedes/pgxn-utils">pgxn_utils</a> and I&rsquo;m proud to tell you that a new version was released!</p>
<p>Trying to simplify your extension-development life I&rsquo;ve added two tasks to
<code>pgxn_utils</code>: <code>change</code> and <code>bundle</code>. The first one is just a convenient way to
change META information about extension, incrementally, the second one is an
easy way to archive your extension in a zip file well named.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Hello everyone!</p>
<p>This was a productive weekend that allowed me to work on some new features in
<a href="https://github.com/guedes/pgxn-utils">pgxn_utils</a> and I&rsquo;m proud to tell you that a new version was released!</p>
<p>Trying to simplify your extension-development life I&rsquo;ve added two tasks to
<code>pgxn_utils</code>: <code>change</code> and <code>bundle</code>. The first one is just a convenient way to
change META information about extension, incrementally, the second one is an
easy way to archive your extension in a zip file well named.</p>
<p>To install it just type:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gem install pgxn_utils
</span></span></code></pre></div><p>Or, if you don&rsquo;t want to install it yet, see it in action on this <a href="https://blip.tv/pgcasts/pgxn_utils-0-1-1-released-5194610">screencast</a>
3:05.</p>
<p><strong>Work in progress&hellip;</strong></p>
<p>I&rsquo;m working now to simplify the release, creating a task to send bundled file
to <a href="https://pgxn.org">PGXN</a>.</p>
<p>There are a lot of work to do yet so, please, <a href="https://github.com/guedes/pgxn-utils/issues">tell me</a> if you found a bug or
have suggestions.</p>
<p>Have a nice code! &ldquo;:)</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/5465631144</id><title type="html">PGXN Utils</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/pgxn-utils/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-05-14T01:05:00Z</published><author><name>Dickson S. Guedes</name></author><category scheme="https://blog.pgxn.org/tags" term="build" label="Build"/><category scheme="https://blog.pgxn.org/tags" term="makefile" label="Makefile"/><category scheme="https://blog.pgxn.org/tags" term="control-file" label="Control File"/><category scheme="https://blog.pgxn.org/tags" term="readme" label="README"/><category scheme="https://blog.pgxn.org/tags" term="meta" label="Meta"/><category scheme="https://blog.pgxn.org/tags" term="create-extension" label="Create Extension"/><category scheme="https://blog.pgxn.org/tags" term="skeleton" label="Skeleton"/><category scheme="https://blog.pgxn.org/tags" term="generator" label="Generator"/><summary type="html"><![CDATA[<p>Do you ever have problems with copy and paste? I often did, and that is why I
create custom templates for often used files that match certain patterns.</p>
<p>With files from the structure of PostgreSQL&rsquo;s extensions was the same thing.</p>
<p>I was tired of creating the files and edit the META, controlfile, READMEs,
etc. every time I start a new extension and felt that I need something that
made me more productive, so I decided to create an automatic generator and
share it with the world.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Do you ever have problems with copy and paste? I often did, and that is why I
create custom templates for often used files that match certain patterns.</p>
<p>With files from the structure of PostgreSQL&rsquo;s extensions was the same thing.</p>
<p>I was tired of creating the files and edit the META, controlfile, READMEs,
etc. every time I start a new extension and felt that I need something that
made me more productive, so I decided to create an automatic generator and
share it with the world.</p>
<p>I called it <a href="https://github.com/guedes/pgxn-utils/">pgxn-utils</a>, and you should give it a try: it is easy to install,
easy to use and will help you to start hacking quickly!</p>
<p><strong>How?</strong></p>
<ol>
<li>
<p>First install it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">gem install pgxn_utils
</span></span></code></pre></div></li>
<li>
<p>Then start a new extension:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">pgxn_utils skeleton my_cool_extension
</span></span></code></pre></div></li>
</ol>
<p>Thats all! It will create the initial skeleton for you and you can start
coding! But, if you don&rsquo;t want to install it, <a href="https://pgcasts.com/media/pgxn_utils-usage-example.mpeg">see it in action</a></p>
<p>Good hack!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/5458118596</id><title type="html">New HOWTO</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/new-howto/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-05-13T20:34:46Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="howto" label="HOWTO"/><category scheme="https://blog.pgxn.org/tags" term="build" label="Build"/><category scheme="https://blog.pgxn.org/tags" term="makefile" label="Makefile"/><category scheme="https://blog.pgxn.org/tags" term="readme" label="README"/><category scheme="https://blog.pgxn.org/tags" term="changes" label="Changes"/><category scheme="https://blog.pgxn.org/tags" term="documentation" label="Documentation"/><category scheme="https://blog.pgxn.org/tags" term="meta.json" label="META.json"/><category scheme="https://blog.pgxn.org/tags" term="control-file" label="Control File"/><summary type="html"><![CDATA[<p>I updated the <a href="https://manager.pgxn.org/">howto</a> yesterday. This document explains how to create a PGXN
distribution. If you&rsquo;re interested in releasing PostgreSQL extensions on
<a href="https://pgxn.org/">PGXN</a>, this document is worth a read.</p>
<p>In essence, it&rsquo;s really simple: Just create a <a href="https://pgxn.org/spec/"><code>META.json</code></a> and upload. But to
get the full benefit, there are quite a few other recommendations. Already
familiar with it? Here&rsquo;s the checklist:</p>
<ul>
<li>Create a <a href="https://pgxn.org/spec/"><code>META.json</code></a></li>
<li>Create a <a href="https://www.postgresql.org/docs/9.1/static/extend-extensions.html">control file</a></li>
<li>Create a <a href="https://www.postgresql.org/docs/current/static/xfunc-c.html#XFUNC-C-PGXS"><code>Makefile</code></a></li>
<li>Implement the code in the <code>sql</code> and <code>src</code> directories</li>
<li>Write tests in the <code>test</code> directory</li>
<li>Write a <a href="https://search.cpan.org/perldoc?Text::Markup">Text::Markup</a>-recognizable <code>README</code></li>
<li>Write <a href="https://search.cpan.org/perldoc?Text::Markup">Text::Markup</a>-recognizable documentation in the <code>doc</code> directory</li>
<li>Consider including other files: <code>Changes</code>, <code>LICENSE</code>, <code>INSTALL</code>, <code>COPYING</code>,
<code>AUTHORS</code></li>
<li><a href="https://manager.pgxn.org/">Release it</a>!</li>
</ul>
<p>Be sure to read the <a href="https://manager.pgxn.org/">howto</a> for details. Got feedback or suggestions? Leave a
comment!</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I updated the <a href="https://manager.pgxn.org/">howto</a> yesterday. This document explains how to create a PGXN
distribution. If you&rsquo;re interested in releasing PostgreSQL extensions on
<a href="https://pgxn.org/">PGXN</a>, this document is worth a read.</p>
<p>In essence, it&rsquo;s really simple: Just create a <a href="https://pgxn.org/spec/"><code>META.json</code></a> and upload. But to
get the full benefit, there are quite a few other recommendations. Already
familiar with it? Here&rsquo;s the checklist:</p>
<ul>
<li>Create a <a href="https://pgxn.org/spec/"><code>META.json</code></a></li>
<li>Create a <a href="https://www.postgresql.org/docs/9.1/static/extend-extensions.html">control file</a></li>
<li>Create a <a href="https://www.postgresql.org/docs/current/static/xfunc-c.html#XFUNC-C-PGXS"><code>Makefile</code></a></li>
<li>Implement the code in the <code>sql</code> and <code>src</code> directories</li>
<li>Write tests in the <code>test</code> directory</li>
<li>Write a <a href="https://search.cpan.org/perldoc?Text::Markup">Text::Markup</a>-recognizable <code>README</code></li>
<li>Write <a href="https://search.cpan.org/perldoc?Text::Markup">Text::Markup</a>-recognizable documentation in the <code>doc</code> directory</li>
<li>Consider including other files: <code>Changes</code>, <code>LICENSE</code>, <code>INSTALL</code>, <code>COPYING</code>,
<code>AUTHORS</code></li>
<li><a href="https://manager.pgxn.org/">Release it</a>!</li>
</ul>
<p>Be sure to read the <a href="https://manager.pgxn.org/">howto</a> for details. Got feedback or suggestions? Leave a
comment!</p>
<p>Oh, and check out <a href="https://github.com/guedes/pgxn-utils/">pgxn-utils</a> and simplify your extension-development life.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/5441748625</id><title type="html">Using JSONP Callbacks</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/jsonp-callbacks/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-05-13T03:58:33Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="jsonp" label="JSONP"/><category scheme="https://blog.pgxn.org/tags" term="callback" label="Callback"/><category scheme="https://blog.pgxn.org/tags" term="blog" label="Blog"/><category scheme="https://blog.pgxn.org/tags" term="json" label="Json"/><summary type="html"><![CDATA[<p>Using the new <a href="https://github.com/pgxn/pgxn-api/wiki/JSONP">JSONP callback support</a> in the PGXN API, I just added a sidebar
to <a href="https://www.justatheory.com/" title="Just a Theory">my personal blog</a> that shows a list of all my PGXN distributions. The code
is simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;links&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">h2</span><span class="p">&gt;</span>PGXN Code<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">function</span> <span class="nx">pgxn_distros</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="s1">&#39;&lt;dl&gt;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="nx">dist</span> <span class="k">in</span> <span class="nx">data</span><span class="p">.</span><span class="nx">releases</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s1">&#39;&lt;dt&gt;&lt;a href=https://pgxn.org/dist/&#39;</span> <span class="o">+</span> <span class="nx">dist</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                <span class="s1">&#39;&gt;&#39;</span> <span class="o">+</span> <span class="nx">dist</span> <span class="o">+</span> <span class="s1">&#39;&lt;/a&gt;&lt;/dt&gt;&#39;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                <span class="s1">&#39;&lt;dd&gt;&#39;</span> <span class="o">+</span> <span class="nx">data</span><span class="p">.</span><span class="nx">releases</span><span class="p">[</span><span class="nx">dist</span><span class="p">].</span><span class="kr">abstract</span> <span class="o">+</span> <span class="s1">&#39;&lt;/dd&gt;&#39;</span>
</span></span><span class="line"><span class="cl">            <span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="s1">&#39;&lt;/dl&gt;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://api.pgxn.org/user/theory.json?callback=pgxn_distros&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>And the output looks like this:</p>
<p><img src="/2011/jsonp-callbacks/pgxn-code.png" alt="PGXN Code"></p>
<p>Not bad, eh? AS you can see, JSONP is dead easy to use with the JSON files
served by the JSON API. Try that, CPAN!</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Using the new <a href="https://github.com/pgxn/pgxn-api/wiki/JSONP">JSONP callback support</a> in the PGXN API, I just added a sidebar
to <a href="https://www.justatheory.com/" title="Just a Theory">my personal blog</a> that shows a list of all my PGXN distributions. The code
is simple:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;links&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">h2</span><span class="p">&gt;</span>PGXN Code<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">function</span> <span class="nx">pgxn_distros</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="s1">&#39;&lt;dl&gt;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="nx">dist</span> <span class="k">in</span> <span class="nx">data</span><span class="p">.</span><span class="nx">releases</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="s1">&#39;&lt;dt&gt;&lt;a href=https://pgxn.org/dist/&#39;</span> <span class="o">+</span> <span class="nx">dist</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                <span class="s1">&#39;&gt;&#39;</span> <span class="o">+</span> <span class="nx">dist</span> <span class="o">+</span> <span class="s1">&#39;&lt;/a&gt;&lt;/dt&gt;&#39;</span> <span class="o">+</span>
</span></span><span class="line"><span class="cl">                <span class="s1">&#39;&lt;dd&gt;&#39;</span> <span class="o">+</span> <span class="nx">data</span><span class="p">.</span><span class="nx">releases</span><span class="p">[</span><span class="nx">dist</span><span class="p">].</span><span class="kr">abstract</span> <span class="o">+</span> <span class="s1">&#39;&lt;/dd&gt;&#39;</span>
</span></span><span class="line"><span class="cl">            <span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="s1">&#39;&lt;/dl&gt;&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;text/javascript&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://api.pgxn.org/user/theory.json?callback=pgxn_distros&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>And the output looks like this:</p>
<p><img src="/2011/jsonp-callbacks/pgxn-code.png" alt="PGXN Code"></p>
<p>Not bad, eh? AS you can see, JSONP is dead easy to use with the JSON files
served by the JSON API. Try that, CPAN!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/5197276864</id><title type="html">Distribution Name Constraint</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/distribution-name-constraint/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-05-04T20:42:37Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="distribution-name" label="Distribution Name"/><category scheme="https://blog.pgxn.org/tags" term="constraint" label="Constraint"/><category scheme="https://blog.pgxn.org/tags" term="domain" label="Domain"/><category scheme="https://blog.pgxn.org/tags" term="ascii" label="ASCII"/><category scheme="https://blog.pgxn.org/tags" term="file-name" label="File Name"/><summary type="html"><![CDATA[<p>Following <a href="https://groups.google.com/group/pgxn-users/browse_thread/thread/4528b2a02e8886ff">a pgxn-users discussion</a>, I&rsquo;m looking at adding a constraint on the
names distributions can have. I&rsquo;m thinking this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">DOMAIN</span><span class="w"> </span><span class="n">namespace</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">CITEXT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">CHECK</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">VALUE</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="s1">&#39;^[-a-z0-9_]{2,}$&#39;</span><span class="w"> </span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>So, just ASCII numbers and letters, dash, and underscore, with a minimum
length of two characters. Maybe <code>,</code> and <code>+</code> should be allowed, too? What do
you think? What constraints do you expect on distribution download file names?</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Following <a href="https://groups.google.com/group/pgxn-users/browse_thread/thread/4528b2a02e8886ff">a pgxn-users discussion</a>, I&rsquo;m looking at adding a constraint on the
names distributions can have. I&rsquo;m thinking this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">DOMAIN</span><span class="w"> </span><span class="n">namespace</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="n">CITEXT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">CHECK</span><span class="w"> </span><span class="p">(</span><span class="w"> </span><span class="n">VALUE</span><span class="w"> </span><span class="o">~</span><span class="w"> </span><span class="s1">&#39;^[-a-z0-9_]{2,}$&#39;</span><span class="w"> </span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>So, just ASCII numbers and letters, dash, and underscore, with a minimum
length of two characters. Maybe <code>,</code> and <code>+</code> should be allowed, too? What do
you think? What constraints do you expect on distribution download file names?</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/5118152273</id><title type="html">New release for the PGXN client</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/new-release-for-the-pgxn-client/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-05-02T00:59:37Z</published><author><name>Daniele Varrazzo</name></author><category scheme="https://blog.pgxn.org/tags" term="pgxn" label="PGXN"/><category scheme="https://blog.pgxn.org/tags" term="client" label="Client"/><category scheme="https://blog.pgxn.org/tags" term="release" label="Release"/><category scheme="https://blog.pgxn.org/tags" term="uninstall" label="Uninstall"/><category scheme="https://blog.pgxn.org/tags" term="drop" label="Drop"/><category scheme="https://blog.pgxn.org/tags" term="sudo" label="sudo"/><summary type="html"><![CDATA[<p>During the last days I&rsquo;ve done some lightweight hacking on the PGXN client,
and I&rsquo;ve just released the last package on PyPI, with the still very shy
version number of 0.1a4.</p>
<p>First change: the package name. The package is now called <code>pgxnclient</code>,
without the point. As <a href="https://blog.pgxn.org/post/5026314153/writing-a-client-for-pgxn#dsq-comments">Marti has commented</a>, the point in the name could
create problems to some distributions which may have needed to munge it into a
shape fitting their naming constraints. So, in order to install the last
release you will need the command:</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>During the last days I&rsquo;ve done some lightweight hacking on the PGXN client,
and I&rsquo;ve just released the last package on PyPI, with the still very shy
version number of 0.1a4.</p>
<p>First change: the package name. The package is now called <code>pgxnclient</code>,
without the point. As <a href="https://blog.pgxn.org/post/5026314153/writing-a-client-for-pgxn#dsq-comments">Marti has commented</a>, the point in the name could
create problems to some distributions which may have needed to munge it into a
shape fitting their naming constraints. So, in order to install the last
release you will need the command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> easy_install pgxnclient
</span></span></code></pre></div><p>The name of the entry point script is still <code>pgxn</code> and it shouldn&rsquo;t change.</p>
<p>One of the new features in this release is the addition of commands <code>drop</code>, to
remove the the extensions of a distribution from a database (opposite of the
<code>load</code> command) and <code>uninstall</code>, to remove installed files from the system
(opposite of <code>install</code>).</p>
<p>Another change is the ability to work on a local zip or directory instead of
using the API to get metadata and package from PGXN. The main goal of this
feature is to help the packagers to test their packages with the client before
submission, verifying that the data uploaded can be used in an automatized
way.</p>
<p>A third change is in the <code>install</code> command: it previously required to be run
as root if, as likely, the PostgreSQL directories are system ones. I was
feeling this need slightly scary as download and build phases would have been
run as root too, so now <code>sudo</code> is invoked by the script only when installing,
with command line options allowing its customization.</p>
<p>What I feel mostly missing now is documentation: I know that adding it will
force a big review of the code, adding missing comments, add tests to enforce
what documented&hellip; so it&rsquo;s something I will start tomorrow. Once documentation
and code have converged there will also be a more sound basis to discuss about
the client features. And dare version 0.2, maybe :)</p>
<p>Thanks everybody for the feedback. If you want to have a chat about the client
or PGXN in general, the best place is <a href="https://groups.google.com/group/pgxn-users">the PGXN group</a>. See you there!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/5049235040</id><title type="html">About the Infrastructure: PGXN API</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/about-pgxn-api/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-04-29T20:44:02Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="infrastructure" label="Infrastructure"/><category scheme="https://blog.pgxn.org/tags" term="api" label="API"/><category scheme="https://blog.pgxn.org/tags" term="api-server" label="API Server"/><category scheme="https://blog.pgxn.org/tags" term="metadata" label="Metadata"/><category scheme="https://blog.pgxn.org/tags" term="rest" label="REST"/><category scheme="https://blog.pgxn.org/tags" term="http" label="Http"/><summary type="html"><![CDATA[<p>The second piece of the PGXN infrastructure, after <a href="https://blog.pgxn.org/post/4854707157/pgxn-manager" title="About the Infrastructure: PGXN Manager">PGXN Maanager</a>, is the
<a href="https://api.pgxn.org/">PGXN API Server</a>. I&rsquo;ve just finished the <a href="https://github.com/pgxn/pgxn-api/wiki">API documentation</a>, which covers
both the lightweight static file API provided by mirrors and the superset
provided by the API server. So now seems like a good time to talk about the
design of the API server and how it works.</p>
<p>At its core, the PGXN API server is just another mirror. It has an hourly cron
job that <code>rsync</code>s to the master mirror, updating the mirror. But then it
iterates over the <code>rsync</code> log and transforms some things. Here&rsquo;s what it does:</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>The second piece of the PGXN infrastructure, after <a href="https://blog.pgxn.org/post/4854707157/pgxn-manager" title="About the Infrastructure: PGXN Manager">PGXN Maanager</a>, is the
<a href="https://api.pgxn.org/">PGXN API Server</a>. I&rsquo;ve just finished the <a href="https://github.com/pgxn/pgxn-api/wiki">API documentation</a>, which covers
both the lightweight static file API provided by mirrors and the superset
provided by the API server. So now seems like a good time to talk about the
design of the API server and how it works.</p>
<p>At its core, the PGXN API server is just another mirror. It has an hourly cron
job that <code>rsync</code>s to the master mirror, updating the mirror. But then it
iterates over the <code>rsync</code> log and transforms some things. Here&rsquo;s what it does:</p>
<ul>
<li>Unpacks each distribution in a directory for browsing. Here, for example, is
where one can <a href="https://api.pgxn.org/src/semver/semver-0.2.1/">browse the semver 0.2.1</a> sources.</li>
<li>Searches for a <code>README</code> file and any files recognized by <a href="https://search.cpan.org/perldoc?Text::Markup">Text::Markup</a> and
converts them to sanitized HTML with a table of contents. Such files can
then be used to display the <code>README</code> on the <a href="https://pgxn.org/dist/semver/" title="semver distribution page">distribution page</a> and to
display <a href="https://pgxn.org/dist/semver/doc/semver.html" title="semver documentation">individual documentation files</a>.</li>
<li>Merges the distribution metadata file with the latest stable release
<code>META.json</code> generated by PGXN Manager. For example, as of this writing, the
API server&rsquo;s <a href="https://api.pgxn.org/dist/semver/0.2.1/META.json">semver 0.2.1 <code>META.json</code></a> and the unversioned <a href="https://api.pgxn.org/dist/semver.json">semver.json</a>
are identical. Effectively, this format has all the metadata from the
<code>META.json</code> as well as a list of all releases of the distribution from the
<code>semver.json</code>. This is useful for displaying all the data on the
<a href="https://pgxn.org/dist/semver/" title="semver distribution page">distribution page</a> by fetching the data in a single API request.</li>
<li>Updates all other versions of the <code>META.json</code> file. For example, if you look
at the <a href="https://api.pgxn.org/dist/semver/0.2.0/META.json">semver 0.0.0 <code>META.json</code></a>, you&rsquo;ll see that it includes 0.2.1 in its
list of releases, even though 0.2.1 was released after 0.2.0. This allows
<a href="https://pgxn.org/dist/semver/0.2.0/">semver 0.2.0</a> page on the main site to have a select list of version to
choose from, including versions released later, with a single API request.</li>
<li>Adds additional metadata to the extension JSON file for all extensions in
the distribution. The added data includes release dates for the list all
distributions providing the extension, as well as an abstract and doc path
for the latest stable release. To see the differences, compare the <a href="https://api.pgxn.org/mirror/extension/semver.json">mirror
<code>semver.json</code></a> to the <a href="https://api.pgxn.org/extension/semver.json">API <code>semver.json</code></a>.</li>
<li>Adds an abstract for each distribution listed in the user&rsquo;s JSON file and
all tag JSON files. Compare, for example, the <a href="https://api.pgxn.org/mirror/user/theory.json">mirror <code>theory.json</code></a> to the
<a href="https://api.pgxn.org/user/theory.json">API <code>theory.json</code></a> and the <a href="https://api.pgxn.org/mirror/tag/data%20types.json">mirror <code>data types.json</code></a> to the <a href="https://api.pgxn.org/tag/data%20types.json">API
<code>data types.json</code></a>. This allows the <a href="https://pgxn.org/user/theory">user page</a> and <a href="https://pgxn.org/tag/data%20types/">tag pages</a> to include
the abstract in the list of distributions released by the user or associated
with a tag.</li>
<li>Adds records to a <a href="https://incubator.apache.org/lucy/">Lucy</a>-powered full text search index.</li>
</ul>
<p>All of this merging stuff came out of my thinking following the discussion of
the <a href="https://blog.pgxn.org/post/3099288750/pgxn-api-rfc">PGXN API RFC</a>. The decision to use <a href="https://incubator.apache.org/lucy/">Lucy</a> instead of PostgreSQL&rsquo;s
<a href="https://www.postgresql.org/docs/current/static/textsearch.html">full-text search</a> followed rather naturally from this, as I quickly realized
that there was no other driving need for a relational database behind the API
at all. The <em>only</em> dynamic API is the <a href="https://github.com/pgxn/pgxn-api/wiki/search-api">search API</a>. Everything else is just
static files. And given the <a href="https://www.depesz.com/index.php/2010/10/17/why-im-not-fan-of-tsearch-2/">performance issues</a> of in-database search, as
well as the desire to have fewer outside dependencies, made the decision a
natural one.</p>
<p>Beyond the syncing, there is a very simple web server providing the HTTP REST
interface to the static JSON files and the full-text search. That&rsquo;s it,
really. The API server is really just another mirror on steroids. The nice
thing is that it allows an interface, such as <a href="https://search.cpan.org/perldoc?WWW::PGXN">WWW::PGXN</a> or the new <a href="https://blog.pgxn.org/post/5026314153/writing-a-client-for-pgxn">PGXN
client</a> to work with either interface, just failing gracefully when API server
APIs are unavailable.</p>
<p>If you want to learn more about the specifics of the REST API, the <a href="https://github.com/pgxn/pgxn-api/wiki">API
documentation</a> has <em>all</em> the details. Really, it&rsquo;s quite comprehensive!</p>
<p>I actually consider the API to be 1.0-complete at this point, unlike PGXN
Manager. The only thing I want to add is <a href="https://en.wikipedia.org/wiki/JSONP">JSONP</a> support for static JSON files
(right now it&rsquo;s only for search results) and might tweak a few things here and
there, but otherwise I think it&rsquo;s in pretty good shape.</p>
<p>Longer term, though, it might be worthwhile to add some other features to
enhance the value of PGXN overall. Some ideas:</p>
<ul>
<li>Distribution and/or extension ratings (reviews, Like/Dislike, stars, or
something).</li>
<li>Diffs to compare changes between versions.</li>
<li>A test reporting infrastructure with result matrices (á la <a href="https://www.cpantesters.org/">CPAN Testers</a>.</li>
</ul>
<p>But I think we need to build up some momentum on the foundation that&rsquo;s in
place. Have you submitted your extensions, yet?</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/5026314153</id><title type="html">Writing a client for PGXN</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/writing-a-client-for-pgxn/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-04-29T00:15:00Z</published><author><name>Daniele Varrazzo</name></author><summary type="html"><![CDATA[<p>PostgreSQL 9.1 is quickly rolling to the beta phase, and the release will
bring a new useful feature: <a href="https://developer.postgresql.org/pgdocs/postgres/extend-extensions.html">extensions</a>. Well, actually extensions have
always existed, but they didn&rsquo;t have an identity before: what you used to get
was a set of loose objects and mysterious functions in your namespace,
typically getting in the way in your dump and restore. Now here it is: <a href="https://developer.postgresql.org/pgdocs/postgres/sql-createextension.html">CREATE
EXTENSION</a>, in all its uppercase glory!</p>
<p>So, an extension will exist in your database, and this finally gives the
possibility to organize them, this is what <a href="https://pgxn.org/">PGXN</a> is for: a repository for
metadata, documentation and code, neatly packaged and asking to be picked off
the shelf. I&rsquo;ve recently <a href="https://pgmp.projects.postgresql.org/">written an extension</a> myself so I got some original
material to play with PGXN, which has resulted in quite a pleasant experience:
<a href="https://manager.pgxn.org/">the infrastructure</a> is well done thanks to David&rsquo;s design, and <a href="https://github.com/pgxn/pgxn-api/wiki/index-api">the API</a> is
ridiculously easy to interact with: I&rsquo;ve literally explored it using curl.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>PostgreSQL 9.1 is quickly rolling to the beta phase, and the release will
bring a new useful feature: <a href="https://developer.postgresql.org/pgdocs/postgres/extend-extensions.html">extensions</a>. Well, actually extensions have
always existed, but they didn&rsquo;t have an identity before: what you used to get
was a set of loose objects and mysterious functions in your namespace,
typically getting in the way in your dump and restore. Now here it is: <a href="https://developer.postgresql.org/pgdocs/postgres/sql-createextension.html">CREATE
EXTENSION</a>, in all its uppercase glory!</p>
<p>So, an extension will exist in your database, and this finally gives the
possibility to organize them, this is what <a href="https://pgxn.org/">PGXN</a> is for: a repository for
metadata, documentation and code, neatly packaged and asking to be picked off
the shelf. I&rsquo;ve recently <a href="https://pgmp.projects.postgresql.org/">written an extension</a> myself so I got some original
material to play with PGXN, which has resulted in quite a pleasant experience:
<a href="https://manager.pgxn.org/">the infrastructure</a> is well done thanks to David&rsquo;s design, and <a href="https://github.com/pgxn/pgxn-api/wiki/index-api">the API</a> is
ridiculously easy to interact with: I&rsquo;ve literally explored it using curl.</p>
<p>There was a big piece missing in the jigsaw that&rsquo;s being composed: a client to
make easy for database developers and administrators to download, build and
install extensions, and with PGXN getting more mature and people starting
packaging their work it was really <em>begging</em> to be written! So here it is:
<del>pgxn.client</del> <a href="https://github.com/dvarrazzo/pgxnclient">pgxnclient</a>, a command line tool to interact with PGXN. There
is still a lot to be developed, but the idea is to release early, get feedback
for it and improve it quickly to complete the extensions picture. So it&rsquo;s
already <a href="https://pypi.python.org/pypi/pgxnclient/">available on PyPI</a> and very easy to start with it: just use</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> sudo easy_install pgxnclient
</span></span></code></pre></div><p>and in a few seconds you will have a script called <code>pgxn</code>, offering several
commands that can be displayed with <code>pgxn --help</code> (and currently this is the
most up-to-date documentation you can get, as new commands are getting added
very quickly).</p>
<p>Were you looking for a solution to store hashes? You may try:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> pgxn search <span class="nb">hash</span>
</span></span><span class="line"><span class="cl"><span class="go">sha 1.0.0
</span></span></span><span class="line"><span class="cl"><span class="go">session_hash_tools 1.0.0
</span></span></span><span class="line"><span class="cl"><span class="go">semver 0.2.1
</span></span></span></code></pre></div><p>Uhm, is <code>sha</code> a possible solution?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> pgxn info sha
</span></span><span class="line"><span class="cl"><span class="go">INFO: best version: sha 1.0.0
</span></span></span><span class="line"><span class="cl"><span class="go">name: sha
</span></span></span><span class="line"><span class="cl"><span class="go">abstract: This module provides datatypes for storing SHA-1,
</span></span></span><span class="line"><span class="cl"><span class="go">SHA-2 and MD5 hashes
</span></span></span><span class="line"><span class="cl"><span class="go">maintainer: Alexey Klyukin &lt;a...@commandprompt.com&gt;
</span></span></span><span class="line"><span class="cl"><span class="go">license: postgresql
</span></span></span><span class="line"><span class="cl"><span class="go">release_status: stable
</span></span></span><span class="line"><span class="cl"><span class="go">version: 1.0.0
</span></span></span><span class="line"><span class="cl"><span class="go">date: 2011-03-16T10:33:00Z
</span></span></span><span class="line"><span class="cl"><span class="go">sha1: 0187b0d261d302605bf8d0a15cdbd809deb245dd
</span></span></span><span class="line"><span class="cl"><span class="go">provides: sha: 1.0.0
</span></span></span></code></pre></div><p>Let&rsquo;s say it is exactly what we were looking for (we could also display the
readme with <code>pgxn info --readme</code>). Shall we give it a try?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> sudo pgxn install sha
</span></span><span class="line"><span class="cl"><span class="go">INFO: best version: sha 1.0.0
</span></span></span><span class="line"><span class="cl"><span class="go">INFO: saving /tmp/tmpj8G6kM/sha-1.0.0.zip
</span></span></span><span class="line"><span class="cl"><span class="go">INFO: unpacking: /tmp/tmpj8G6kM/sha-1.0.0.zip
</span></span></span><span class="line"><span class="cl"><span class="go">INFO: building extension
</span></span></span><span class="line"><span class="cl"><span class="go">[some compiler log]
</span></span></span><span class="line"><span class="cl"><span class="go">INFO: installing extension
</span></span></span><span class="line"><span class="cl"><span class="go">[files being copied]
</span></span></span></code></pre></div><p>Now the code is in the right place in the database directory (a specific
<code>pg_config</code> can be specified to choose which one): If you want it in a
specific database:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> pgxn load -U postgres -d <span class="nb">test</span> sha
</span></span></code></pre></div><p>This will result in CREATE EXTENSION being invoked, if the target database
supports it, or in the loading of the provided sql file for PostgreSQL
versions up to 9.0.</p>
<p>Easy, wasn&rsquo;t it?</p>
<p>There are still a lot of features to add:</p>
<ul>
<li>drop extension from a database and uninstall from the system</li>
<li>select the target schema</li>
<li>work with locally downloaded packages</li>
</ul>
<p>but as <code>pgxnclient</code> uses the PGXN server on a side and the <a href="https://developer.postgresql.org/pgdocs/postgres/extend-pgxs.html">PGXS build
infrastructure</a> on the other, the features will be easy to add and the client
work is actually an easy one.</p>
<p>Give it a try if you want: your feedback is very welcome. Meanwhile we will
keep on adding features to cover the entire extensions life cycle.</p>
<p><strong>Edit</strong>: the program has been <a href="https://blog.pgxn.org/post/5118152273/new-release-for-the-pgxn-client">renamed to pgxnclient</a>: the relevant URLs
have been updated.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/4970682941</id><title type="html">PGXN API Docs Published</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/api-docs-published/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-04-27T00:27:55Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="api" label="API"/><category scheme="https://blog.pgxn.org/tags" term="documentation" label="Documentation"/><category scheme="https://blog.pgxn.org/tags" term="docs" label="Docs"/><category scheme="https://blog.pgxn.org/tags" term="api-docs" label="API Docs"/><category scheme="https://blog.pgxn.org/tags" term="zip" label="Zip"/><category scheme="https://blog.pgxn.org/tags" term="client" label="Client"/><category scheme="https://blog.pgxn.org/tags" term="daniele-varrazzo" label="Daniele Varrazzo"/><summary type="html"><![CDATA[<p>The <a href="https://github.com/pgxn/pgxn-api/wiki">PGXN API Documentation</a> is up! I&rsquo;ve just finished writing the docs for
the lightweight REST API provided by all PGXN mirrors. It also documents how
the same APIs differ when provided by the <a href="https://api.pgxn.org/">API server</a>. Their are four more
documents to write still, APIs provided by the API server but not the mirrors
(including full-text search); I should be able to get those done tomorrow.</p>
<p>So if you&rsquo;re interested in use the API for various things, please <a href="https://github.com/pgxn/pgxn-api/wiki">have a
look</a>! I&rsquo;d appreciate any feedback or corrections.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>The <a href="https://github.com/pgxn/pgxn-api/wiki">PGXN API Documentation</a> is up! I&rsquo;ve just finished writing the docs for
the lightweight REST API provided by all PGXN mirrors. It also documents how
the same APIs differ when provided by the <a href="https://api.pgxn.org/">API server</a>. Their are four more
documents to write still, APIs provided by the API server but not the mirrors
(including full-text search); I should be able to get those done tomorrow.</p>
<p>So if you&rsquo;re interested in use the API for various things, please <a href="https://github.com/pgxn/pgxn-api/wiki">have a
look</a>! I&rsquo;d appreciate any feedback or corrections.</p>
<p>Speaking of users of the API, <a href="https://profiles.google.com/daniele.varrazzo/about">Daniele Varrazzo</a> has started writing a <a href="https://github.com/dvarrazzo/pgxn-client/">PGXN
client app</a> in Python. This is so awesome! It makes me happy that people are
able to just get going on this. And Daniele did it before I&rsquo;d written the
docs, just from reading the PGXN source code. Nice!</p>
<p>Anyway, getting these docs written was the last barrier I had to releasing
various pieces of PGXN on CPAN and generally being able to get on with my
life. I wanted to write these docs first so that I would have a little more
freedom to change things if something struct me as especially stupid.
Fortunately, as I&rsquo;ve written the <a href="https://pgxn.org/">main site</a> as a thin client for the API,
most of the lameness has already been excised. I think the only change I&rsquo;d
like to make is to change the <code>{char}</code> URI template variable for the
<a href="https://github.com/pgxn/pgxn-api/wiki/userlist-api"><code>userlist</code> API</a> to <code>{letter}</code>, because only lowercased ASCII letters a-z are
allowed. It&rsquo;s more accurate.</p>
<p>Another thing I think I&rsquo;ll change is the file name suffix used for the
distribution download files. Currently it&rsquo;s <code>.pgz</code>, but after <a href="https://blog.pgxn.org/post/4854707157/pgxn-manager">Daniele
complained</a> about it, I <a href="https://groups.google.com/group/pgxn-users/browse_thread/thread/1209538be03be8a4">asked around</a> and the concensus seems to be to change
it to <code>.zip</code>. I&rsquo;ll likely do that tomorrow, too. Fortunately it&rsquo;s pretty easy:
Just edit the configuration file and rename the files on the master mirror. At
least it <em>should</em> be that easy!</p>
<p>There was one other thing in the APIs the felt a bit silly, but I don&rsquo;t
remember what it was, so screw it.</p>
<p>Anyway, feedback on the API docs would be greatly appreciated. And &ndash; <em>get
hacking!</em>.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/4948135198</id><title type="html">Case Insensitivity</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/case-insensitivity/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-04-26T04:09:08Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="lowercase" label="Lowercase"/><category scheme="https://blog.pgxn.org/tags" term="case" label="Case"/><category scheme="https://blog.pgxn.org/tags" term="case-sensitive" label="Case Sensitive"/><category scheme="https://blog.pgxn.org/tags" term="case-insensitive" label="Case Insensitive"/><category scheme="https://blog.pgxn.org/tags" term="meaning" label="Meaning"/><summary type="html"><![CDATA[<p>I pushed out new versions of PGXN::Manager, PGXN::API, and PGXN::Site today,
all with the aim of resolving some issues with case-sensitivity. I was alerted
to this when Daniele Varrzzo released <a href="https://pgxn.org/dist/italian_fts/">italian_fts</a> with the <a href="https://pgxn.org/tag/italian/">Italian tag</a> and
the API sync blew up. Oops.</p>
<p>So here&rsquo;s the deal. The following objects have case-insensitive names on PGXN:</p>
<ul>
<li>Distributions</li>
<li>Extensions</li>
<li>Versions</li>
<li>Users (nickname)</li>
<li>Tags</li>
</ul>
<p>These names are case-preserving, so they should show up with the proper
capitalization in JSON files and whatnot. But to avoid issues with URLs, I
decided that all file names should be lowercase only. Fortunately, nearly
everything was lowercase already: only a few nickname JSON files were not
lowercased, plus the files for <a href="https://pgxn.org/dist/pgtap/">pgTAP</a>. So once I got the code updated, I
renamed those files appropriately. In the case of the pgTAP download, I
actually re-indexed it, so that the prefix in the zip file would properly be
&ldquo;pgtap-0.25.0&rdquo; instead of &ldquo;pgTAP-0.25.0&rdquo;, since the file name is now
<code>pgtap-0.25.0.pgz</code>.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I pushed out new versions of PGXN::Manager, PGXN::API, and PGXN::Site today,
all with the aim of resolving some issues with case-sensitivity. I was alerted
to this when Daniele Varrzzo released <a href="https://pgxn.org/dist/italian_fts/">italian_fts</a> with the <a href="https://pgxn.org/tag/italian/">Italian tag</a> and
the API sync blew up. Oops.</p>
<p>So here&rsquo;s the deal. The following objects have case-insensitive names on PGXN:</p>
<ul>
<li>Distributions</li>
<li>Extensions</li>
<li>Versions</li>
<li>Users (nickname)</li>
<li>Tags</li>
</ul>
<p>These names are case-preserving, so they should show up with the proper
capitalization in JSON files and whatnot. But to avoid issues with URLs, I
decided that all file names should be lowercase only. Fortunately, nearly
everything was lowercase already: only a few nickname JSON files were not
lowercased, plus the files for <a href="https://pgxn.org/dist/pgtap/">pgTAP</a>. So once I got the code updated, I
renamed those files appropriately. In the case of the pgTAP download, I
actually re-indexed it, so that the prefix in the zip file would properly be
&ldquo;pgtap-0.25.0&rdquo; instead of &ldquo;pgTAP-0.25.0&rdquo;, since the file name is now
<code>pgtap-0.25.0.pgz</code>.</p>
<p>So why make these objects case-insensitive? Well, because I tend to think of
the names of things as having <em>meaning</em>, and really, there is no difference in
meaning between &ldquo;pgtap,&rdquo; &ldquo;pgTAP,&rdquo; or &ldquo;PGTap.&rdquo; I&rsquo;ve relased pgTAP; I think it
would be as confusing as hell if someone else released a completely different
distribution named &ldquo;PGTap.&rdquo; (And I went through a bit of hell when the
<a href="https://search.cpan.org/perldoc?Apache::test">Apache::test</a> CPAN module was replaced with <a href="https://search.cpan.org/perldoc?Apache::Test">Apache::Test</a>, a completely
new module. It really messed things up on the case-insensitive file system I
use).</p>
<p>But as I said, the case is preserved in the name I use, just not in the names
of files. So if you look at the <a href="https://pgxn.org/dist/pgtap/">pgTAP distribution page</a>, you&rsquo;ll see
that &ldquo;This Release&rdquo; is labeled &ldquo;pgTAP 0.25.0&rdquo; (I need to fix the <code>&lt;h1&gt;</code>
element still).</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/4854707157</id><title type="html">About the Infrastructure: PGXN Manager</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/pgxn-manager/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-04-23T02:54:34Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="pgxnmanager" label="PGXN::Manager"/><category scheme="https://blog.pgxn.org/tags" term="infrastructure" label="Infrastructure"/><category scheme="https://blog.pgxn.org/tags" term="code" label="Code"/><category scheme="https://blog.pgxn.org/tags" term="perl" label="Perl"/><category scheme="https://blog.pgxn.org/tags" term="semver" label="SemVer"/><category scheme="https://blog.pgxn.org/tags" term="json" label="Json"/><category scheme="https://blog.pgxn.org/tags" term="normalization" label="Normalization"/><category scheme="https://blog.pgxn.org/tags" term="validation" label="Validation"/><category scheme="https://blog.pgxn.org/tags" term="zip" label="Zip"/><summary type="html"><![CDATA[<p>The PGXN infrastructure is currently made up of four parts:</p>
<ul>
<li>
<p>The network of <a href="https://api.pgxn.org/mirror/meta/mirrors.json">mirrors</a>, derived from the <a href="https://master.pgxn.org/">master mirror</a>, and synchronized
on various schedules via <code>rsync</code> (<a href="https://pgxn.org/mirroring/">host a mirror</a>).</p>
</li>
<li>
<p><a href="https://manager.pgxn.org/">PGXN Manager</a> (<a href="https://github.com/pgxn/pgxn-manager/" title="Fork PGXN::Manager on GitHub">code</a>) is the core of the network. It provides the
interface for users to upload releases, processes those releases, indexes
them, and puts them on the master mirror. Details below.</p>
</li>
<li>
<p><a href="https://api.pgxn.org">PGXN API</a> (<a href="https://github.com/pgxn/pgxn-api" title="Fork PGXN::API on GitHub">code</a>) is a mirror server with benefits. Once an hour, it
<code>rsync</code>s from the master mirror, and does extra processing of new and
modified files, notably full-text indexing. I&rsquo;ll write up some details next
week.</p>
</li>
<li>
<p><a href="https://pgxn.org">PGXN Site</a> (<a href="https://github.com/pgxn/pgxn-site" title="Fork PGXN::Site on GitHub">code</a>) powers the main site. It&rsquo;s a thin wrapper around the
API server, using <a href="https://github.com/pgxn/www-pgxn" title="Fork WWW::PGXN on GitHub">WWW::PGXN</a> to fetch JSON files and convert them to HTML.
I&rsquo;ll write more about this bit next week, too.</p>
</li>
</ul>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>The PGXN infrastructure is currently made up of four parts:</p>
<ul>
<li>
<p>The network of <a href="https://api.pgxn.org/mirror/meta/mirrors.json">mirrors</a>, derived from the <a href="https://master.pgxn.org/">master mirror</a>, and synchronized
on various schedules via <code>rsync</code> (<a href="https://pgxn.org/mirroring/">host a mirror</a>).</p>
</li>
<li>
<p><a href="https://manager.pgxn.org/">PGXN Manager</a> (<a href="https://github.com/pgxn/pgxn-manager/" title="Fork PGXN::Manager on GitHub">code</a>) is the core of the network. It provides the
interface for users to upload releases, processes those releases, indexes
them, and puts them on the master mirror. Details below.</p>
</li>
<li>
<p><a href="https://api.pgxn.org">PGXN API</a> (<a href="https://github.com/pgxn/pgxn-api" title="Fork PGXN::API on GitHub">code</a>) is a mirror server with benefits. Once an hour, it
<code>rsync</code>s from the master mirror, and does extra processing of new and
modified files, notably full-text indexing. I&rsquo;ll write up some details next
week.</p>
</li>
<li>
<p><a href="https://pgxn.org">PGXN Site</a> (<a href="https://github.com/pgxn/pgxn-site" title="Fork PGXN::Site on GitHub">code</a>) powers the main site. It&rsquo;s a thin wrapper around the
API server, using <a href="https://github.com/pgxn/www-pgxn" title="Fork WWW::PGXN on GitHub">WWW::PGXN</a> to fetch JSON files and convert them to HTML.
I&rsquo;ll write more about this bit next week, too.</p>
</li>
</ul>
<p>Some details on Manager. This is by far the most complicated part of the
system. Which is funny, because I hadn&rsquo;t anticipated that when I started work
on PGXN (I&rsquo;d estimated half as many hours as for implementing the site). But
as I worked through design issues and wrote the code, the need for that
complexity became apparent &ndash; and not just because it&rsquo;s the only part that
offers authentication. <em>The main reason it&rsquo;s complex is so that no other part
needs to be.</em></p>
<p>Allow me to explain. People can upload almost anything as a distribution. So
long as the <code>META.json</code> adheres to <a href="https://pgxn.org/spec/">the spec</a>, the rest can be just about
anything. But the upload doesn&rsquo;t necessarily end up on the network unmodified.
Sure, if you follow the guidelines of the <a href="https://manager.pgxn.org/howto">HOWTO</a> what you uploaded will be
exactly what ends up on the network. But I didn&rsquo;t want to be that strict about
PGXN Manager would accept. So in addition to verifying the structure of your
<code>META.json</code> file, <a href="https://github.com/pgxn/pgxn-manager/blob/master/lib/PGXN/Manager/Distribution.pm" title="PGXN::Manager::Distribution on GitHub">PGXN::Manager::Distribution</a> also:</p>
<ul>
<li>
<p>Extracts the archive. If it&rsquo;s not a zip file, a zip archive is created from
the contents of the uploaded file. You can upload any kind of archive
readable by <a href="https://search.cpan.org/perldoc?Archive::Extract">Archive::Extract</a>. The currently supported formats are: <code>.tar</code>,
<code>.tar.gz</code>, <code>.gz</code>, <code>.Z</code>, <code>tar.bz2</code>, <code>.tbz</code>, <code>.bz2</code>, <code>.zip</code>, <code>.xz,</code>, <code>.txz</code>,
<code>.tar.xz</code> and <code>.lzma</code>. By always converting to a zip file, PGXN client apps
can be quite simple, not having to worry about the archive format.</p>
</li>
<li>
<p>Validates the <code>META.json</code>. This part isn&rsquo;t perfect, yet. I fixed a bug today
where it would die and return a 500 on a missing version number. It&rsquo;d
probably be worthwhile to adapt <a href="https://search.cpan.org/perldoc?CPAN::Meta::Validator">CPAN::Meta::Validator</a> to validate PGXN
<code>META.json</code> files at some point, both for Manager and for developers wanting
to validate before uploading.</p>
</li>
<li>
<p>Normalizes all version numbers in the <code>META.json</code> into <a href="https://semver.org/">semantic versions</a>.
You can specify the distribution version, prerequisite versions, and
extension versions as simple numbers and they&rsquo;ll be converted to semantic
versions. A version like &ldquo;1.20&rdquo;, for example, becomes &ldquo;1.2.0&rdquo;. See the
<a href="https://search.cpan.org/~dwheeler/SemVer-v0.2.0/lib/SemVer.pm#Usage">SemVer documentation</a> for details on how versions are normalized via the
<code>declare()</code> method. This normalization is done so that client applications
will get known valid semantic versions to compare when determining
dependencies. However, it&rsquo;s best that they be semantic versions to begin
with. Normalized versions will be written back to the archive <code>META.json</code>
file (with the &ldquo;generated_by&rdquo; key updated to reflect that PGXN Manager
regenerated the file). If no versions need validating, the archive
<code>META.json</code> will be left alone.</p>
</li>
<li>
<p>Makes sure the zip archive has a directory prefix named <code>&quot;$dist-$version/&quot;</code>.
If the archive has no directory prefix, or if the prefix is not
<code>&quot;$dist-$version/&quot;</code>, the archive is rewritten with that prefix. This ensures
that the archive will always extract into a directory with the same name as
the archive and not spray files all over your desktop when you unzip it.</p>
</li>
<li>
<p>Copies or writes out a new zip file named <code>&quot;$dist-$version.pgz&quot;</code>. Think of
<code>.pgz</code> as &ldquo;PostgreSQL Zip&rdquo; or, if you&rsquo;d rather, &ldquo;PGXN Zip&rdquo;. Either way, it&rsquo;s
just a zip archive.</p>
</li>
</ul>
<p>That processing done, with a good <code>META.json</code> and zip archive, the JSON,
username, and SHA1 of the zip archive are handed off to the database for more
processing. The <a href="https://github.com/pgxn/pgxn-manager/wiki/DB-API#add_distribution"><code>add_distribution()</code></a> database function does all the heavy
lifting here. It:</p>
<ul>
<li>
<p>Parses the JSON string, validates that all required keys are present, and
normalizes version numbers. Yes, this is redundant, but I don&rsquo;t think I need
to lecture the reads of this blog about database integrity. :-)</p>
</li>
<li>
<p>Creates a new metadata structure and stores all the required and many of the
optional meta spec keys, as well as the SHA1 of the distribution file, the
date, and the user&rsquo;s nickname.</p>
</li>
<li>
<p>Sets the &ldquo;release_status&rdquo; to &ldquo;stable&rdquo; if there was no status in the original
JSON.</p>
</li>
<li>
<p>Adds a &ldquo;provides&rdquo; section to the metadata if none was included in the
original JSON. In such a case, it assumes that the distribution contains one
extension and that it has the same name and version as the distribution
itself.</p>
</li>
<li>
<p>Validates that the uploading user is owner or co-owner of all provided
extensions. If no one is listed as owner of one or more included extensions,
the user will be assigned ownership. If the user is not owner or co-owner of
any included extensions, an exception will be thrown.</p>
</li>
<li>
<p>Records the distribution, extensions, and tags in the database.</p>
</li>
</ul>
<p>Once all this work is done, <code>add_distribution()</code> returns all the JSON that
needs to be written to the mirror. These files make up the &ldquo;index&rdquo; on the
network, and include:</p>
<ul>
<li>The <code>META.json</code> file (<a href="https://api.pgxn.org/mirror/dist/semver/0.2.1/META.json">example</a>). This file is derived from the <code>META.json</code>
included in the archive (<a href="https://api.pgxn.org/src/semver/semver-0.2.1/META.json">example</a>), but reflects all the normalization
changes and added keys outlined above.</li>
<li>A short JSON file summarizing all releases of the distribution
(<a href="https://api.pgxn.org/mirror/dist/semver.json">example</a>).</li>
<li>JSON files describing all releases of all extensions included in the
distribution (<a href="https://api.pgxn.org/mirror/extension/semver.json">example</a>).</li>
<li>A JSON file for the user, which includes data about all releases made by
that user (<a href="https://api.pgxn.org/mirror/user/theory.json">example</a>).</li>
<li>JSON files for each tag associated with the distribution. Each of these
files lists all the distributions the tag is associated with (<a href="https://api.pgxn.org/mirror/tag/datatype.json">example</a>).</li>
</ul>
<p>It also returns JSON for network statistics files. These are updated every
time a new release is uploaded:</p>
<ul>
<li><a href="https://api.pgxn.org/mirror/stats/dist.json"><code>dist.json</code></a> lists the 56 most recent releases and has a count of all
distributions and of releases of those distributions.</li>
<li><a href="https://api.pgxn.org/mirror/stats/extension.json"><code>extension.json</code></a> lists the 56 most recent extension releases and a count
of all extensions on the network.</li>
<li><a href="https://api.pgxn.org/mirror/stats/user.json"><code>user.json</code></a> lists of the 56 most prolific users (based on the number of
distributions) and a count of distributions and releases for each.</li>
<li><a href="https://api.pgxn.org/mirror/stats/tag.json"><code>tag.json</code></a> lists the 56 most popular tags (measured by the number of
distributions they&rsquo;re associated with) and a count of all tags on the
network.</li>
<li><a href="https://api.pgxn.org/mirror/stats/summary.json"><code>summary.json</code></a> has basic summary information about the network, which is
just counts of distributions, releases, extensions, users, tags, and
mirrors.</li>
</ul>
<p>If you think that&rsquo;s a lot of data to be updated, you&rsquo;re right! But since
releases are relatively infrequent (a couple a day at the moment), it&rsquo;s best
to generate all this stuff as static files that are <code>rsync</code>ed to all mirrors.
In this way, <em>any</em> mirror can function as a very simple, lightweight REST API.
And indeed, that&rsquo;s just how the planned <a href="https://github.com/pgxn/pgxn-client/" title="Fork PGXN::Client on GitHub">PGXN client</a> will behave. The
<a href="https://github.com/pgxn/www-pgxn/" title="Fork WWW::PGXN on GitHub">WWW::PGXN</a> already provides the interface it will use.</p>
<p>I guess that was a lot of information. Let this be a reference document for
interested hackers, then. The core functionality is all there, but there&rsquo;s a
<a href="https://github.com/pgxn/pgxn-manager/issues/">lot more to be done</a>:</p>
<ul>
<li>Add a UI for users to assign extensions to co-owners (when more than one
user is allowed to upload an extension) and to transfer ownership (<a href="https://github.com/pgxn/pgxn-manager/issues/6">#6</a>).</li>
<li>Add a user administration interface (<a href="https://github.com/pgxn/pgxn-manager/issues/7">#7</a>).</li>
<li>Add a UI where admins can transfer ownership of extensions when an owner
disappears (<a href="https://github.com/pgxn/pgxn-manager/issues/8">#8</a>).</li>
<li>Add a callback architecture to trigger other actions on successful upload.
These would include tweeting new releases (currently just handled in the
controller) and running arbitrary command-line utilities (such as syncing
the PGXN API server so it can be up-to-date as quickly as possible) (<a href="https://github.com/pgxn/pgxn-manager/issues/9">#9</a>).</li>
<li>Add support for &ldquo;instant mirroring&rdquo; via <a href="https://search.cpan.org/perldoc?File::Rsync::Mirror::Recent"><code>rrr</code></a> (<a href="https://github.com/pgxn/pgxn-manager/issues/10">#10</a>).</li>
<li>Add an Atom feed for recent releases (basically a dupe of the distribution
stats file) (<a href="https://github.com/pgxn/pgxn-manager/issues/17">#17</a>).</li>
<li>Add a command-line interface to the API. The HTTP server is written in such
a way that it can act as an API serving JSON, but it&rsquo;s not well-tested and
nothing uses it yet (that I&rsquo;m aware of) (<a href="https://github.com/pgxn/pgxn-client/issues/1">#1</a>).</li>
<li>Add UI for re-indexing a distribution (<a href="https://github.com/pgxn/pgxn-manager/issues/11">#11</a>).</li>
<li>Add ability to delete distributions (<a href="https://github.com/pgxn/pgxn-manager/issues/12">#12</a>)?</li>
<li>Add an email gateway so that email addressed to <code>$nickname@pgxn.org</code> will be
forwarded to a user&rsquo;s actual address. This would allow us to remove literal
email addresses from the JSON files and the site (the site obfuscates them,
but still&hellip;). Anyone got some good postfix chops for this? The <a href="https://github.com/pgxn/pgxn-manager/blob/master/sql/03-users.sql"><code>users</code>
table</a> is quite simple (<a href="https://github.com/pgxn/pgxn-manager/issues/13">#13</a>).</li>
</ul>
<p>Want to help out? Fork <a href="https://manager.pgxn.org/" title="For PGXN::Manager on GitHub">PGXN Manager</a> and have at it. Hell, at this point
I&rsquo;d really appreciate a code review, as I&rsquo;m pretty sure there&rsquo;s only been one
set of eyes on this code so far.</p>
<p>Next week, I plan to blog about</p>
<ul>
<li>Project status: where the hours went and where they&rsquo;re going.</li>
<li><a href="https://tools.ietf.org/html/draft-gregorio-uritemplate-04">URI templates</a> and how they provide a method index for the mirror API</li>
<li>Mirrors and mirroring via <code>rsync</code></li>
<li><a href="https://api.pgxn.org/">PGXN API</a>: how it provides a superset of the mirror API, including
full-text search.</li>
<li><a href="https://pgxn.org/">PGXN Site</a>: how it&rsquo;s just a very thing wrapper over the API (but can
read the API directly from the file system!)</li>
<li>Plans for the PGXN client</li>
</ul>
<p>But given how these things go, and how I need to start writing mirror API and
API server documentation, it might take me a longer to get to them all.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/4809720896</id><title type="html">Working on the donor shirt design</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/shirt-mockup/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-04-21T17:10:58Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="shirt" label="Shirt"/><category scheme="https://blog.pgxn.org/tags" term="gear" label="Gear"/><category scheme="https://blog.pgxn.org/tags" term="logo" label="Logo"/><category scheme="https://blog.pgxn.org/tags" term="donor" label="Donor"/><summary type="html"><![CDATA[<figure><img src="/2011/shirt-mockup/shirt.jpg"
			alt="Photo of a male torso in a blue-ish shirt with the PGXN logo pinned to its side"><figcaption>
			<p>Working on the donor shirt design.</p>
		</figcaption>
</figure>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<figure><img src="/2011/shirt-mockup/shirt.jpg"
			alt="Photo of a male torso in a blue-ish shirt with the PGXN logo pinned to its side"><figcaption>
			<p>Working on the donor shirt design.</p>
		</figcaption>
</figure>]]></content></entry><entry><id>https://blog.pgxn.org/post/4809393274</id><title type="html">What Media Types for Browsing?</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/safe-media-types/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-04-21T16:56:43Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="mime" label="MIME"/><category scheme="https://blog.pgxn.org/tags" term="media-type" label="Media Type"/><category scheme="https://blog.pgxn.org/tags" term="mime-type" label="MIME Type"/><category scheme="https://blog.pgxn.org/tags" term="browse" label="Browse"/><category scheme="https://blog.pgxn.org/tags" term="source" label="Source"/><category scheme="https://blog.pgxn.org/tags" term="user-submitted" label="User Submitted"/><category scheme="https://blog.pgxn.org/tags" term="content" label="Content"/><category scheme="https://blog.pgxn.org/tags" term="text" label="Text"/><category scheme="https://blog.pgxn.org/tags" term="plain-text" label="Plain Text"/><category scheme="https://blog.pgxn.org/tags" term="html" label="HTML"/><category scheme="https://blog.pgxn.org/tags" term="c" label="C"/><category scheme="https://blog.pgxn.org/tags" term="json" label="JSON"/><summary type="html"><![CDATA[<p>Thanks to a comment from &ldquo;Anon,&rdquo; I&rsquo;m auditing the media types used to serve
files from the &ldquo;Browse&rdquo; links on the site. The browse interface is managed by
<a href="https://search.cpan.org/perldoc?Plack::App::Directory">Plack::App::Directory</a>, which gets its media type mapping from <a href="https://github.com/miyagawa/Plack/blob/master/lib/Plack/MIME.pm">this file</a>.
What PGXN::API does is simply change the mappings of some of those types to
<code>text/plain</code>. As of <a href="https://github.com/pgxn/pgxn-api/commit/0157c18cbc0835b627fa2e42b2433337f5f3fff5">this commit</a>, files are served as plain text if:</p>
<ul>
<li>The strings &ldquo;html&rdquo;, &ldquo;x-c&rdquo;, &ldquo;xml&rdquo;, &ldquo;calendar&rdquo;, or &ldquo;vcard&rdquo; appear in the
default media type; or</li>
<li>The file name extension is one of <code>.bat</code>, <code>.css</code>, <code>.eml</code>, <code>.js</code>, <code>.json</code>,
<code>.mime</code>, or <code>.swf</code>.</li>
</ul>
<p>Are there other <a href="https://github.com/miyagawa/Plack/blob/master/lib/Plack/MIME.pm">media types</a> that should be disabled for safe
browsing of user-submitted content?</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Thanks to a comment from &ldquo;Anon,&rdquo; I&rsquo;m auditing the media types used to serve
files from the &ldquo;Browse&rdquo; links on the site. The browse interface is managed by
<a href="https://search.cpan.org/perldoc?Plack::App::Directory">Plack::App::Directory</a>, which gets its media type mapping from <a href="https://github.com/miyagawa/Plack/blob/master/lib/Plack/MIME.pm">this file</a>.
What PGXN::API does is simply change the mappings of some of those types to
<code>text/plain</code>. As of <a href="https://github.com/pgxn/pgxn-api/commit/0157c18cbc0835b627fa2e42b2433337f5f3fff5">this commit</a>, files are served as plain text if:</p>
<ul>
<li>The strings &ldquo;html&rdquo;, &ldquo;x-c&rdquo;, &ldquo;xml&rdquo;, &ldquo;calendar&rdquo;, or &ldquo;vcard&rdquo; appear in the
default media type; or</li>
<li>The file name extension is one of <code>.bat</code>, <code>.css</code>, <code>.eml</code>, <code>.js</code>, <code>.json</code>,
<code>.mime</code>, or <code>.swf</code>.</li>
</ul>
<p>Are there other <a href="https://github.com/miyagawa/Plack/blob/master/lib/Plack/MIME.pm">media types</a> that should be disabled for safe
browsing of user-submitted content?</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/4783001135</id><title type="html">Extension Makefiles for PostgreSQL 9.1</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/extension-makefiles/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-04-20T19:29:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="extension" label="Extension"/><category scheme="https://blog.pgxn.org/tags" term="makefile" label="Makefile"/><category scheme="https://blog.pgxn.org/tags" term="make" label="make"/><category scheme="https://blog.pgxn.org/tags" term="pgxs" label="Pgxs"/><category scheme="https://blog.pgxn.org/tags" term="pg_config" label="pg_config"/><summary type="html"><![CDATA[<p>In order to keep distribution packaging as simple as possible, I worked up
this <code>Makefile</code> some time ago:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="nv">DATA</span> <span class="o">=</span> <span class="k">$(</span>wildcard sql/*.sql<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">DOCS</span> <span class="o">=</span> <span class="k">$(</span>wildcard doc/*.txt<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">TESTS</span> <span class="o">=</span> <span class="k">$(</span>wildcard test/sql/*.sql<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">REGRESS</span> <span class="o">=</span> <span class="k">$(</span>patsubst test/sql/%.sql,%,<span class="k">$(</span>TESTS<span class="k">))</span>
</span></span><span class="line"><span class="cl"><span class="nv">REGRESS_OPTS</span> <span class="o">=</span> --inputdir<span class="o">=</span><span class="nb">test</span> --load-language<span class="o">=</span>plpgsql
</span></span><span class="line"><span class="cl"><span class="nv">MODULES</span> <span class="o">=</span> <span class="k">$(</span>patsubst %.c,%,<span class="k">$(</span>wildcard src/*.c<span class="k">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">PG_CONFIG</span> <span class="o">=</span> pg_config
</span></span><span class="line"><span class="cl"><span class="nv">PGXS</span> <span class="o">:=</span> <span class="k">$(</span>shell <span class="k">$(</span>PG_CONFIG<span class="k">)</span> --pgxs<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="err">include</span> <span class="k">$(</span><span class="nv">PGXS</span><span class="k">)</span>
</span></span></code></pre></div><p>The nice thing about this code is that it has nothing specific to a
distribution in it. It figures out what SQL files there are, what doc files
there are, and what C files need compiling by just looking in the <code>sql</code>,
<code>doc</code>, and <code>src</code> directories, respectively. It also specifies that tests are
in the <code>test</code> directory. About the only thing I&rsquo;ve customized here is adding
<code>--load-language=plpgsql</code> to <code>REGRESS_OPTS</code>, as the tests for the distribution
I&rsquo;ve copied this from require PL/pgSQL to run. Simple, and anyone can use it
with very little need to tweak it, as long as they don&rsquo;t mind storing their
files in the specified directories.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>In order to keep distribution packaging as simple as possible, I worked up
this <code>Makefile</code> some time ago:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="nv">DATA</span> <span class="o">=</span> <span class="k">$(</span>wildcard sql/*.sql<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">DOCS</span> <span class="o">=</span> <span class="k">$(</span>wildcard doc/*.txt<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">TESTS</span> <span class="o">=</span> <span class="k">$(</span>wildcard test/sql/*.sql<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">REGRESS</span> <span class="o">=</span> <span class="k">$(</span>patsubst test/sql/%.sql,%,<span class="k">$(</span>TESTS<span class="k">))</span>
</span></span><span class="line"><span class="cl"><span class="nv">REGRESS_OPTS</span> <span class="o">=</span> --inputdir<span class="o">=</span><span class="nb">test</span> --load-language<span class="o">=</span>plpgsql
</span></span><span class="line"><span class="cl"><span class="nv">MODULES</span> <span class="o">=</span> <span class="k">$(</span>patsubst %.c,%,<span class="k">$(</span>wildcard src/*.c<span class="k">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">PG_CONFIG</span> <span class="o">=</span> pg_config
</span></span><span class="line"><span class="cl"><span class="nv">PGXS</span> <span class="o">:=</span> <span class="k">$(</span>shell <span class="k">$(</span>PG_CONFIG<span class="k">)</span> --pgxs<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="err">include</span> <span class="k">$(</span><span class="nv">PGXS</span><span class="k">)</span>
</span></span></code></pre></div><p>The nice thing about this code is that it has nothing specific to a
distribution in it. It figures out what SQL files there are, what doc files
there are, and what C files need compiling by just looking in the <code>sql</code>,
<code>doc</code>, and <code>src</code> directories, respectively. It also specifies that tests are
in the <code>test</code> directory. About the only thing I&rsquo;ve customized here is adding
<code>--load-language=plpgsql</code> to <code>REGRESS_OPTS</code>, as the tests for the distribution
I&rsquo;ve copied this from require PL/pgSQL to run. Simple, and anyone can use it
with very little need to tweak it, as long as they don&rsquo;t mind storing their
files in the specified directories.</p>
<p>Today, I&rsquo;m updating my distributions to support PostgreSQL 9.1&rsquo;s new
<code>CREATE EXTENSION</code> syntax, but I want to continue supporting older versions of
PostgreSQL, as well. Basically, this means that the files listed in the <code>DATA</code>
variable vary based on the version of PostgreSQL you&rsquo;re installing against.
Here are the additional things the <code>Makefile</code> needs to do:</p>
<ul>
<li>If installing against PostgreSQL less than version 9.1, exclude files in
<code>DATA</code> that contain <code>--</code>. Such files are are migration scripts, which aren&rsquo;t
supported before 9.1.</li>
<li>If installing against PostgreSQL greater than or equal to 9.1:
<ul>
<li>Copy the files <code>sql/$EXTENSION.sql</code> and <code>sql/$EXTENSION--unpackaged.sql</code>
to <code>sql/$EXTENSION--$EXTVERSION.sql</code> and
<code>sql/$EXTENSION--npackated--$EXTVERSION.sql</code>, respectively.
<code>CREATE EXTENSION</code> requires that the version string be in migration file
name. I&rsquo;d rather not have to rename the file in my repository before every
release (and I&rsquo;d rather keep it without the version for  9.1 anyway), so
it needs to be copied.</li>
<li>Add the new <code>sql/$EXTENSION--$VERSION.sql</code> file to <code>EXTRA_CLEAN</code>.</li>
<li>Include only files in <code>DATA</code> that contain <code>--</code>. There&rsquo;s no need to install
the original file without the version number, or any uninstall file,
either, since it&rsquo;s not needed on 9.1 anymore.</li>
</ul>
</li>
</ul>
<p>I&rsquo;ve been trying to figure out how to modify my standard <code>Makefile</code> to support
these changes, without requiring a lot of tweaking, so that other folks can
easily use it in the future. Thanks to help from <a href="https://people.planetpostgresql.org/andrew/">Andrew Dunstan</a>, this is
what I&rsquo;ve come up with:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="nv">EXTENSION</span><span class="o">=</span>semver
</span></span><span class="line"><span class="cl"><span class="nv">EXTVERSION</span><span class="o">=</span>0.2.2
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">DATA</span> <span class="o">=</span> <span class="k">$(</span>filter-out <span class="k">$(</span>wildcard sql/*--*.sql<span class="k">)</span>,<span class="k">$(</span>wildcard sql/*.sql<span class="k">))</span>
</span></span><span class="line"><span class="cl"><span class="nv">DOCS</span> <span class="o">=</span> <span class="k">$(</span>wildcard doc/*.txt<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">TESTS</span> <span class="o">=</span> <span class="k">$(</span>wildcard test/sql/*.sql<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">REGRESS</span> <span class="o">=</span> <span class="k">$(</span>patsubst test/sql/%.sql,%,<span class="k">$(</span>TESTS<span class="k">))</span>
</span></span><span class="line"><span class="cl"><span class="nv">REGRESS_OPTS</span> <span class="o">=</span> --inputdir<span class="o">=</span><span class="nb">test</span> --load-language<span class="o">=</span>plpgsql
</span></span><span class="line"><span class="cl"><span class="nv">MODULES</span> <span class="o">=</span> <span class="k">$(</span>patsubst %.c,%,<span class="k">$(</span>wildcard src/*.c<span class="k">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">PG_CONFIG</span> <span class="o">=</span> pg_config
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">VERSION</span>     <span class="o">=</span> <span class="k">$(</span>shell <span class="k">$(</span>PG_CONFIG<span class="k">)</span> --version <span class="p">|</span> awk <span class="s1">&#39;{print $$2}&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">PGVER_MAJOR</span> <span class="o">=</span> <span class="k">$(</span>shell <span class="nb">echo</span> <span class="k">$(</span>VERSION<span class="k">)</span> <span class="p">|</span> awk -F. <span class="s1">&#39;{ print ($$1 + 0) }&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">PGVER_MINOR</span> <span class="o">=</span> <span class="k">$(</span>shell <span class="nb">echo</span> <span class="k">$(</span>VERSION<span class="k">)</span> <span class="p">|</span> awk -F. <span class="s1">&#39;{ print ($$2 + 0) }&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">ifeq</span> <span class="err">(</span><span class="k">$(</span><span class="nv">PGVER_MAJOR</span><span class="k">)</span><span class="err">,</span> <span class="err">9)</span>
</span></span><span class="line"><span class="cl"><span class="err">ifneq</span> <span class="err">(</span><span class="k">$(</span><span class="nv">PGVER_MINOR</span><span class="k">)</span><span class="err">,</span> <span class="err">0)</span>
</span></span><span class="line"><span class="cl"><span class="nf">all</span><span class="o">:</span> <span class="n">sql</span>/<span class="k">$(</span><span class="nv">EXTENSION</span><span class="k">)</span>--<span class="k">$(</span><span class="nv">EXTVERSION</span><span class="k">)</span>.<span class="n">sql</span> <span class="n">sql</span>/<span class="k">$(</span><span class="nv">EXTENSION</span><span class="k">)</span>--<span class="n">unpackaged</span>--<span class="k">$(</span><span class="nv">EXTVERSION</span><span class="k">)</span>.<span class="n">sql</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">sql/$(EXTENSION)--$(EXTVERSION).sql</span><span class="o">:</span> <span class="n">sql</span>/<span class="k">$(</span><span class="nv">EXTENSION</span><span class="k">)</span>.<span class="n">sql</span>
</span></span><span class="line"><span class="cl">    cp $&lt; <span class="nv">$@</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nf">sql/$(EXTENSION)--unpackaged--$(EXTVERSION).sql</span><span class="o">:</span> <span class="n">sql</span>/<span class="k">$(</span><span class="nv">EXTENSION</span><span class="k">)</span>--<span class="n">unpackaged</span>.<span class="n">sql</span>
</span></span><span class="line"><span class="cl">    cp $&lt; <span class="nv">$@</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">DATA</span> <span class="o">=</span> <span class="k">$(</span>filter-out sql/<span class="k">$(</span>EXTENSION<span class="k">)</span>--unpackaged.sql,<span class="k">$(</span>wildcard sql/*--*.sql<span class="k">))</span> sql/<span class="k">$(</span>EXTENSION<span class="k">)</span>--<span class="k">$(</span>EXTVERSION<span class="k">)</span>.sql
</span></span><span class="line"><span class="cl"><span class="nv">EXTRA_CLEAN</span> <span class="o">=</span> sql/<span class="k">$(</span>EXTENSION<span class="k">)</span>--<span class="k">$(</span>EXTVERSION<span class="k">)</span>.sql sql/<span class="k">$(</span>EXTENSION<span class="k">)</span>--unpackaged--<span class="k">$(</span>EXTVERSION<span class="k">)</span>.sql
</span></span><span class="line"><span class="cl"><span class="err">endif</span>
</span></span><span class="line"><span class="cl"><span class="err">endif</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">PGXS</span> <span class="o">:=</span> <span class="k">$(</span>shell <span class="k">$(</span>PG_CONFIG<span class="k">)</span> --pgxs<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="err">include</span> <span class="k">$(</span><span class="nv">PGXS</span><span class="k">)</span>
</span></span></code></pre></div><p>This is not exactly ideal, but not <em>too</em> bad. It&rsquo;s not quite the drop-in
version we had before, because now the first line needs to name the extension
we&rsquo;re distributing, and the second needs to specify the version (and would
then need to be updated for every release). Maybe they could be read from the
control file somehow? Other than that, you should be able to just forget the
rest of the file (mostly). Here&rsquo;s how it addresses the above requirements:</p>
<ul>
<li>To exclude files with <code>--</code> in them on  9.1, the first <code>DATA</code> line filters
them out:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="nv">DATA</span> <span class="o">=</span> <span class="k">$(</span>filter-out <span class="k">$(</span>wildcard sql/*--*.sql<span class="k">)</span>,<span class="k">$(</span>wildcard sql/*.sql<span class="k">))</span>
</span></span></code></pre></div><ul>
<li>
<p>Next, we need to know if we&rsquo;re on 9.1 or higher. So we use
<code>pg_config --version</code> to get the version number and some <code>awk</code> stuff to get
the major and minor parts. Then, if the major version is 9 and the minor is
<em>not</em> 0, we:</p>
<ul>
<li>Add <code>sql/$(EXTENSION)--$(EXTVERSION).sql</code> and
<code>sql/$(EXTENSION)--unpackaged--$(EXTVERSION).sql</code> as dependencies of the
<code>all</code> rule (which is the default PGXS rule).</li>
<li>Add the <code>sql/$(EXTENSION)--$(EXTVERSION).sql</code> and
<code>sql/$(EXTENSION)--unpackated--$(EXTVERSION).sql</code> rules, which copy
<code>sql/$(EXTVERVERSION).sql</code> and <code>sql/$(EXTVERVERSION)--unpackaged.sql</code>
files. Of course this assumes that such files exist.</li>
<li>Add <code>sql/$(EXTENSION)--$(EXTVERSION).sql</code> and
<code>sql/$(EXTENSION)--unpackaged--$(EXTVERSION).sql</code> to <code>EXTRA_CLEAN</code>, so
that they&rsquo;ll be deleted by <code>make clean</code>.</li>
<li>Set <code>DATA</code> again, this time to include <em>only</em> files with <code>--</code> in them,
except for <code>sql/$(EXTENSION)--unpackaged.sql</code>.</li>
</ul>
</li>
</ul>
<p>And with that, it works. But it has some disadvantages over the previous, very
simple <code>Makefile</code> I&rsquo;ve been using up to now:</p>
<ul>
<li>One must modify it for every release, if only to set <code>EXTVERSION</code>.</li>
<li>It relies more on Unix tools than previously, specifically <code>awk</code>. I&rsquo;m not
sure how big a deal this is in practice; the <a href="https://github.com/theory/pgtap/blob/master/Makefile">pgTAP Makefile</a> has been
relying on <code>awk</code> an even worse gymnastics for some time and no one has
complained.</li>
<li>It&rsquo;s not perfectly future-proof. It will need to be modified when PostgreSQL
10.0.0 is released, and again when 11.0.0 is released.</li>
<li>It won&rsquo;t handle a distribution with multiple extension in it. I don&rsquo;t even
want to think about that.</li>
</ul>
<p>Suggestions for ways to eliminate these shortcomings would be greatly
appreciated, especially if it allows use extension authors to get back to
something as simple as the first example at the top of this post.</p>
<p><strong>UPDATE:</strong> Added the <code>unpackaged</code> bits I didn&rsquo;t realize I needed until after
I&rsquo;d released a new version of <a href="https://pgxn.org/dist/semver/">semver</a> and discovered that the &ldquo;unpackaged&rdquo;
script needs to always be tied to the default version.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/4601750614</id><title type="html">It&amp;rsquo;s Finally Up: The New PGXN Site!</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/new-pgxn-site/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-04-14T06:32:06Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="site" label="Site"/><category scheme="https://blog.pgxn.org/tags" term="launch" label="Launch"/><category scheme="https://blog.pgxn.org/tags" term="final" label="Final"/><category scheme="https://blog.pgxn.org/tags" term="awesome" label="Awesome"/><category scheme="https://blog.pgxn.org/tags" term="api" label="API"/><summary type="html"><![CDATA[<p>I&rsquo;m pleased to announce that the new <a href="https://pgxn.org/">PGXN SITE</a> went live last night. Some of
the things it does:</p>
<ul>
<li>Full text search of all documentation, distribution metadata and <code>README</code>s,
extensions metadata, and users</li>
<li>list of the 56 most recent uploads</li>
<li>Find users by first letter of nickname (mostly so search spiders can drill
down to all site content)</li>
<li>A tag cloud with the 56 most commonly-used tags (they&rsquo;re all the same size
and color right now because each one is used only once at the moment)</li>
<li>Pages for users (<a href="https://pgxn.org/user/alexk">example</a>)</li>
<li>Pages for distributions, with inlined <code>README</code> (<a href="https://pgxn.org/dist/explanation/">example</a>)</li>
<li>Browse unzipped distributions (<a href="https://api.pgxn.org/src/semver/semver-0.2.0/">example</a>)</li>
<li>Pages for documentation (<a href="https://pgxn.org/dist/explanation/doc/explanation.html">example</a>)</li>
<li>Permalinks for extensions <a href="https://pgxn.org/extension/pair">example</a>)</li>
</ul>
<p>The entire site is backed by the <a href="https://api.pgxn.org/">API Server</a>, which is mainly composed of
static JSON and HTML files read by the site back end from the local file
system. So the site is quite fast. Please browse around, let me know what you
think, and please <a href="https://github.com/pgxn/pgxn-site/issues">report any bugs</a> you find.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;m pleased to announce that the new <a href="https://pgxn.org/">PGXN SITE</a> went live last night. Some of
the things it does:</p>
<ul>
<li>Full text search of all documentation, distribution metadata and <code>README</code>s,
extensions metadata, and users</li>
<li>list of the 56 most recent uploads</li>
<li>Find users by first letter of nickname (mostly so search spiders can drill
down to all site content)</li>
<li>A tag cloud with the 56 most commonly-used tags (they&rsquo;re all the same size
and color right now because each one is used only once at the moment)</li>
<li>Pages for users (<a href="https://pgxn.org/user/alexk">example</a>)</li>
<li>Pages for distributions, with inlined <code>README</code> (<a href="https://pgxn.org/dist/explanation/">example</a>)</li>
<li>Browse unzipped distributions (<a href="https://api.pgxn.org/src/semver/semver-0.2.0/">example</a>)</li>
<li>Pages for documentation (<a href="https://pgxn.org/dist/explanation/doc/explanation.html">example</a>)</li>
<li>Permalinks for extensions <a href="https://pgxn.org/extension/pair">example</a>)</li>
</ul>
<p>The entire site is backed by the <a href="https://api.pgxn.org/">API Server</a>, which is mainly composed of
static JSON and HTML files read by the site back end from the local file
system. So the site is quite fast. Please browse around, let me know what you
think, and please <a href="https://github.com/pgxn/pgxn-site/issues">report any bugs</a> you find.</p>
<p>There are a few more things left to do on my list:</p>
<ul>
<li>Add pages for users who don&rsquo;t yet have distributions on the network.</li>
<li>Convert the localization interface to <code>gettext</code> and solicit translations.</li>
<li>Document the API; might lead to some changes as I run across
inconsistencies.</li>
<li>Add an Atom feed of recent releases.</li>
<li>Add aliases for <code>$nickname@pgxn.org</code> addresses, to forward to personal
addresses; this way we can just publish pgxn.org addresses.</li>
<li>Write documentation on how to optimize distributions for optimal exposure on
PGXN.</li>
<li>Consider changing the Permalink URL to something shorter. Suggestions?</li>
</ul>
<p>If you&rsquo;d liked to help out with any of these tasks, just let me know. Better
yet, grab the source from <a href="https://github.org/pgxn/">GitHub</a> and just hack!</p>
<p>Oh, and speaking of the source, I would <em>really</em> appreciate a code review or
four. Although I have solicited advice on key questions fro people more
knowledgeable than I (thanks Aristotle, Miyagawa, Andreas, Graham), the code
is entirely my own. More eyes will be a huge help!</p>
<p>Tomorrow I&rsquo;ll blog a bit about the architecture for the network. I&rsquo;m quite
happy with it.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/4252690755</id><title type="html">Thoughts on Content Localization</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/content-localization/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-04-01T05:33:16Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="localization" label="Localization"/><category scheme="https://blog.pgxn.org/tags" term="internationalization" label="Internationalization"/><category scheme="https://blog.pgxn.org/tags" term="translation" label="Translation"/><category scheme="https://blog.pgxn.org/tags" term="localemaketext" label="Locale::Maketext"/><summary type="html"><![CDATA[<p>The new site is coming along <em>very</em> nicely. I&rsquo;m really excited about it. The
addition of the API server has turned out to be an inspiration. I never
realized how clean the separation between content and presentation could be
until I did this &ndash; even though I&rsquo;ve preached that very separation for years
in one of my <a href="https://www.kineticode.com/">day jobs</a>.</p>
<p>But speaking of that separation, I am running into a bit of an annoyance with
content specific to the site. All the content from the network is working
beautifully &ndash; I just fetch it from the API server. But the site has its own
content page independent of the network, including an FAQ, a page on setting
up a mirror, a list of our backers, etc. And the ugly bit has to do with
localization.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>The new site is coming along <em>very</em> nicely. I&rsquo;m really excited about it. The
addition of the API server has turned out to be an inspiration. I never
realized how clean the separation between content and presentation could be
until I did this &ndash; even though I&rsquo;ve preached that very separation for years
in one of my <a href="https://www.kineticode.com/">day jobs</a>.</p>
<p>But speaking of that separation, I am running into a bit of an annoyance with
content specific to the site. All the content from the network is working
beautifully &ndash; I just fetch it from the API server. But the site has its own
content page independent of the network, including an FAQ, a page on setting
up a mirror, a list of our backers, etc. And the ugly bit has to do with
localization.</p>
<p>I&rsquo;ve built the site (and <a href="https://manager.pgxn.org/">PGXN Manager</a>) using the Perl standard localization
library <a href="https://search.cpan.org/perldoc?Locale::Maketext">Locale::Maketext</a>. And it works great for short labels and such, like
so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-perl" data-lang="perl"><span class="line"><span class="cl"><span class="k">our</span> <span class="nv">%Lexicon</span> <span class="o">=</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#39;hometitle&#39;</span> <span class="o">=&gt;</span> <span class="s">&#39;PGXN: PostgreSQL Extension Network&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#39;PostgreSQL Extension Network&#39;</span> <span class="o">=&gt;</span> <span class="s">&#39;PostgreSQL Extension Network&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#39;About PGXN&#39;</span> <span class="o">=&gt;</span> <span class="s">&#39;About PGXN&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#39;User&#39;</span> <span class="o">=&gt;</span> <span class="s">&#39;User&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#39;Recent Uploads&#39;</span> <span class="o">=&gt;</span> <span class="s">&#39;Recent Uploads&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#39;Blog&#39;</span> <span class="o">=&gt;</span> <span class="s">&#39;Blog&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#39;Frequently Asked Questions&#39;</span> <span class="o">=&gt;</span> <span class="s">&#39;Frequently Asked Questions&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#39;Release It&#39;</span> <span class="o">=&gt;</span> <span class="s">&#39;Release It&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span></code></pre></div><p>Translators just have to copy this code into a subclass and change the strings
on the right-hand side of the <code>=&gt;</code>s. Easy, right? It&rsquo;s also very good with
variables and pluralization. I just use it in the templates like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-perl" data-lang="perl"><span class="line"><span class="cl"><span class="n">title</span> <span class="p">{</span> <span class="n">T</span> <span class="s">&#39;PostgreSQL Extension Network&#39;</span> <span class="p">};</span>
</span></span></code></pre></div><p>What sucks, however, is when I need to have longer content pages, especially
those that mix HTML in with the text. The FAQ is particularly relevant here:
most of the answers have at least one link embedded in them. The trouble is,
templating language encodes HTML entities. So I can&rsquo;t easily put HTML in the
localization files, unless I put it in raw, and the output it raw. So the
localization file might have a line like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-perl" data-lang="perl"><span class="line"><span class="cl"><span class="s">&#39;Send us an &lt;a href=&#34;mailto:pgxn@example.com&#34;&gt;email&lt;/a&gt; with your questions&#39;</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl"><span class="s">&#39;Send us an &lt;a href=&#34;mailto:pgxn@example.com&#34;&gt;email&lt;/a&gt; with your questions&#39;</span><span class="p">,</span>
</span></span></code></pre></div><p>And then the template would be told to output the text raw, without encoding
HTML entities:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-perl" data-lang="perl"><span class="line"><span class="cl"><span class="n">p</span> <span class="p">{</span> <span class="n">outs_raw</span> <span class="n">T</span> <span class="s">&#39;Send us an &lt;a href=&#34;mailto:pgxn@example.com&#34;&gt;email&lt;/a&gt; with your questions&#39;</span> <span class="p">};</span>
</span></span></code></pre></div><p>So now we&rsquo;ve lost the benefit of the templating, really: we have to write the
raw HTML ourselves. And besides, for multi-paragraph documents, it gets
annoying to have a bunch of localization lines with names like
<code>about_paragraph_1</code>, <code>about_paragraph_2</code>, etc. That makes it additionally
difficult for translators. There&rsquo;s just too much crap getting in the way. I&rsquo;ve
always liked the idea of having all translations in one place, but I don&rsquo;t
think there&rsquo;s enough of an advantage to that to overcome the annoyances.</p>
<p>So I&rsquo;d like to find a better way to manage these content pages more like
<em>documents,</em> in a format that&rsquo;s easy for translators to manage and isn&rsquo;t so
broken up. I&rsquo;m open to suggestions. Ideally the content would still be all
kept in one place for a given language, but maybe that&rsquo;s just not feasible.</p>
<p>The one thing that occurs to me is to keep documents for given languages in
separate <a href="https://daringfireball.net/projects/markdown/">Markdown</a>-formatted files. Each language would have a directory with
the language code (&ldquo;en&rdquo;, &ldquo;en-uk&rdquo;, &ldquo;fr&rdquo;, etc.), and then at build time they
would be converted to HTML for fast serving at run-time. Or maybe at
distribution-packaging time. I&rsquo;ve resisted something like this for a while
because it means one has to find stuff to translate in two different places
(the localization library for UI elements and the directory of documents for
content), but maybe it would be best.</p>
<p>How have you solved this problem in your apps? How badly have you annoyed your
translators?</p>
<p>Oh, while I&rsquo;m thinking about it, where should documentation for the API server
be published? I could include it in the <a href="https://github.com/theory/pgxn-api/">PGXN::API</a> source, which would be
useful for anyone else who wanted to run an API server (which will be dead
easy, by the way). But there will also be some stuff that&rsquo;s specific to a
given installation (proxying, downtime, etc.). So maybe it should go on the
main site or something? Or perhaps we need a dedicated wiki server for shit
like this (and some of those content pages like the FAQ, maybe).</p>
<p>Thoughts?</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/4018670551</id><title type="html">Thoughts on Indexing and Documentation</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/indexing-docs/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-03-22T05:13:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="indexing" label="Indexing"/><category scheme="https://blog.pgxn.org/tags" term="full-text-search" label="Full Text Search"/><category scheme="https://blog.pgxn.org/tags" term="readme" label="README"/><category scheme="https://blog.pgxn.org/tags" term="documentation" label="Documentation"/><category scheme="https://blog.pgxn.org/tags" term="search-results" label="Search Results"/><category scheme="https://blog.pgxn.org/tags" term="search" label="Search"/><summary type="html"><![CDATA[<p>So I&rsquo;m designing the full text indexing for the PGXN search site. I&rsquo;m modeling
it on <a href="https://http//search.cpan.org">CPAN Search</a>, which has been great. There are four search options:</p>
<ul>
<li>Full documentation search. This is the most common. Includes doc title and
body.</li>
<li>User search. Search on names, nicknames, email addresses, URIs, etc.</li>
<li>Distribution search. Search on distribution name, abstract, description,
tags, and the README.</li>
<li>Extension search. Search on extension name and abstract.</li>
<li>Tag search. Search on tag name only.</li>
</ul>
<p>The documentation search is the one I&rsquo;m perhaps least sure about. It assumes
that each extension in a distribution will have documentation. But so far that
has not really been the practice for PostgreSQL extensions. Most folks seem to
stick the documentation in the README. And even then it can be <a href="https://master.pgxn.org/dist/countnulls/1.0.0/README.txt">almost
nothing</a>. So a search for &ldquo;count nulls&rdquo; probably would not find &ldquo;countnulls&rdquo;
extension, because there is no documentation. What should I do about this? I&rsquo;m
thinking one of:</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>So I&rsquo;m designing the full text indexing for the PGXN search site. I&rsquo;m modeling
it on <a href="https://http//search.cpan.org">CPAN Search</a>, which has been great. There are four search options:</p>
<ul>
<li>Full documentation search. This is the most common. Includes doc title and
body.</li>
<li>User search. Search on names, nicknames, email addresses, URIs, etc.</li>
<li>Distribution search. Search on distribution name, abstract, description,
tags, and the README.</li>
<li>Extension search. Search on extension name and abstract.</li>
<li>Tag search. Search on tag name only.</li>
</ul>
<p>The documentation search is the one I&rsquo;m perhaps least sure about. It assumes
that each extension in a distribution will have documentation. But so far that
has not really been the practice for PostgreSQL extensions. Most folks seem to
stick the documentation in the README. And even then it can be <a href="https://master.pgxn.org/dist/countnulls/1.0.0/README.txt">almost
nothing</a>. So a search for &ldquo;count nulls&rdquo; probably would not find &ldquo;countnulls&rdquo;
extension, because there is no documentation. What should I do about this? I&rsquo;m
thinking one of:</p>
<ul>
<li>
<p>Encourage folks to write documentation. I&rsquo;m going to do this anyway, because
the docs will really help the visibility of an extension on the site. It
looks <a href="https://theory.github.com/pgxn/pgtap.html">like this</a>. If you have no docs for an extension, your extension will
not appear in the search results (or perhaps it might, but link to the
distribution).</p>
</li>
<li>
<p>If there is no documentation for an extension in a distribution, index the
README as the documentation. I&rsquo;m not really keen on this idea, because the
README should describe the distribution, how to install it, etc. I&rsquo;m
planning to use it in the distribution-specific index. Documentation of the
extension should be more about how the extension works, what it&rsquo;s interface
is, etc. Or so it seems to me, at least (I&rsquo;m admittedly biased to this
practice among CPAN modules). But at least with this approach there would be
a link to &ldquo;documentation&rdquo; for an extension on the search site.</p>
</li>
</ul>
<p>Erm, not really thinking of any other options. I feel pretty strongly that
folks should write docs for their extensions, as much as possible, and I&rsquo;ve
set things up so that, from PGXN&rsquo;s point of view, at least, you can write
documentation in whatever format you like (assuming the format is supported by
or added to <a href="https://search.cpan.org/perldoc?Text::Markup">Text::Markup</a>), as long as they&rsquo;re in a <code>doc/</code> or <code>docs</code>
directory. I want it to be as easy as possible. But I also want there to be
decent search results ASAP.</p>
<p>Comments?</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/3641963548</id><title type="html">Question: Ajax and Search Engine Indexing</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/ajax-search-indexing/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-03-04T20:20:08Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="ajax" label="Ajax"/><category scheme="https://blog.pgxn.org/tags" term="api" label="API"/><category scheme="https://blog.pgxn.org/tags" term="search-engine" label="Search Engine"/><category scheme="https://blog.pgxn.org/tags" term="indexing" label="Indexing"/><category scheme="https://blog.pgxn.org/tags" term="indexability" label="Indexability"/><category scheme="https://blog.pgxn.org/tags" term="response-code" label="Response Code"/><category scheme="https://blog.pgxn.org/tags" term="not-found" label="Not Found"/><summary type="html"><![CDATA[I&rsquo;ve started working on the main (search) site in earnest now. The basic
layout is done, and I&rsquo;m working on the distribution view (<a href="https://theory.github.com/pgxn/pgtapdist.html">mockup</a>). My
thinking so far has been that I would simply serve a page that requested, say,
<code>/dist/pgTAP/</code>, and that page would use Ajax requests to fetch the data from
the <a href="https://api.pgxn.org/">API server</a> and display stuff. I think this will work pretty well except
for one thing: 404s.]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;ve started working on the main (search) site in earnest now. The basic
layout is done, and I&rsquo;m working on the distribution view (<a href="https://theory.github.com/pgxn/pgtapdist.html">mockup</a>). My
thinking so far has been that I would simply serve a page that requested, say,
<code>/dist/pgTAP/</code>, and that page would use Ajax requests to fetch the data from
the <a href="https://api.pgxn.org/">API server</a> and display stuff. I think this will work pretty well except
for one thing: 404s.</p>
<p>That is, if you request <code>/dist/nonexistent/</code>, then it will load a page with
the HTTP status code <code>200 OK</code>, but then, when the Ajax request 404s, it will
show a &ldquo;Not found&rdquo; error message. That&rsquo;s all well and good, but I&rsquo;m wondering
about the impact of two things:</p>
<ol>
<li>
<p>Since the page itself won&rsquo;t 404, search engines might index links to
nonexistent extensions. Of course, bad links won&rsquo;t be <em>that</em> common, but
of course they do happen and then tend to live forever.</p>
</li>
<li>
<p>If the search site uses Ajax to fetch the contents of a page via JSON (or,
for documentation, as an HTML document it will put into a div), will the
full content be properly indexed by search engines?</p>
</li>
</ol>
<p>So these are serious questions, in my mind. Do we loose good search engine
indelibility when we load content dynamically?</p>
<p>Of course, I can instead write it so that the back end fetches stuff from the
API server (and perhaps directly from the file system) and get &lsquo;round these
issues, but then it&rsquo;s less of a cool example of the use of the API server.</p>
<p>What do you think? Good advice much appreciated!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/3099288750</id><title type="html">PGXN API RFC</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2011/pgxn-api-rfc/"/><updated>2026-10-07T16:13:48Z</updated><published>2011-02-04T04:05:33Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="search" label="Search"/><category scheme="https://blog.pgxn.org/tags" term="api" label="API"/><category scheme="https://blog.pgxn.org/tags" term="mirror" label="Mirror"/><category scheme="https://blog.pgxn.org/tags" term="json" label="JSON"/><category scheme="https://blog.pgxn.org/tags" term="cpan" label="CPAN"/><category scheme="https://blog.pgxn.org/tags" term="metacpan" label="metacpan"/><category scheme="https://blog.pgxn.org/tags" term="javascript" label="JavaScript"/><category scheme="https://blog.pgxn.org/tags" term="application" label="Application"/><summary type="html"><![CDATA[<p>Things slowed up a bit over the last couple of months, I admit. There are any
number of reasons for that, not the least were the intrusion of the holidays
and a <a href="https://www.designsceneapp.com/">little side project</a> I&rsquo;ve been hacking on after-hours (and sometimes
during-hours). But I&rsquo;m ramping things up again now and need <em>your</em> feedback on
my current plans. Here&rsquo;s what I&rsquo;m working on: the search site.</p>
<h2 id="search-sites-and-apis">Search Sites and APIs</h2>
<p>Well, sort of. First of all, I&rsquo;ve decided that the &ldquo;search site&rdquo; should not be
a separate thing. The <a href="https://pgxn.org/">main site</a> will be the search site. This is following
the example of <a href="https://openjsan.org">JSAN</a>, as well as feedback from <a href="https://search.cpan.org/~gbarr/">Graham Barr</a>, who created and
maintains <a href="https://search.cpan.org/">CPAN Search</a>. Apparently people are often confused that
<a href="https://search.cpan.org/">search.cpan.org</a> is separate from <a href="https://www.cpan.org">www.cpan.org</a>. No point in
adding in confusion from the beginning. And besides, now that the PGXN
fund-raising <a href="https://blog.pgxn.org/post/2063677299/goooooooaaaal">is over</a>, I don&rsquo;t know what else would go on the home page.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Things slowed up a bit over the last couple of months, I admit. There are any
number of reasons for that, not the least were the intrusion of the holidays
and a <a href="https://www.designsceneapp.com/">little side project</a> I&rsquo;ve been hacking on after-hours (and sometimes
during-hours). But I&rsquo;m ramping things up again now and need <em>your</em> feedback on
my current plans. Here&rsquo;s what I&rsquo;m working on: the search site.</p>
<h2 id="search-sites-and-apis">Search Sites and APIs</h2>
<p>Well, sort of. First of all, I&rsquo;ve decided that the &ldquo;search site&rdquo; should not be
a separate thing. The <a href="https://pgxn.org/">main site</a> will be the search site. This is following
the example of <a href="https://openjsan.org">JSAN</a>, as well as feedback from <a href="https://search.cpan.org/~gbarr/">Graham Barr</a>, who created and
maintains <a href="https://search.cpan.org/">CPAN Search</a>. Apparently people are often confused that
<a href="https://search.cpan.org/">search.cpan.org</a> is separate from <a href="https://www.cpan.org">www.cpan.org</a>. No point in
adding in confusion from the beginning. And besides, now that the PGXN
fund-raising <a href="https://blog.pgxn.org/post/2063677299/goooooooaaaal">is over</a>, I don&rsquo;t know what else would go on the home page.</p>
<p>The other thing that&rsquo;s happened is, just as I was getting my butt in gear on
this stuff, a new CPAN search site came to my attention, <a href="https://search.metacpan.org/">μετα CPAN</a>. This is
an interesting project. What they did instead of creating a monolithic HTML
search site is to create a <a href="https://github.com/CPAN-API/cpan-api/wiki/API-docs">simple API</a> that serves nothing but JSON. It has
search and displays metadata for CPAN objects (distributions, maintainers,
modules, etc.). The search site, then, is not really a site at all, but a pure
JavaScript application. Once you load it, it just uses the API server to get
all the data. There are a few tricks server-side to proxy the API server so as
to avoid cross-site scripting issues. But otherwise it just works in the
browser.</p>
<p>Now I&rsquo;m not sure I&rsquo;ll do the same thing, exactly, but there&rsquo;s a lot of appeal
in creating a RESTful API server that&rsquo;s independent of the search site, and
then building the search site to use it. It also has the advantage of being
useful for other projects to just use. Want to create a PGXN search widget for
your blog? Yeah, there&rsquo;s an API for that.</p>
<h2 id="a-super-restful-directory">A Super RESTful Directory</h2>
<p>Of course, thanks to the &ldquo;RESTful Directory&rdquo; design for the mirrors (described
<a href="https://blog.pgxn.org/post/988613682/restful-directory" title="A RESTful Directory">here</a> and revised <a href="https://blog.pgxn.org/post/1138292188/arch-and-extension-json" title="Architecture, New Extension JSON Format">here</a>), any mirror is a lightweight API already.
There&rsquo;s a <em>lot</em> of metadata one can get just from the static JSON files it
generates. The design is flexible&ndash;but designed with a command-line client in
mind. As such, many commands executed in a command-line client would likely
requires multiple requests to a mirror. For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">&gt;</span> install pgtap
</span></span></code></pre></div><p>This would request <code>/by/extension/pgtap.json</code> from the server. It would then
parse that file and see that the latest stable version of pgTAP is in the
distribution &ldquo;pgTAP&rdquo; at version &ldquo;0.25.0&rdquo;. So it would then download
<code>/dist/pgTAP-0.25.0.pgz</code> to install.</p>
<p>This is great for a command-line client, but wouldn&rsquo;t be so great for a search
site to be responsive. Ideally, a site should send a single request to get all
the data it needs for a particular page.</p>
<p>So here&rsquo;s what I&rsquo;m thinking for a PGXN API server: It will offer a superset of
the functionality of any other PGXN mirror. That is, all the JSON files in a
mirror will be present, but many of them will have more information than they
would on other mirrors. And then, of course, there will be other URIs to offer
additional API calls.</p>
<h3 id="details">Details</h3>
<p>So what does that look like? Let&rsquo;s take the pgTAP distribution, which I
released on PGXN earlier this week. To find the pgTAP distribution, one
requests:</p>
  <blockquote>
    <p><a href="https://master.pgxn.org/by/dist/pgTAP.json"><code>/by/dist/pgTAP.json</code></a></p>
  </blockquote>
<p>From that, one can see that the latest table release is 0.25.0, and so one can
then request</p>
  <blockquote>
    <p><a href="https://master.pgxn.org/dist/pgTAP/pgTAP-0.25.0.json"><code>/dist/pgTAP/pgTAP-0.25.0.json</code></a></p>
  </blockquote>
<p>to get all the metadata for that particular release. What I propose, to avoid
the two requests, is to include the contents of the second file in the first.
That would then have all the data necessary to generate the <a href="https://theory.github.com/pgxn/pgtapdist.html">pgTAP
distribution page</a> on the PGXN site.</p>
<p>The API would offer similar supersets of data for the <a href="https://master.pgxn.org/by/extension/pgtap.json">extension</a> , <a href="https://master.pgxn.org/by/owner/theory.json">owner</a> ,
and <a href="https://master.pgxn.org/by/tag/testing.json">tag</a> metadata files, to have the data necessary for the design of the
corresponding <a href="https://theory.github.com/pgxn/pgtap.html">extension</a>, <a href="https://theory.github.com/pgxn/theory.html">owner</a> and tag layouts of the site.</p>
<h3 id="additional-resources">Additional Resources</h3>
<p>In addition to adding metadata to the existing mirrored JSON files, there
would be other resources available for request from the API server. They would
include:</p>
<ul>
<li>
<p>Extension Documentation. Each distribution may include documentation for
included extensions in the <code>doc</code> subdirectory. These will go under the
directory for a specific distribution such as
<code>/dist/pgTAP/pgTAP-0.35.0/doc/pgtap.html</code>. The latest version of each
document would also be available under <code>/by/extension</code>, as in
<code>/by/extension/pgtap.html</code>. This requires that the documentation file have
the same base name as the extension file itself.</p>
</li>
<li>
<p>Other documentation. I&rsquo;d like to support arbitrary documentation, such as
for included binary executables, HOWTOs, etc. The canonical copies will go
under the versioned distribution URL, of course, but I&rsquo;m not sure about
permalinks. That might require an extension of the <a href="https://pgxn.org/meta/spec.txt">Meta Spec</a>; I haven&rsquo;t
quite figured that out, yet.</p>
</li>
<li>
<p>Source code. There will be an interface to browse an unpacked copy of any
distribution as plain text. This will be under <code>/src</code>, as in
<code>/src/pgTAP/pgTAP-0.35.0/</code>.</p>
</li>
</ul>
<h3 id="search-api">Search API</h3>
<p>Of course. This is the big one, really. I think it makes sense to have the
<code>/by</code> URI respond to search requests. Thus, a request for</p>
<pre tabindex="0"><code>/by?q=testing
</code></pre><p>would search everything. If you only want to search a certain category of
object, you&rsquo;d hit the appropriate URI:</p>
<pre tabindex="0"><code>/by/dist?q=tap
/by/owner?q=clochard
/by/tag?q=test
/by/extension?q=gis
</code></pre><p>The nice thing about this is that it retains the existing entity URLs. The
directory level determines which entities you get.</p>
<h2 id="your-thoughts">Your Thoughts?</h2>
<p>So that&rsquo;s my thinking on the search API. I&rsquo;m going to start hacking on it in
earnest tomorrow, and perhaps next week I can get a very early version out
(basically just another mirror to start with).</p>
<p>But what do you think? Seem like a sane approach? Am I missing anything
obvious or doing anything clearly stupid? Please let me know in the comments!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/2063677299</id><title type="html">Goooooooaaaal!</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/goooooooaaaal/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-12-01T22:39:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="fundraising" label="Fundraising"/><category scheme="https://blog.pgxn.org/tags" term="goal" label="Goal"/><category scheme="https://blog.pgxn.org/tags" term="contribution" label="Contribution"/><category scheme="https://blog.pgxn.org/tags" term="enova" label="Enova"/><category scheme="https://blog.pgxn.org/tags" term="myyearbook" label="myYearbook"/><category scheme="https://blog.pgxn.org/tags" term="thanks" label="Thanks"/><category scheme="https://blog.pgxn.org/tags" term="thermometer" label="Thermometer"/><summary type="html"><![CDATA[<p>I&rsquo;m pleased to announce that, thanks to a pledge from <a href="https://www.enovafinancial.com/">Enova Financial</a> at the
newly-minted &ldquo;Patron&rdquo; level, we have achieved our fundraising goal of
$25,000. Thanks to Jim &ldquo;Decibel!&rdquo; Nasby for putting together the contribution
that put us over the top!</p>
<p>I&rsquo;m thrilled to find that this approach to getting a project going actually
works. I&rsquo;ve had the idea for PGXN for a long time, but knew that I was never
going to be able to make it happen unless I could get help. So putting things
together, writing <a href="https://wiki.postgresql.org/wiki/PGXN">a spec</a> and <a href="https://pgxn.org/status.html">project plan</a>, and putting together the
<a href="https://pgxn.org/">fundraising site</a> with a prominent &ldquo;fundraising thermometer&rdquo; and contribution
levels&hellip;well, it really paid off.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;m pleased to announce that, thanks to a pledge from <a href="https://www.enovafinancial.com/">Enova Financial</a> at the
newly-minted &ldquo;Patron&rdquo; level, we have achieved our fundraising goal of
$25,000. Thanks to Jim &ldquo;Decibel!&rdquo; Nasby for putting together the contribution
that put us over the top!</p>
<p>I&rsquo;m thrilled to find that this approach to getting a project going actually
works. I&rsquo;ve had the idea for PGXN for a long time, but knew that I was never
going to be able to make it happen unless I could get help. So putting things
together, writing <a href="https://wiki.postgresql.org/wiki/PGXN">a spec</a> and <a href="https://pgxn.org/status.html">project plan</a>, and putting together the
<a href="https://pgxn.org/">fundraising site</a> with a prominent &ldquo;fundraising thermometer&rdquo; and contribution
levels&hellip;well, it really paid off.</p>
<p>So for others of you with ideas of projects you&rsquo;d like to take on, but don&rsquo;t
have the tuits without financial assistance, take heed! With a bit of work
up-front and a the design of some compelling rewards for contributors &ndash; as
well as a simple way for folks to donate (perhaps use <a href="https://www.kickstarter.com/">Kickstarter</a> or
<a href="https://www.fossexperts.com/">FOSSExperts</a>)&ndash; you too can make your dream a reality. Well, that and
personally seeking out potential funders and lobbying them. It takes some
doing, but the payoff is well worth it.</p>
<p>And the payoff here is that I&rsquo;ll actually be able to make PGXN happen. We
already have the <a href="https://manager.pgxn.org/">upload site</a> up and running (with more features in the
works), and now we have the funds for me to make the <a href="https://theory.github.com/pgxn/">final design</a> become a
reality, as well as the <a href="https://wiki.postgresql.org/wiki/PGXN#PGXN_Client">command-line client</a>.</p>
<p>And it was made possible by everyone who has supported us:</p>
<ul>
<li><a href="https://www.myyearbook.com/">myYearbook</a></li>
<li><a href="https://www.pgexperts.com/">PostgreSQL Experts, Inc.</a></li>
<li><a href="https://www.dalibo.org/en/">Dalibo</a></li>
<li><a href="https://www.enovafinancial.com/">Enova</a></li>
<li><a href="https://www.etsy.com/">Etsy</a></li>
<li><a href="https://www.postgresql.us/">United States PostgreSQL Association</a></li>
<li><a href="https://www.commandprompt.com/">Command Prompt</a></li>
<li><a href="https://www.marchex.com/">Marchex</a></li>
<li>Richard Broersma</li>
<li><a href="https://tigerlead.com/">TigerLead</a></li>
<li>Thom Brown</li>
<li>Hitoshi Harada</li>
<li><a href="https://www.25th-floor.com/">25th-floor</a></li>
<li><a href="https://www.hubbellgrp.com/">Hubbell Group</a></li>
<li>John S. Gage</li>
<li><a href="https://www.2ndquadrant.us/">Greg Smith</a></li>
<li><a href="https://www.urbandb.com/">UrbanDB.com</a></li>
<li><a href="https://depesz.com/">depesz</a></li>
<li><a href="https://jim.nasby.net/">Jim Nasby</a></li>
<li>David Golden</li>
<li><a href="https://thoughts.j-davis.com/">Jeff Davis</a></li>
<li><a href="https://www.estately.com/">Estately</a></li>
<li><a href="https://www.full-table-scan.com/">Chris Spotts</a></li>
<li><a href="https://www.kineticode.com/">Kineticode</a></li>
<li><a href="https://www.cxnet.cl/">CxNet (Chile)</a></li>
<li><a href="https://www.schemaverse.com/">Schemaverse</a></li>
<li><a href="https://www.midstorm.org/~telles/">Fábio Telles Rodriguez</a></li>
<li><a href="https://pgdba.net/blog/">Michael Nacos</a></li>
<li>August Zajonc</li>
</ul>
<p>And special thanks to <a href="https://it.toolbox.com/blogs/database-soup/">Josh Berkus</a> for suggesting the fundraising approach,
and to making suggestions and helping out as I created the infrastructure to
make it happen. And also to <a href="https://www.linkedin.com/in/gavinmroy">Gavin Roy</a> for the founding contribution from
myYearbook that really got things kicked off.</p>
<p>Other folks who have helped with feedback and comments as development has got
underway include</p>
<ul>
<li><a href="https://plasmasturm.org/">Aριστοτέλης Παγκαλτζής (Aristotle Pagaltzis)</a></li>
<li><a href="https://www.dagolden.com/">David Golden</a></li>
<li><a href="https://bulknews.typepad.com/blog/">Tatsuhiko Miyagawa</a></li>
<li><a href="https://www.shadowcat.co.uk/blog/matt-s-trout/">Matt S Trout</a></li>
<li><a href="https://www.linkedin.com/in/gbarr">Graham Barr</a></li>
<li><a href="https://iki.fi/jhi/">Jarkko Hietaniemi</a></li>
<li><a href="https://search.cpan.org/~andk/">Andreas J. König</a></li>
<li>Other folks I&rsquo;ve forgotten (sorry, remind me!)</li>
</ul>
<p>My thanks to you all! Watch this space for further developments. I aim to have
the project complete this winter, with a formal launch no later than <a href="https://www.pgcon.org/2011/">PGCon</a>.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1673708474</id><title type="html">PGWest Slides, Manager Access, Wish List, Back to Work</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/slides-manager-work/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-11-24T22:56:54Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="slides" label="Slides"/><category scheme="https://blog.pgxn.org/tags" term="pgwest" label="PgWest"/><category scheme="https://blog.pgxn.org/tags" term="presentation" label="Presentation"/><category scheme="https://blog.pgxn.org/tags" term="back-to-work" label="Back to Work"/><category scheme="https://blog.pgxn.org/tags" term="design" label="Design"/><category scheme="https://blog.pgxn.org/tags" term="search-site" label="Search Site"/><category scheme="https://blog.pgxn.org/tags" term="blue-sky" label="Blue Sky"/><category scheme="https://blog.pgxn.org/tags" term="wish-list" label="Wish List"/><summary type="html"><![CDATA[<p>PGWest Slides, Back to Work</p>
<p>Well, back to work a bit, anyway. Tomorrow is Thanksgiving in the U.S., so I&rsquo;m
not likely to do much more until next week. But I&rsquo;ll be heads down on it,
then, working to get the search site done.</p>
<p>Before I talk about that, though, I realize that I forgot to post the link to
my slides from <a href="https://www.postgresqlconference.org/2010/west/">PGWest</a>. I actually posted them <em>before</em> the talk, I just
never got round to mentioning it here. So, here it is:</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>PGWest Slides, Back to Work</p>
<p>Well, back to work a bit, anyway. Tomorrow is Thanksgiving in the U.S., so I&rsquo;m
not likely to do much more until next week. But I&rsquo;ll be heads down on it,
then, working to get the search site done.</p>
<p>Before I talk about that, though, I realize that I forgot to post the link to
my slides from <a href="https://www.postgresqlconference.org/2010/west/">PGWest</a>. I actually posted them <em>before</em> the talk, I just
never got round to mentioning it here. So, here it is:</p>
<p>I&rsquo;m pretty happy with how it came out. The organizers shot video of it, too;
I&rsquo;ll post a link or embed when it drops.</p>
<p>Meanwhile, I <em>have</em> returned to work on the <a href="https://theory.github.com/pgxn/">search site design</a>. How do you
like it? The pages I&rsquo;ve completed the design for are:</p>
<ul>
<li><a href="https://theory.github.com/pgxn/">Search/Home Page</a></li>
<li><a href="https://theory.github.com/pgxn/results.html">Search Results</a></li>
<li><a href="https://theory.github.com/pgxn/pgtap.html">Extension documentation</a></li>
<li><a href="https://theory.github.com/pgxn/pgtapdist.html">Distribution information</a></li>
<li><a href="https://theory.github.com/pgxn/theory.html">Owner information</a></li>
</ul>
<p>Imagine if all open-source PostgreSQL extensions were posted on this site. You
could search through them all, including their documentation, browse their
docs, find related extensions, and see what else the extension owner has
worked on. All in one place, without downloading anything. That&rsquo;s the plan for
this site. Notice anything missing you think should be there? Let me know.</p>
<p>So I&rsquo;m going to start work on the app to create this now, but before I do, I
wanted to point out the PGXN project <a href="https://github.com/theory/pgxn/wiki/PGXN-Wish-List">wish list wiki</a>. Folks have been having
all sorts of ideas for features that would be great for PGXN, and I don&rsquo;t want
to lose track of them. So if you&rsquo;ve had any ideas at all, no matter how big or
small, <em>please</em> feel free to add them to the wiki. As the project progresses,
some I might be able to just build in, but others I might want to see go onto
a project road map. What do you want to see on the road map?</p>
<p>Oh, and if you want to see <em>your</em> extensions on this site, please do <a href="https://manager.pgxn.org/account/register">register
a PGXN Manager account</a>. Once your account is approved (and we&rsquo;re pretty quick
to do so), have a look at the <a href="https://manager.pgxn.org/howto">how to</a> and release your extensions on PGXN
today!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1473157261</id><title type="html">PGWest, Search Site Sneak Peak</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/pgwest-and-sneak-peak/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-11-03T21:09:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="search" label="Search"/><category scheme="https://blog.pgxn.org/tags" term="pgwest" label="PgWest"/><category scheme="https://blog.pgxn.org/tags" term="presentation" label="Presentation"/><category scheme="https://blog.pgxn.org/tags" term="extensions" label="Extensions"/><category scheme="https://blog.pgxn.org/tags" term="postgresql-9.1" label="Postgresql 9.1"/><summary type="html"><![CDATA[I&rsquo;ve been working on my <a href="https://www.postgresqlconference.org/content/building-and-distributing-postgresql-extensions-without-learning-c">PostgreSQL Conference West presentation</a>, which
heavily features PGXN, of course. I think it&rsquo;s looking good. If you&rsquo;re at
<a href="https://www.postgresqlconference.org/2010/west/">PGWest</a> or are in the San Francisco area and free, come see the talk! Should
be a good introduction to creating PostgreSQL extensions and distributing them
on PGXN. The latest bit I added is a section on the modifications needed to
support the forthcoming <a href="https://s.coop/pgext"><code>CREATE EXTENSION</code></a> support slated for 9.1.
Fortunately it&rsquo;s dead simple, and will make dealing with extensions in the
database a lot simpler, administratively. Really looking forward to that. Of
course I&rsquo;ll post slides once the talk is over.]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;ve been working on my <a href="https://www.postgresqlconference.org/content/building-and-distributing-postgresql-extensions-without-learning-c">PostgreSQL Conference West presentation</a>, which
heavily features PGXN, of course. I think it&rsquo;s looking good. If you&rsquo;re at
<a href="https://www.postgresqlconference.org/2010/west/">PGWest</a> or are in the San Francisco area and free, come see the talk! Should
be a good introduction to creating PostgreSQL extensions and distributing them
on PGXN. The latest bit I added is a section on the modifications needed to
support the forthcoming <a href="https://s.coop/pgext"><code>CREATE EXTENSION</code></a> support slated for 9.1.
Fortunately it&rsquo;s dead simple, and will make dealing with extensions in the
database a lot simpler, administratively. Really looking forward to that. Of
course I&rsquo;ll post slides once the talk is over.</p>
<p>As part of preparing for the talk, and because there isn&rsquo;t currently much to
actually <em>see</em> of PGXN, I&rsquo;ve been mocking up the layout for the new search
site, which as you know from the <a href="https://pgxn.org/status.html">status page</a> is the next part of the project
I&rsquo;m slated to work on. I&rsquo;ve been committing the mockps to the gh-pages branch
of the repository, which means you can see what it looks like live on the net
<a href="https://theory.github.com/pgxn/">right here</a>. That&rsquo;s the home page, including our sponsor links and tag cloud.
Click the &ldquo;PGXN Search&rdquo; button to see a mockup of search results (or get them
<a href="https://theory.github.com/pgxn/results.html">here</a>). Click on any search result to see the mockup of a documentation page
(or link it <a href="https://theory.github.com/pgxn/pgtap.html">here</a>). The design is based on the <a href="https://www.oswd.org/design/preview/id/2839">lazydays</a> open-source Web
design, and I&rsquo;m quite happy with it. Your thoughts?</p>
<p>As this comes together, I&rsquo;m gearing up to start hacking on the app to produce
the search site. At this point, I&rsquo;m thinking that it would become the new home
page for <a href="https://pgxn.org/">PGXN</a>, rather than a separate search.pgxn.org site. Thoughts?</p>
<p>I&rsquo;ll post the slides tomorrow.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1374928184</id><title type="html">The new PGXN Manager &amp;ldquo;Internal Server Error&amp;rdquo; page</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/internal-server-error/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-10-22T19:12:14Z</published><author><name>David E. Wheeler</name></author><summary type="html"><![CDATA[<figure><img src="/2010/internal-server-error/owowow.png"
			alt="Photo of a male torso in a blue-ish shirt with the PGXN logo pinned to its side"><figcaption>
			<p>The new PGXN Manager &ldquo;Internal Server Error&rdquo; page.</p>
		</figcaption>
</figure>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<figure><img src="/2010/internal-server-error/owowow.png"
			alt="Photo of a male torso in a blue-ish shirt with the PGXN logo pinned to its side"><figcaption>
			<p>The new PGXN Manager &ldquo;Internal Server Error&rdquo; page.</p>
		</figcaption>
</figure>]]></content></entry><entry><id>https://blog.pgxn.org/post/1368261274</id><title type="html">Reserved Extensions</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/reserved-extensions/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-10-21T20:43:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="reserved-words" label="Reserved Words"/><category scheme="https://blog.pgxn.org/tags" term="reserved-extensions" label="Reserved Extensions"/><category scheme="https://blog.pgxn.org/tags" term="extensions" label="Extensions"/><category scheme="https://blog.pgxn.org/tags" term="postgresql" label="PostgreSQL"/><category scheme="https://blog.pgxn.org/tags" term="procedural-languages" label="Procedural Languages"/><category scheme="https://blog.pgxn.org/tags" term="pl/pgsql" label="PL/pgSQL"/><category scheme="https://blog.pgxn.org/tags" term="pl/perl" label="PL/Perl"/><category scheme="https://blog.pgxn.org/tags" term="contributed-modules" label="Contributed Modules"/><summary type="html"><![CDATA[<p>I&rsquo;m thinking about how to add support for reserved extensions. These are
extensions that one needs to depend on, but aren&rsquo;t distributed via PGXN.
Primarily, this means stuff distributed with the PostgreSQL core, including:</p>
<ul>
<li>PostgreSQL itself</li>
<li>Bundled procedural languages (plpgsql, plperl, etc.)</li>
<li>All <a href="https://www.postgresql.org/docs/current/static/contrib.html">contributed modules</a></li>
</ul>
<p>Basically, anything that an extension might want to declare as a dependency,
but that isn&rsquo;t on PGXN itself.</p>
<p>There are a number of ways to do this. Which do you think would be the best
approach?</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;m thinking about how to add support for reserved extensions. These are
extensions that one needs to depend on, but aren&rsquo;t distributed via PGXN.
Primarily, this means stuff distributed with the PostgreSQL core, including:</p>
<ul>
<li>PostgreSQL itself</li>
<li>Bundled procedural languages (plpgsql, plperl, etc.)</li>
<li>All <a href="https://www.postgresql.org/docs/current/static/contrib.html">contributed modules</a></li>
</ul>
<p>Basically, anything that an extension might want to declare as a dependency,
but that isn&rsquo;t on PGXN itself.</p>
<p>There are a number of ways to do this. Which do you think would be the best
approach?</p>
<ol>
<li>Hard-code a list into the source code</li>
<li>Include an editable list in the runtime configuration file</li>
<li>Add a database table reserved for them and an API to edit it</li>
<li>Create a &ldquo;pgdev&rdquo; user and upload a distribution declaring all those
extensions in a <code>META.json</code> file</li>
<li>Same as 4, but actually upload the PostgreSQL source</li>
</ol>
<p>I&rsquo;m leaning towards #2, perhaps having it automatically maintain a list in the
database and a metadata file on the mirrors.</p>
<p>But what do you think? Opinions wanted!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1353947079</id><title type="html">Creating Distributions</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/creating-distributions/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-10-19T22:28:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="distribution" label="Distribution"/><category scheme="https://blog.pgxn.org/tags" term="makefile" label="Makefile"/><category scheme="https://blog.pgxn.org/tags" term="pgxn" label="Pgxn"/><category scheme="https://blog.pgxn.org/tags" term="pgxs" label="Pgxs"/><category scheme="https://blog.pgxn.org/tags" term="pg_config" label="pg_config"/><category scheme="https://blog.pgxn.org/tags" term="pg_regress" label="pg_regress"/><category scheme="https://blog.pgxn.org/tags" term="directory-structure" label="Directory Structure"/><summary type="html"><![CDATA[<p>Following the <a href="https://blog.pgxn.org/post/1352326020/first-upload">upload of <code>pair</code></a> to PGXN, I wanted to take a few minutes to
write about how to structure a PGXN distribution.</p>
<h2 id="omg-distribution-wtf">OMG Distribution WTF?</h2>
<p>First of all, what is a &ldquo;distribution&rdquo; in the PGXN sense? Basically, it&rsquo;s a
collection of one or more PostgreSQL extensions. That&rsquo;s it.</p>
<p>So why allow more than one extension? Maybe no PGXN distribution will ever
have more than one extension. After all, the goal should be many focused,
minimalist tools that folks can combine in their apps. But sometimes it
doesn&rsquo;t work out that way.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Following the <a href="https://blog.pgxn.org/post/1352326020/first-upload">upload of <code>pair</code></a> to PGXN, I wanted to take a few minutes to
write about how to structure a PGXN distribution.</p>
<h2 id="omg-distribution-wtf">OMG Distribution WTF?</h2>
<p>First of all, what is a &ldquo;distribution&rdquo; in the PGXN sense? Basically, it&rsquo;s a
collection of one or more PostgreSQL extensions. That&rsquo;s it.</p>
<p>So why allow more than one extension? Maybe no PGXN distribution will ever
have more than one extension. After all, the goal should be many focused,
minimalist tools that folks can combine in their apps. But sometimes it
doesn&rsquo;t work out that way.</p>
<p>As an example, I&rsquo;ve been planning to break <a href="https://pgtap.org/">pgTAP</a> up into two parts for a
while: one for scalar and relational testing, the other for schema testing.
Often one needs only the scalar and relational testing, while the schema
testing is more often needed only for testing replication and whatnot. Whether
or not I choose to distribute both parts in one package I have yet to
determine, but it could well make sense to keep them in one distribution.</p>
<p>Besides, I&rsquo;ve tried to write <a href="https://github.com/theory/pgxn-manager">PGXN::Manager</a> in such a way that it&rsquo;s not
specific to PostgreSQL. So that if someone wanted to create a Drupal XN with
it or something, they could. Or PyXN. Or, hell, even if <a href="https://cpan/org/">CPAN</a> wanted to
switch someday, they could. (Note that I&rsquo;ve registered myxn.org for fun. I may
or may not do anything with that, but see <a href="https://theory.github.com/mytap/">MyTAP</a>. Yes, I am insane.)</p>
<h2 id="thats-so-meta">That&rsquo;s So Meta</h2>
<p>Anyway, back to the structure of distributions. At its simplest, the only
thing PGXN requires is a single file, <code>META.json</code>, which describes the
package. This is (currently) the only file that PGXN Manager uses to index a
distribution, so it&rsquo;s important to get it right. The <a href="https://pgxn.org/meta/spec.html">PGXN Meta Spec</a> has a
rather complete example of a hypothetical pgTAP distribution <code>META.json</code>.</p>
<p>If you have only one .sql file for your extension and it&rsquo;s the same name as
the distribution (and I expect this would be common), then you can make it
pretty simple. For example, the <code>pair</code> distribution has only one SQL file. So
the <code>META.json</code> could be:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;pair&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;abstract&#34;</span><span class="p">:</span> <span class="s2">&#34;A key/value pair data type&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.1.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;maintainer&#34;</span><span class="p">:</span> <span class="s2">&#34;David E. Wheeler &lt;david@justatheory.com&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;license&#34;</span><span class="p">:</span> <span class="s2">&#34;postgresql&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;meta-spec&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://pgxn.org/meta/spec.txt&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>That&rsquo;s it. The only thing that may not be obvious from this example is that
all version numbers in a <code>META.json</code> <em>must</em> be <a href="https://semver.org/">semantic versions</a>. If they&rsquo;re
not, PGXN will make them so. So &ldquo;1.2&rdquo; will become &ldquo;1.2.0&rdquo;, and so would
&ldquo;1.02&rdquo;. So do try to use semantic versions and not worry about it.</p>
<p>In the short run, you won&rsquo;t need anything more in your <code>META.json</code> file. But
once I get to creating the search site and the command-line client for PGXN,
you&rsquo;re probably going to want to do more. Other useful keys to include are:</p>
<ul>
<li><a href="https://pgxn.org/meta/spec.html#tags"><code>tags</code></a>: An array of tags to associate with a distribution. Will help
with searching.</li>
<li><a href="https://pgxn.org/meta/spec.html#prereqs"><code>prereqs</code></a>: A list of prerequisite extensions or PostgreSQL contrib
moules (or PostgreSQL itself).</li>
<li><a href="https://pgxn.org/meta/spec.html#provides"><code>provides</code></a>: A list of included extensions. Useful if you have more than
one or the one has a different name that the distribution (silly, but it
happens). It also will index such extension names such that you are the
owner, if you&rsquo;re the first to update one with that name.</li>
<li><a href="https://pgxn.org/meta/spec.html#release_status"><code>release_status</code></a>: To label a distribution as &ldquo;stable,&rdquo; &ldquo;unstable,&rdquo; or
&ldquo;testing.&rdquo; Useful for uploading distributions for people to test but that
clients won&rsquo;t install by default.</li>
<li><a href="https://pgxn.org/meta/spec.html#resources"><code>resources</code></a>: A list of related links, such as to an SCM repository or
bug tracker. The search site will output these links.</li>
</ul>
<p>Have a look at <a href="https://github.com/theory/kv-pair/blob/master/META.json">the <code>META.json</code> in the <code>pair</code> distribution</a> for a more
extended example.</p>
<h2 id="new-order">New Order</h2>
<p>For PGXN, the general idea is that you&rsquo;ll use <a href="https://www.postgresql.org/docs/9/static/xfunc-c.html#XFUNC-C-PGXS">PGXS</a> to create your PostgreSQL
extensions. I&rsquo;m hoping to encourage a slight modification of the directory
layout for PGXN distributions, but as I hope I&rsquo;ve made clear so far, PGXN
itself doesn&rsquo;t really care how you structure things, or if you use PGXS. That
said, the proposed <a href="https://wiki.postgresql.org/wiki/PGXN#PGXN_Client">download and installation client</a> will assume the use of
PGXS (unless and until the PostgreSQL core adds some other kind of
extension-building support), so it&rsquo;s probably the best choice.</p>
<p>Most PGXS-powered distributions have the code files in the main directory,
with documentation in a <code>README.extension_name</code> file. What I&rsquo;d like to see
instead, and will encourage via the forthcoming <a href="https://wiki.postgresql.org/wiki/PGXN#Search_Site">search site</a>, is that things
be organized into subdirectories:</p>
<ul>
<li><code>src</code> for any C source code files</li>
<li><code>sql</code> for SQL source files. These usually are responsible for installing an
extension into a database</li>
<li><code>doc</code> for documentation files (the search site will likely look there for
Markdown, Textile, HTML, and other document formats)</li>
<li><code>test</code> for tests</li>
</ul>
<p>I&rsquo;ve tried to make the <code>pair</code> distribution a good <a href="https://github.com/theory/kv-pair/blob/">example of this</a>. To make
it all work, The <a href="https://github.com/theory/kv-pair/blob/master/Makefile">Makefile</a> is written like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="nv">DATA</span> <span class="o">=</span> sql/pair.sql sql/uninstall_pair.sql
</span></span><span class="line"><span class="cl"><span class="nv">TESTS</span> <span class="o">=</span> <span class="k">$(</span>wildcard test/sql/*.sql<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">REGRESS</span> <span class="o">=</span> <span class="k">$(</span>patsubst test/sql/%.sql,%,<span class="k">$(</span>TESTS<span class="k">))</span>
</span></span><span class="line"><span class="cl"><span class="nv">REGRESS_OPTS</span> <span class="o">=</span> --inputdir<span class="o">=</span><span class="nb">test</span>
</span></span><span class="line"><span class="cl"><span class="nv">DOCS</span> <span class="o">=</span> doc/pair.txt
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="err">ifdef</span> <span class="err">NO_PGXS</span>
</span></span><span class="line"><span class="cl"><span class="nv">top_builddir</span> <span class="o">=</span> ../..
</span></span><span class="line"><span class="cl"><span class="err">include</span> <span class="k">$(</span><span class="nv">top_builddir</span><span class="k">)</span><span class="err">/src/Makefile.global</span>
</span></span><span class="line"><span class="cl"><span class="err">include</span> <span class="k">$(</span><span class="nv">top_srcdir</span><span class="k">)</span><span class="err">/contrib/contrib-global.mk</span>
</span></span><span class="line"><span class="cl"><span class="err">else</span>
</span></span><span class="line"><span class="cl"><span class="nv">PG_CONFIG</span> <span class="o">=</span> pg_config
</span></span><span class="line"><span class="cl"><span class="nv">PGXS</span> <span class="o">:=</span> <span class="k">$(</span>shell <span class="k">$(</span>PG_CONFIG<span class="k">)</span> --pgxs<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="err">include</span> <span class="k">$(</span><span class="nv">PGXS</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="err">endif</span>
</span></span></code></pre></div><p>The <code>DATA</code> variable identifies the files containing the extension, while
<code>TESTS</code> loads a list of all the tests, which are in the <code>test/sql</code> directory.
Note that I&rsquo;m using <code>pg_regress</code> for tests. It expects that tests be named and
that there be corresponding &ldquo;expected&rdquo; files to compare against. With the
<code>REGRESS_OPTS = --inputdir=test</code> line, I&rsquo;m telling <code>pg_regess</code> to find the
test files in <a href="https://github.com/theory/kv-pair/tree/master/test/sql/"><code>test/sql</code></a> and the expected output files in <a href="https://github.com/theory/kv-pair/tree/master/test/expected/"><code>test/expected</code></a>.
And finally, the <code>DOCS</code> variable points to a single file with the
documentation, <a href="https://github.com/theory/kv-pair/blob/master/doc/pair.txt"><code>doc/pair.txt</code></a>. If this extension had required any C code
(like <a href="https://pgtap.org/">pgTAP</a> or <a href="https://postgis.org/">PostGIS</a> do), I would have pointed the <code>MODULES</code> variable at
files in a <code>src</code> directory.</p>
<p>After that we just have build instructions. If called with <code>make NO_PGXS=1</code>,
it assumes that the unzipped distribution directory has been put in the
&ldquo;contrib&rdquo; directory of the PostgreSQL source tree used to build PostgreSQL.
That&rsquo;s probably only important if one is installing on PostgreSQL 8.1 or
lower. Otherwise, it assumes a plain <code>make</code> and uses the <a href="https://www.postgresql.org/docs/current/static/app-pgconfig.html"><code>pg_config</code></a> in your
path to find PGXS to do the build.</p>
<p>For more on PostgreSQL extension building support, please consult <a href="https://www.postgresql.org/docs/9/static/xfunc-c.html#XFUNC-C-PGXS">the
documentation</a>.</p>
<h2 id="zip-me-up">Zip Me Up</h2>
<p>Once you&rsquo;ve got your extension developed and well-tested, and your
distribution just right and the <code>META.json</code> file all proof-read and solid,
it&rsquo;s time to upload the distribution to PGXN. What you want to do is to zip it
up to create a distribution archive. Here&rsquo;s what I did for <code>pair</code>, exporting
it from Git:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">git checkout-index -af --prefix ~/Desktop/pair-0.1.0/
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> ~/Desktop/
</span></span><span class="line"><span class="cl">rm pair-0.1.0/.gitignore
</span></span><span class="line"><span class="cl">zip -r pair-0.1.0.zip pair-0.1.0
</span></span></code></pre></div><p>Then the <code>pair-0.1.0.zip</code> file was ready to upload. Simple, eh?</p>
<p>Now, one can upload any kind of archive file to PGXN, including a tarball, or
bzip2&hellip;um&hellip;ball? Basically, any kind of archive format recognized by
<a href="https://search.cpan.org/perldoc?Archive::Extract">Archive::Extract</a>. You can upload a <code>.pgz</code> if you like, in which case PGXN
will assume that it&rsquo;s a zip file. A zip file is best because then
PGXN::Manager won&rsquo;t have to rewrite it. It&rsquo;s also preferable that everything
be unpacked from an archive into a directory with the name
<code>$distribution-$version</code>. If not, PGXN will rewrite it to do so. But it saves
the server some effort if all it has to do is move a .zip file that&rsquo;s properly
formatted, so it would be appreciated if you would upload stuff that&rsquo;s already
nicely formatted for distribution in a zip archive.</p>
<h2 id="release-it">Release It!</h2>
<p>And that&rsquo;s it! Not too bad, eh? Just please do be very careful cutting and
pasting examples; I initially uploaded the <code>pair</code> distribution thinking that
it contained pgTAP. It was kind of a PITA to fix. Hopefully we&rsquo;ll be able to
build things up to the point where a lot of this stuff can be automated
(especially the creation of the <code>META.json</code>), but for now it&rsquo;s done by hand.
So be careful out there, and good luck!</p>
<p>Oh, and if you have an extension that you&rsquo;d like to release on PGXN now, I am
running a limited beta for interested extension developers. Please hit the
<a href="https://groups.google.com/group/pgxn-users/">mail list</a> for the details to be posted shortly.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1352594371</id><title type="html">Fundraising and Development Update</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/fundraising-development-update/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-10-19T18:43:57Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="fundraising" label="Fundraising"/><category scheme="https://blog.pgxn.org/tags" term="development" label="Development"/><category scheme="https://blog.pgxn.org/tags" term="pgxn" label="PGXN"/><category scheme="https://blog.pgxn.org/tags" term="contribute" label="Contribute"/><category scheme="https://blog.pgxn.org/tags" term="perl" label="Perl"/><category scheme="https://blog.pgxn.org/tags" term="database" label="Database"/><category scheme="https://blog.pgxn.org/tags" term="pgwest" label="PgWest"/><category scheme="https://blog.pgxn.org/tags" term="conference" label="Conference"/><summary type="html"><![CDATA[<p>Yesterday was a busy day. In addition to making the <a href="https://blog.pgxn.org/post/1352326020/first-upload">first PGXN release</a>, I
updated the fundraising spreadsheet and then the thermometer displayed on the
<a href="https://pgxn.org/contributors.html">main site</a>. The good news is that things are coming along nicely. Thanks to
recent contributions from <a href="https://www.commandprompt.com/">Command Prompt</a>, <a href="https://www.marchex.com/">Marchex</a>, Hitoshi Harada, and
<a href="https://www.25th-floor.com/">25th-floor</a>, we are now just $2500 short of our goal of $25,000. Thank you
all!</p>
<p>Can you help us get to our goal in time for <a href="https://www.postgresqlconference.org/2010/west/">PgWest 2010</a>, which is November
2-4 in San Francisco? I&rsquo;ll be giving a talk there, &ldquo;<a href="https://www.postgresqlconference.org/content/building-and-distributing-postgresql-extensions-without-learning-c">Building and Distributing
PostgreSQL Extensions Without Learning C</a>&rdquo;, in which PGXN will of course be
featured. Would be great to announce that the fundraising was successful.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Yesterday was a busy day. In addition to making the <a href="https://blog.pgxn.org/post/1352326020/first-upload">first PGXN release</a>, I
updated the fundraising spreadsheet and then the thermometer displayed on the
<a href="https://pgxn.org/contributors.html">main site</a>. The good news is that things are coming along nicely. Thanks to
recent contributions from <a href="https://www.commandprompt.com/">Command Prompt</a>, <a href="https://www.marchex.com/">Marchex</a>, Hitoshi Harada, and
<a href="https://www.25th-floor.com/">25th-floor</a>, we are now just $2500 short of our goal of $25,000. Thank you
all!</p>
<p>Can you help us get to our goal in time for <a href="https://www.postgresqlconference.org/2010/west/">PgWest 2010</a>, which is November
2-4 in San Francisco? I&rsquo;ll be giving a talk there, &ldquo;<a href="https://www.postgresqlconference.org/content/building-and-distributing-postgresql-extensions-without-learning-c">Building and Distributing
PostgreSQL Extensions Without Learning C</a>&rdquo;, in which PGXN will of course be
featured. Would be great to announce that the fundraising was successful.</p>
<p>As for the time I&rsquo;ve put in so far, I&rsquo;m happy to have PGXN Manager up and
working, but of course it has taken more hours than I expected. 76.5 so far.
I&rsquo;d estimated 40. Meanwhile, the database design is up to 43 hours from the
estimated 24. And that doesn&rsquo;t count the hours I spent chasing shiny yaks and
shaving them, like <a href="https://search.cpan.org/perldoc?SemVer">SemVer</a> and <a href="https://search.cpan.org/perldoc?Router::Resource">Router::Resource</a>. Those of you who estimate
development projects, take heed! I clearly need to double all my estimates
before I submit them.</p>
<p>Still, with the fundraising nearly done, I&rsquo;m committed to finishing this
project. I view it as a project budget, and so that&rsquo;s what it will be, whether
or not it takes me twice or four times as many hours as I&rsquo;d estimated.</p>
<p>That said, you could help. Right? PGXN Manager is in good shape, but it&rsquo;s not
done. If you&rsquo;d like to roll up your sleeves and contribute some code, please
<a href="https://github.com/theory/pgxn-manager/">fork it</a>, build it, and hack what you can. A few things on the to-do list:</p>
<ul>
<li>Add user admin interface. Should include checkbox to make and remove
administrative permission</li>
<li>Add mirror admin interface</li>
<li>Add extension ownership permissions interface</li>
</ul>
<p>The database API is there for these bits already; the code would mainly be in
Perl. Hit me on #pgxn on <a href="https://webchat.freenode.net/">Freenode</a> if you want to help.</p>
<p>If documentation is your thing, contributions there would be appreciated, as
well. In particular, the <a href="https://manager.pgxn.org/about">About PGXN</a> page is a bit thin. Other interfaces
will need help, too. More on that as we add users.</p>
<p>Thanks everyone for your support!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1352326020</id><title type="html">First Upload to PGXN</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/first-upload/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-10-19T17:46:44Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="pgxn" label="PGXN"/><category scheme="https://blog.pgxn.org/tags" term="upload" label="Upload"/><category scheme="https://blog.pgxn.org/tags" term="distribution" label="Distribution"/><category scheme="https://blog.pgxn.org/tags" term="metadata" label="Metadata"/><category scheme="https://blog.pgxn.org/tags" term="json" label="JSON"/><category scheme="https://blog.pgxn.org/tags" term="pair" label="Pair"/><summary type="html"><![CDATA[<p>Last night I deployed <a href="https://github.com/theory/pgxn-manager/">PGXN::Manager</a> v0.2.1 and uploaded the first
distribution, <a href="https://master.pgxn.org/dist/pair/">pair</a>. If you follow that link you&rsquo;ll see three files:</p>
<ul>
<li><a href="https://master.pgxn.org/dist/pair/pair-0.1.0.json"><code>pair-0.1.0.json</code></a> is metadata file generated by PGXN manager to describe
the distribution. Most of its data is taken from the <a href="https://github.com/theory/kv-pair/blob/master/META.json"><code>META.json</code></a> included
in the uploaded zip file, but a few keys, like &ldquo;sha1&rdquo;, are generated, and
others, like &ldquo;release_status&rdquo; are added if they&rsquo;re not in the included
<code>META.json</code>.</li>
<li><a href="https://master.pgxn.org/dist/pair/pair-0.1.0.readme">pair-0.0.1.readme</a> is a copy of the <code>README</code> file distributed with pair.</li>
<li><a href="https://master.pgxn.org/dist/pair/pair-0.1.0.pgz">pair-0.1.0.pgz</a> is the distribution zip file. That&rsquo;s the file you want to
download, unzip, and build and install.</li>
</ul>
<p>Following the spec I previously <a href="https://blog.pgxn.org/post/988613682/restful-directory">wrote up</a>, there are a number of other files
that get created when a new distribution is uploaded to PGXN. For the pair
extension, we got:</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Last night I deployed <a href="https://github.com/theory/pgxn-manager/">PGXN::Manager</a> v0.2.1 and uploaded the first
distribution, <a href="https://master.pgxn.org/dist/pair/">pair</a>. If you follow that link you&rsquo;ll see three files:</p>
<ul>
<li><a href="https://master.pgxn.org/dist/pair/pair-0.1.0.json"><code>pair-0.1.0.json</code></a> is metadata file generated by PGXN manager to describe
the distribution. Most of its data is taken from the <a href="https://github.com/theory/kv-pair/blob/master/META.json"><code>META.json</code></a> included
in the uploaded zip file, but a few keys, like &ldquo;sha1&rdquo;, are generated, and
others, like &ldquo;release_status&rdquo; are added if they&rsquo;re not in the included
<code>META.json</code>.</li>
<li><a href="https://master.pgxn.org/dist/pair/pair-0.1.0.readme">pair-0.0.1.readme</a> is a copy of the <code>README</code> file distributed with pair.</li>
<li><a href="https://master.pgxn.org/dist/pair/pair-0.1.0.pgz">pair-0.1.0.pgz</a> is the distribution zip file. That&rsquo;s the file you want to
download, unzip, and build and install.</li>
</ul>
<p>Following the spec I previously <a href="https://blog.pgxn.org/post/988613682/restful-directory">wrote up</a>, there are a number of other files
that get created when a new distribution is uploaded to PGXN. For the pair
extension, we got:</p>
<ul>
<li>
<p><a href="https://master.pgxn.org/by/dist/pair.json"><code>by/dist/pair.json</code></a>, which will be updated with information for every
release of the &ldquo;pair&rdquo; distribution.</p>
</li>
<li>
<p><a href="https://master.pgxn.org/by/extension/pair.json"><code>by/extension/pair.json</code></a>, which will be updated for every upload
containing the &ldquo;pair&rdquo; extension.</p>
</li>
<li>
<p><a href="https://master.pgxn.org/by/owner/theory.json"><code>by/owner/theory.json</code></a>, which will be updated every time I upload a
distribution.</p>
</li>
<li>
<p>Files for every tag listed in the <a href="https://master.pgxn.org/dist/pair/pair-0.1.0.json">metadata</a> are also
created. In this case, that includes:</p>
<ul>
<li><a href="https://master.pgxn.org/by/tag/key%20value%20pair.json"><code>by/tag/key value pair.json</code></a></li>
<li><a href="https://master.pgxn.org/by/tag/key%20value.json"><code>by/tag/key value.json</code></a></li>
<li><a href="https://master.pgxn.org/by/tag/ordered%20pair.json"><code>by/tag/ordered pair.json</code></a></li>
<li><a href="https://master.pgxn.org/by/tag/pair.json"><code>by/tag/pair.json</code></a></li>
<li><a href="https://master.pgxn.org/by/tag/variadic%20function.json"><code>by/tag/variadic function.json</code></a></li>
</ul>
<p>Each of these files will be updated every time a distribution is uploaded
containing the relevant tag.</p>
</li>
</ul>
<p>You&rsquo;ll soon be able to upload your own extension distributions to PGXN. If
you&rsquo;re interested, please subscribe to the <a href="https://groups.google.com/group/pgxn-users">mail list</a>, where I&rsquo;ll soon be
inviting folks to get an account and start uploading.</p>
<p>But first, a blog post on how to create a PGXN-friendly distribution archive.
Coming up shortly.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1291485816</id><title type="html">How Can I Detect a Proxied SSL Request?</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/how-can-i-detect-a-proxied-ssl-request/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-10-11T15:00:29Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="mod_proxy" label="mod_proxy"/><category scheme="https://blog.pgxn.org/tags" term="mod_ssl" label="mod_ssl"/><category scheme="https://blog.pgxn.org/tags" term="ssl" label="SSL"/><category scheme="https://blog.pgxn.org/tags" term="proxy" label="Proxy"/><category scheme="https://blog.pgxn.org/tags" term="uri" label="URI"/><category scheme="https://blog.pgxn.org/tags" term="apache" label="Apache"/><category scheme="https://blog.pgxn.org/tags" term="plack" label="Plack"/><category scheme="https://blog.pgxn.org/tags" term="request" label="Request"/><summary type="html"><![CDATA[I&rsquo;ve been thinking about how best to create two sections of the PGXN Manager
site, one that requires authentication and is on SSL and one that&rsquo;s public. So
far, I&rsquo;ve just had all the authenticated stuff go to /auth (because I&rsquo;m using
basic auth), but what I want to do now is require authentication if the
connection is via SSL. However, using a reverse proxy with mod_proxy to map to
the PGXN Manger Plack app running on an internal port, I&rsquo;ve found no
environment variables passed through that would allow me, in code, to
determine whether a request is via a proxied SSL or non-SSL connection. Most
irritating.]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;ve been thinking about how best to create two sections of the PGXN Manager
site, one that requires authentication and is on SSL and one that&rsquo;s public. So
far, I&rsquo;ve just had all the authenticated stuff go to /auth (because I&rsquo;m using
basic auth), but what I want to do now is require authentication if the
connection is via SSL. However, using a reverse proxy with mod_proxy to map to
the PGXN Manger Plack app running on an internal port, I&rsquo;ve found no
environment variables passed through that would allow me, in code, to
determine whether a request is via a proxied SSL or non-SSL connection. Most
irritating.</p>
<p>So tell me what you think about the alternative plan I&rsquo;ve come up with.</p>
<p>I&rsquo;ll have two plack apps, one mapped to /auth and one mapped to /no-auth. The
former will require authentication and the latter will not. They&rsquo;ll have
separate dispatch tables, of course. Then I&rsquo;ll have the SSL site proxied to
/auth and the non-SSL site proxied to /no-auth. Makes sense, right?</p>
<p>The only hangup I can see (though maybe you can see others?) is that my
current method of generating URIs knows nothing about proxies. So If I link to
/auth/account, when requests come through the proxy, it should actually create
a link to /account. Does it make sense to use relative links instead of
absolute links for all links to avoid this issue? I think it might be kind of
annoying, because not all the code is aware of the current URI, though there
are ways to deal with that.</p>
<p>Thoughts?</p>
<p>I guess I could stick with absolute URLs, and then have the <a href="https://github.com/theory/pgxn-manager/blob/master/lib/PGXN/Manager/Request.pm#L14"><code>uri_for</code></a> use
<code>$req−&gt;uri−&gt;path</code> for a proxied request and <code>$req−&gt;path_info</code> for a
non-proxied request.</p>
<p>Sure would be nice if there was some way to tell from the environment that a
request was forwarded from an SSL connection, though. Alas, there are only
three extra environment variable set by the proxy server:</p>
<ul>
<li>HTTP_X_FORWARDED_FOR</li>
<li>HTTP_X_FORWARDED_HOST</li>
<li>HTTP_X_FORWARDED_SERVER</li>
</ul>
<p>No HTTP_X_FORWARDED_PORT or HTTP_X_FORWARDED_SSL or SSL_ENABLED or anything
like that. Maybe I&rsquo;m missing something in my reading of the <a href="https://httpd.apache.org/docs/2.2/mod/mod_proxy.html">mod_proxy
documentation</a>?</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1274234177</id><title type="html">Mail List, SSL</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/mail-list-ssl/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-10-09T06:01:08Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="mail-list" label="Mail List"/><category scheme="https://blog.pgxn.org/tags" term="ssl" label="SSL"/><category scheme="https://blog.pgxn.org/tags" term="basic-auth" label="Basic Auth"/><summary type="html"><![CDATA[I&rsquo;m <em>this</em> close to having PGXN Manager ready for a limited beta. I&rsquo;ve got it
running on <a href="https://kineticode.com/">Kineticode</a>&rsquo;s server, and have been tweaking things here and
there, fixing some bugs and filling in a few missing bits. In the next couple
of days I&rsquo;ll get some more kinks worked out and then start inviting folks in.
If you&rsquo;re interested &ndash; especially if you have an extension you&rsquo;d like to
release, leave a comment here to let me know.]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;m <em>this</em> close to having PGXN Manager ready for a limited beta. I&rsquo;ve got it
running on <a href="https://kineticode.com/">Kineticode</a>&rsquo;s server, and have been tweaking things here and
there, fixing some bugs and filling in a few missing bits. In the next couple
of days I&rsquo;ll get some more kinks worked out and then start inviting folks in.
If you&rsquo;re interested &ndash; especially if you have an extension you&rsquo;d like to
release, leave a comment here to let me know.</p>
<p>Better yet, sign up for our new <a href="https://groups.google.com/group/pgxn-users">mail list</a>. I&rsquo;ve set this up so that PGXN
users can have a place to meet and discuss things (naturally). I expect
discussion will be about how to create proper distribution archives (hint:
it&rsquo;s all about the <a href="https://github.com/theory/pgxn/wiki/PGXN-Meta-Spec"><code>META.json</code></a> file); issues with PGXN Manager or the
network itself, and directions for ongoing development.</p>
<p>Oh, one thing I wanted to run by you here: I currently have two sections in
PGXN Manager: public and users only. Right now the only difference is that all
the users-only stuff is under the <code>/auth/</code> URI, which is itself limited by a
basic auth challenge. I find this setup a bit hinky, though, because the link
to <code>/about</code>, for example, appears in the nav menu for both sections, and if
you&rsquo;re logged in and click it, it will look like you&rsquo;re logged out.</p>
<p>So I was thinking perhaps that I&rsquo;d change it so that the difference is that
user-only is on port 443 (SSL) and public on port 80. That way I could have
links to <code>about</code> on both and one wouldn&rsquo;t appear to be logged out by clicking
it from the SSL site. Because, you know, they&rsquo;re effectively two different
sites.</p>
<p>Thoughts? Is using the SSL divide perhaps the most natural way to separate
user-only access from public access?</p>
<p>More soon.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1212136083</id><title type="html">Conflict and Redirection on POST</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/conflict-redirection-on-post/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-09-29T21:10:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="http" label="Http"/><category scheme="https://blog.pgxn.org/tags" term="post" label="Post"/><category scheme="https://blog.pgxn.org/tags" term="redirect" label="Redirect"/><category scheme="https://blog.pgxn.org/tags" term="rdg" label="RDG"/><category scheme="https://blog.pgxn.org/tags" term="post/redirect/get" label="Post/Redirect/Get"/><category scheme="https://blog.pgxn.org/tags" term="conflict" label="Conflict"/><category scheme="https://blog.pgxn.org/tags" term="http-status-codes" label="HTTP Status Codes"/><summary type="html"><![CDATA[Had an interesting discussion on <a href="#ZgotmplZ">#plack</a>. The upload form, which takes a POST
request for an upload, sends a redirect on a successful form submission. This
is known as the <a href="https://en.wikipedia.org/wiki/Post/Redirect/Get">Post/Redirect/Get (RDG)</a> pattern, though I didn&rsquo;t know that
before <a href="https://duckduckgo.com/?q=redirect&#43;from&#43;post">DuckDuckGoing it</a> it today. But I realized that the code was <em>not</em>
redirecting on a failed form submission, but reloading the form. I was
thinking that one should always redirect on POST, and so was looking into a
Rails-like <code>flash</code> pattern to cache an error message and the form contents on
the redirect. But in this discussion, I learned a few things:]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Had an interesting discussion on <a href="#ZgotmplZ">#plack</a>. The upload form, which takes a POST
request for an upload, sends a redirect on a successful form submission. This
is known as the <a href="https://en.wikipedia.org/wiki/Post/Redirect/Get">Post/Redirect/Get (RDG)</a> pattern, though I didn&rsquo;t know that
before <a href="https://duckduckgo.com/?q=redirect&#43;from&#43;post">DuckDuckGoing it</a> it today. But I realized that the code was <em>not</em>
redirecting on a failed form submission, but reloading the form. I was
thinking that one should always redirect on POST, and so was looking into a
Rails-like <code>flash</code> pattern to cache an error message and the form contents on
the redirect. But in this discussion, I learned a few things:</p>
<ol>
<li>
<p>One should use <a href="https://tools.ietf.org/html/rfc2616#section-10.3.4">HTTP 1.1 status code 303</a> rather than <a href="https://tools.ietf.org/html/rfc2616#section-10.3.3">status code 302</a>
for these sorts of redirects. Apparently, this status code, called &ldquo;See
Other,&rdquo; is specifically designed for use redirecting from a POST. Thanks
to <a href="https://blog.weftsoar.net/">counfound</a> for pointing that out.</p>
</li>
<li>
<p>The whole point of RDG is to prevent a double form submission. But the
truth is, in the case of an error, you <em>want</em> a second form submission.
For errors &ndash; such as a username conflict or a malformed distribution
archive &ndash; the POST failed, so you show the form again along with a
message about the problem and how to fix it.</p>
</li>
</ol>
<p>What&rsquo;s nice about this is that it&rsquo;s already the way I was doing it! In fact,
for such errors, I&rsquo;m returning <a href="https://tools.ietf.org/html/rfc2616#section-10.4.10">HTTP status code 409</a>, which indicates that
the request failed due to a conflict, and the user should fix it and resubmit.
So no need to redirect on failure. Yay!</p>
<p>Note that for XMLHttpRequests, I&rsquo;m not redirecting at all, but simply
returning the proper status code (success or conflict, generally) and a
fragment to be used to show an error message (or &ldquo;success&rdquo;) (in HTML, JSON, or
plain text, depending on the requestor&rsquo;s preferred type).</p>
<p>I think this works pretty well, and I&rsquo;m pleased to be making good use of HTTP.
Does it make sense to you?</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1205386689</id><title type="html">Account Requests and Moderation</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/account-moderation/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-09-28T18:47:37Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="account" label="Account"/><category scheme="https://blog.pgxn.org/tags" term="request" label="Request"/><category scheme="https://blog.pgxn.org/tags" term="moderation" label="Moderation"/><category scheme="https://blog.pgxn.org/tags" term="accept" label="Accept"/><category scheme="https://blog.pgxn.org/tags" term="reject" label="Reject"/><category scheme="https://blog.pgxn.org/tags" term="user" label="User"/><category scheme="https://blog.pgxn.org/tags" term="administrator" label="Administrator"/><category scheme="https://blog.pgxn.org/tags" term="jquery" label="jQuery"/><category scheme="https://blog.pgxn.org/tags" term="validation" label="Validation"/><category scheme="https://blog.pgxn.org/tags" term="ue" label="UE"/><category scheme="https://blog.pgxn.org/tags" term="ui" label="UI"/><category scheme="https://blog.pgxn.org/tags" term="degrade" label="Degrade"/><summary type="html"><![CDATA[<p>Last week I created the <a href="https://github.com/theory/pgxn-manager" title="PGXN Manager repository on GitHub">PGXN Manager</a> interface for requesting a PGXN user
account. It looks like this:</p>
<p><img src="/2010/account-moderation/request-account.png" alt="Request PGXN Account"></p>
<p>I really like the placeholder support in HTML 5, here nicely rendered by
Safari. I&rsquo;ve also used the jQuery <a href="https://docs.jquery.com/Plugins/Validation">Validation plugin</a> to validate the form
fields. So if you try to submit an incomplete form, it will complain before
submitting, like so:</p>
<p><img src="/2010/account-moderation/incomplete-form.png" alt="Incomplete PGXN Request Form"></p>
<p>The back end does similar validation if you have JavaScript disabled, so it
should degrade nicely. I think I might add a Twitter field so <a href="https://twitter.com/pgxn">@pgxn</a> can
credit users for their uploads, but otherwise this is done.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Last week I created the <a href="https://github.com/theory/pgxn-manager" title="PGXN Manager repository on GitHub">PGXN Manager</a> interface for requesting a PGXN user
account. It looks like this:</p>
<p><img src="/2010/account-moderation/request-account.png" alt="Request PGXN Account"></p>
<p>I really like the placeholder support in HTML 5, here nicely rendered by
Safari. I&rsquo;ve also used the jQuery <a href="https://docs.jquery.com/Plugins/Validation">Validation plugin</a> to validate the form
fields. So if you try to submit an incomplete form, it will complain before
submitting, like so:</p>
<p><img src="/2010/account-moderation/incomplete-form.png" alt="Incomplete PGXN Request Form"></p>
<p>The back end does similar validation if you have JavaScript disabled, so it
should degrade nicely. I think I might add a Twitter field so <a href="https://twitter.com/pgxn">@pgxn</a> can
credit users for their uploads, but otherwise this is done.</p>
<p>As with <a href="https://pause.perl.org/" title="The [Perl programming] Authors Upload Server">PAUSE</a>, an administrator must accept it or reject an account request.
Once your account is approved (likely unless you&rsquo;re a spammer), you&rsquo;ll be able
to upload distributions (I&rsquo;m going to do that part today). I finished the user
admin interface yesterday. Here&rsquo;s a screen snap:</p>
<p><img src="/2010/account-moderation/moderate-requests.png" alt="PGXN User Administration"></p>
<p>Some details. PGXN Manager will be hosted at <a href="https://manager.pgxn.org/"><code>https://manager.pgxn.org/</code></a>. It
uses basic auth for authentication; logged-in users will access
<a href="https://manager.pgxn.org/auth"><code>https://manager.pgxn.org/auth</code></a>. Administrators will have an extra menu item
to this screen, which will allow them to accept or reject account requests.
Its URI is <code>/auth/admin/moderate</code>. The admin can click the &ldquo;Play&rdquo; button to
see the requestor&rsquo;s note explaining why he should get an account. It&rsquo;s a
popover enabled by some jQuery code and looks like this:</p>
<p><img src="/2010/account-moderation/why-popover.png" alt="PGXN User Admin Why Popover"></p>
<p>Once an admin has read the request, she can accept it by clicking the blue
checkmark icon, or reject it by clicking the red minus icon. The former links
to <code>/auth/admin/accept/{nickname}</code> and the latter to
<code>/auth/admin/reject/{nickname}</code>. The submits are done by jQuery async requests
by default, but if you have JavaScript disabled they will submit as usual and
the back end will process the request and simply redirect to the moderation
screen.</p>
<p>This works very well, I think. It&rsquo;s an attractive interface and degrades
reasonably well. (Well, you can&rsquo;t read the request details if you have
JavaScript disabled, but the requests work nicely). The jQuery code fades out
a row after it has been accepted or rejected, so it&rsquo;s easy for an admin to do
a bunch of moderation all at once. And finally, the interface is driven by the
URLs, so I think it&rsquo;s pretty restful. The only ones I&rsquo;m not sure about are the
accept/reject URLs, because they have action names in them (&ldquo;accept&rdquo; and
&ldquo;reject&rdquo;). Is that RESTful? Or should they use GET query strings or something?</p>
<p>Okay, on to the upload interface. Wish me luck!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1205127396</id><title type="html">Quick Status Update</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/quick-status-update/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-09-28T17:29:48Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="status-update" label="Status Update"/><category scheme="https://blog.pgxn.org/tags" term="manager" label="Manager"/><category scheme="https://blog.pgxn.org/tags" term="contribute" label="Contribute"/><category scheme="https://blog.pgxn.org/tags" term="sponsor" label="Sponsor"/><summary type="html"><![CDATA[<p>Sometimes I wish I weren&rsquo;t such a perfectionist.</p>
<p>But not often.</p>
<p>A quick update:</p>
<p>So far, I&rsquo;ve spent 43 hours on the <a href="https://github.com/theory/pgxn-manager" title="PGXN Manager repository on GitHub">PGXN Manager</a> database. I had estimated 24
hours. Ha ha ha ha ha!</p>
<p>I&rsquo;ve spent 45 hours on the implementation of the app itself. I had estimated
40 hours.</p>
<p>How will I make up the difference? I&rsquo;m not sure, really. I&rsquo;ve already
under-recorded my hours quite a lot, basically donating my time to create
<a href="https://search.cpan.org/perldoc?SemVer">SemVer</a> and to create the basic HTML layout for the site (I suck at design,
but found <a href="https://andreasviklund.com/templates/andreas07/">a good template</a> to base it on). That will likely continue. This is
an OSS project, and while I&rsquo;m getting paid to work on it thanks to our
<a href="https://pgxn.org/contributors.html">generous contributors</a>, I&rsquo;m also contributing quite a lot of time to it.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Sometimes I wish I weren&rsquo;t such a perfectionist.</p>
<p>But not often.</p>
<p>A quick update:</p>
<p>So far, I&rsquo;ve spent 43 hours on the <a href="https://github.com/theory/pgxn-manager" title="PGXN Manager repository on GitHub">PGXN Manager</a> database. I had estimated 24
hours. Ha ha ha ha ha!</p>
<p>I&rsquo;ve spent 45 hours on the implementation of the app itself. I had estimated
40 hours.</p>
<p>How will I make up the difference? I&rsquo;m not sure, really. I&rsquo;ve already
under-recorded my hours quite a lot, basically donating my time to create
<a href="https://search.cpan.org/perldoc?SemVer">SemVer</a> and to create the basic HTML layout for the site (I suck at design,
but found <a href="https://andreasviklund.com/templates/andreas07/">a good template</a> to base it on). That will likely continue. This is
an OSS project, and while I&rsquo;m getting paid to work on it thanks to our
<a href="https://pgxn.org/contributors.html">generous contributors</a>, I&rsquo;m also contributing quite a lot of time to it.</p>
<p>Speaking of contributors, we&rsquo;ve still not quite met our fundraising goals.
There&rsquo;s enough there for me to finish PGXN Manager so that you can start
releasing your extensions on PGXN, and for me to start on the search and
documentation site, but not finish it. If you know any organizations that
would like to sponsor this work and get their name and link on the PGXN site
in perpetuity, please <a href="https://pgxn.org/">send them over!</a>.</p>
<p><em>Ahem.</em></p>
<p>So what have I been doing? I&rsquo;ll write up some notes in a few other blog posts,
including how I&rsquo;m generating JSON in the database, using <a href="https://plackperl.org/">Plack</a> for the app,
and nicely-degrading Ajaxification with <a href="https://jquery.org/">jQuery</a> and RESTful URLs (I hope!).
The main page of the app works, as does authentication via basic auth. There&rsquo;s
a screen to request a user account, and a UI for PGXN admins to accept or
reject such requests. To make it actually usable, I just need to add the
upload feature for users, and then I can do a first release. That will allow
people to sign up, get approved, and start uploading extensions for
distribution on PGXN.</p>
<p>I <em>will</em> get that that done this week.</p>
<p>After that, I&rsquo;ll add screens for users to edit their account information,
change passwords, reset passwords, and edit permissions. I expect each of
those to take less time, as they&rsquo;ll use a lot of the infrastructure I&rsquo;ve
already built.</p>
<p>Watch this space, and thanks for your patience!</p>
<p>Oh, and if you want to help out, please do fork <a href="https://github.com/theory/pgxn-manager" title="PGXN Manager repository on GitHub">PGXN::Manager</a>
and ping me in #pgxn on Freenode for the deets on getting it built.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1138292188</id><title type="html">Architecture, New Extension JSON Format</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/arch-and-extension-json/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-09-17T17:33:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="pgxn-manager" label="PGXN Manager"/><category scheme="https://blog.pgxn.org/tags" term="pgxnmanager" label="PGXN::Manager"/><category scheme="https://blog.pgxn.org/tags" term="plack" label="Plack"/><category scheme="https://blog.pgxn.org/tags" term="catalyst" label="Catalyst"/><category scheme="https://blog.pgxn.org/tags" term="dancer" label="Dancer"/><category scheme="https://blog.pgxn.org/tags" term="jifty" label="Jifty"/><category scheme="https://blog.pgxn.org/tags" term="extension" label="Extension"/><category scheme="https://blog.pgxn.org/tags" term="metadata" label="Metadata"/><summary type="html"><![CDATA[<p>I&rsquo;m making good progress on <a href="https://github.com/theory/pgxn-manager">PGXN Manager</a>. Hopefully I can start alpha
testing it next week. As I mentioned <a href="https://blog.pgxn.org/post/1082188310/db-status-update">previously</a>, I had estimated 40 hours of
work to create it, but was hoping to get it done in around 30 (because I spent
10 extra hours on the database design). So far I&rsquo;m at 23 hours, so it&rsquo;s
looking pretty good.</p>
<p>Architecturally, I&rsquo;ve gone for a very minimal <a href="https://plackperl.org/">Plack</a>-based app. No
<a href="https://www.catalystframework.org/">Catalyst</a>, <a href="https://jifty.org/">Jifty</a>, or even <a href="https://perldancer.org/">Dancer</a>. Just a very simple <a href="https://github.com/theory/pgxn-manager/blob/master/lib/PGXN/Manager/Router.pm">Plack app</a> that
uses <a href="https://search.cpan.org/perldoc?Router::Simple::Sinatraish">Router::Simple::Sinatraish</a> to route URIs to the appropriate
<a href="https://github.com/theory/pgxn-manager/blob/master/lib/PGXN/Manager/Controller.pm">controller</a> actions (which are just class methods). The controller just
dispatches to <a href="https://search.cpan.org/perldoc?Template::Declare">Template::Declare</a>-based <a href="https://github.com/theory/pgxn-manager/blob/master/lib/PGXN/Manager/Templates.pm">templates</a> for the HTML rendering. I
guess I&rsquo;ve kind of created my own framework here, but really, there ain&rsquo;t much
to it. This app is simple enough that I couldn&rsquo;t see the use of adding all the
overhead of a framework.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;m making good progress on <a href="https://github.com/theory/pgxn-manager">PGXN Manager</a>. Hopefully I can start alpha
testing it next week. As I mentioned <a href="https://blog.pgxn.org/post/1082188310/db-status-update">previously</a>, I had estimated 40 hours of
work to create it, but was hoping to get it done in around 30 (because I spent
10 extra hours on the database design). So far I&rsquo;m at 23 hours, so it&rsquo;s
looking pretty good.</p>
<p>Architecturally, I&rsquo;ve gone for a very minimal <a href="https://plackperl.org/">Plack</a>-based app. No
<a href="https://www.catalystframework.org/">Catalyst</a>, <a href="https://jifty.org/">Jifty</a>, or even <a href="https://perldancer.org/">Dancer</a>. Just a very simple <a href="https://github.com/theory/pgxn-manager/blob/master/lib/PGXN/Manager/Router.pm">Plack app</a> that
uses <a href="https://search.cpan.org/perldoc?Router::Simple::Sinatraish">Router::Simple::Sinatraish</a> to route URIs to the appropriate
<a href="https://github.com/theory/pgxn-manager/blob/master/lib/PGXN/Manager/Controller.pm">controller</a> actions (which are just class methods). The controller just
dispatches to <a href="https://search.cpan.org/perldoc?Template::Declare">Template::Declare</a>-based <a href="https://github.com/theory/pgxn-manager/blob/master/lib/PGXN/Manager/Templates.pm">templates</a> for the HTML rendering. I
guess I&rsquo;ve kind of created my own framework here, but really, there ain&rsquo;t much
to it. This app is simple enough that I couldn&rsquo;t see the use of adding all the
overhead of a framework.</p>
<p>Meanwhile, I&rsquo;ve been hacking on the <a href="https://github.com/theory/pgxn-manager/blob/master/lib/PGXN/Manager/Distribution.pm">distributon class</a>. This will be the core
class of the app. It takes a <a href="https://search.cpan.org/perldoc?Plack::Request::Upload">Plack upload</a> object and a username and does all
the rest, analyzing an uploaded archive, normalizing it if necessary,
registering it with the database, and indexing it by updating all the
appropriate JSON files on the mirrors. It&rsquo;s nearly finished, but I have one
other thing to do in the database, first.</p>
<p>In my <a href="https://blog.pgxn.org/post/1082188310/db-status-update">last update</a>, I asked for advice on whether or not PGXN
should allow an extension with the same version number to appear in multiple
distributions. And thanks to a <a href="https://blog.pgxn.org/post/1082188310/db-status-update#comment-75931198">comment from Aristotle</a>, I&rsquo;m changing it to
allow that. But it also means that my <a href="https://blog.pgxn.org/post/988613682/restful-directory#extension-json">original extension json spec</a> needs to
change.</p>
<p>Here&rsquo;s an example of what I&rsquo;m thinking. Say that there are three versions of
an extension named &ldquo;trip&rdquo;, and that they appear in distributions as follows:</p>
<pre tabindex="0"><code>trip 0.2.6
  pair-0.3.0
<p>trip 0.2.5
trip-0.2.2
pair-0.2.2rc
pair-0.2.1
trip-0.1.1</p>
<p>trip 0.2.4
pair-0.1.1rc
pair-0.1.0
</code></pre><p>So sometimes it’s in the “trip” distribution and other times it’s in the
“pair” distribution. My thought is that, for a given version, it would list
the distributions it’s in in reverse chronological order (by upload date). So
the format would be:</p></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="nt">&#34;latest&#34;</span><span class="p">:</span> <span class="s2">&#34;stable&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="nt">&#34;stable&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;pair&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.3.0&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">   <span class="nt">&#34;testing&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;pair&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.2.2rc&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">   <span class="nt">&#34;distributions&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;0.2.6&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">         <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;pair&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.3.0&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;0.2.5&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">         <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;trip&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.2.2&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">         <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;pair&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.2.2rc&#34;</span><span class="p">,</span> <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;testing&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">         <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;pair&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.2.1&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">         <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;trip&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.1.1&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;0.2.4&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">         <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;pair&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.1.1rc&#34;</span><span class="p">,</span> <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;testing&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">         <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;pair&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.1.0&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">]</span>
</span></span><span class="line"><span class="cl">   <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This way, every distribution it&rsquo;s included in is listed, and clients can
quickly tell where to find the latest stable, testing, and unstable versions,
and which of those is the most recent. This is a bit more convoluted than <a href="https://blog.pgxn.org/post/988613682/restful-directory#extension-json">the
original</a>, but I think is a good choice, in that
it&rsquo;s comprehensive but also easy to figure out what&rsquo;s the latest.</p>
<p>Unless you can think of a better format, this is what I&rsquo;m going with.
Comments?</p>
<p>Look for a post next week announcing an alpha program!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1088721089</id><title type="html">On Legacy File Systems</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/legacy-file-systems/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-09-08T23:41:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="uri-templates" label="URI Templates"/><category scheme="https://blog.pgxn.org/tags" term="file-systems" label="File Systems"/><category scheme="https://blog.pgxn.org/tags" term="hashing" label="Hashing"/><category scheme="https://blog.pgxn.org/tags" term="names" label="Names"/><category scheme="https://blog.pgxn.org/tags" term="mirrors" label="Mirrors"/><summary type="html"><![CDATA[<p>When we last looked at the <a href="https://blog.pgxn.org/post/988613682/restful-directory">organization of the mirror</a>, I was pretty happy
with the design except for one thing: The use of the letter hashing variables
in the URI templates. It&rsquo;s just ugly and, damnit, is it really necessary
anymore? I mean, sure, I had to <a href="https://github.com/bricoleurs/bricolage/commit/37c3ac85006503bde4240f642a315ff4c3fb425b">work around this issue in Bricolage</a> back in
2005, but on modern file systems like zfs and ext3, does it really matter
anymore?</p>
<p>I was chatting about this with <a href="https://schwern.dreamhosters.com/">Schwern</a> just now, and he thought it just
didn&rsquo;t matter anymore. So I asked my fellow <a href="https://pgexperts.com/">PGX</a> associate <a href="https://www.facebook.com/frosty996">Jeff Frost</a> about
this, and he said, &ldquo;Used to be anytime you went above 1000 entries in one
directory, things would start to slow down. Certainly reiserfs, xfs, jfs, and
zfs don&rsquo;t suffer that issue.&rdquo; But what about ext3?</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>When we last looked at the <a href="https://blog.pgxn.org/post/988613682/restful-directory">organization of the mirror</a>, I was pretty happy
with the design except for one thing: The use of the letter hashing variables
in the URI templates. It&rsquo;s just ugly and, damnit, is it really necessary
anymore? I mean, sure, I had to <a href="https://github.com/bricoleurs/bricolage/commit/37c3ac85006503bde4240f642a315ff4c3fb425b">work around this issue in Bricolage</a> back in
2005, but on modern file systems like zfs and ext3, does it really matter
anymore?</p>
<p>I was chatting about this with <a href="https://schwern.dreamhosters.com/">Schwern</a> just now, and he thought it just
didn&rsquo;t matter anymore. So I asked my fellow <a href="https://pgexperts.com/">PGX</a> associate <a href="https://www.facebook.com/frosty996">Jeff Frost</a> about
this, and he said, &ldquo;Used to be anytime you went above 1000 entries in one
directory, things would start to slow down. Certainly reiserfs, xfs, jfs, and
zfs don&rsquo;t suffer that issue.&rdquo; But what about ext3?</p>
<p>I happen to have a box with ext3, so I tested it. Here&rsquo;s what I found. To stat
one file among 20,000, <code>time</code> says:</p>
<pre tabindex="0"><code>real    0m0.005s
user    0m0.000s
sys     0m0.000s
</code></pre><p>Not bad. And on file among 200?</p>
<pre tabindex="0"><code>real    0m0.009s
user    0m0.000s
sys     0m0.010s
</code></pre><p>Well, you can&rsquo;t get much closer than that. What about subdirectories? To stat
a file inside one of 20,000 subdirectories, <code>time</code> tells me:</p>
<pre tabindex="0"><code>real    0m0.015s
user    0m0.010s
sys     0m0.000s
</code></pre><p>And a file inside one of 200 subdirectories:</p>
<pre tabindex="0"><code>real    0m0.005s
user    0m0.000s
sys     0m0.000s
</code></pre><p>Well, I can live with that. I suppose there are some file systems out there
that still have this problem, but you know what? I&rsquo;m not going to worry about
them. It&rsquo;s thinking too far in advance anyway (if PGXN has this kind of
scaling problem we&rsquo;ll be lucky!), and the farther out we get, the less of a
problem it is.</p>
<p>So you know what? I&rsquo;m not going to use the hashing of extension, distribution,
and owner names. Let the file systems worry about that performance, not me.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1082188310</id><title type="html">Status Update: DB API, Extension Versions RFC</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/db-status-update/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-09-07T18:46:38Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="status-update" label="Status Update"/><category scheme="https://blog.pgxn.org/tags" term="database" label="Database"/><category scheme="https://blog.pgxn.org/tags" term="api" label="API"/><category scheme="https://blog.pgxn.org/tags" term="documentation" label="Documentation"/><category scheme="https://blog.pgxn.org/tags" term="extensions" label="Extensions"/><category scheme="https://blog.pgxn.org/tags" term="versions" label="Versions"/><category scheme="https://blog.pgxn.org/tags" term="rfc" label="RFC"/><summary type="html"><![CDATA[I spent most of the time I had to work on PGXN the last two weeks creating the
database for <a href="https://github.com/theory/pgxn-manager/">PGXN Manager</a>. I had estimated 24 hours of work to design the
database. So far I&rsquo;ve logged 34 hours. But I think that will come out in the
wash, really, because I did a lot of work to generate JSON from database
functions. This is code I had originally expected to do in the app layer. I&rsquo;m
starting work on that this week, with an estimated 40 hours. Hope I can do it
in 30. :-)]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I spent most of the time I had to work on PGXN the last two weeks creating the
database for <a href="https://github.com/theory/pgxn-manager/">PGXN Manager</a>. I had estimated 24 hours of work to design the
database. So far I&rsquo;ve logged 34 hours. But I think that will come out in the
wash, really, because I did a lot of work to generate JSON from database
functions. This is code I had originally expected to do in the app layer. I&rsquo;m
starting work on that this week, with an estimated 40 hours. Hope I can do it
in 30. :-)</p>
<p>I&rsquo;m pretty happy with how the database API is turning out. See the nifty
<a href="https://github.com/theory/pgxn-manager/wiki/DB-API">database API documentation</a> I whipped up using <a href="https://github.com/theory/pgxn-manager/blob/master/bin/gendoc">gendoc</a> and embedded
<a href="https://fletcherpenney.net/multimarkdown/users_guide/multimarkdown_syntax_guide/">MultiMarkdown</a>. There are still a few tweaks I need to make. I just checked
in a change to eliminate all the <code>ALIAS</code>es I <a href="https://blog.pgxn.org/post/1053165383/alias-in-vogue">blogged about</a> last week. Turns
out that one can just <a href="https://archives.postgresql.org/pgsql-hackers/2010-09/msg00404.php">function-name-qualify</a> the function parameter names.
Who knew? I sure didn&rsquo;t.</p>
<p>But I do have a question for you, dear readers. Right now, the database
requires that extensions have unique version numbers in every distribution. So
if, for example, you uploaded the distribution foo 1.2.2 with the extension
bar 1.2.2, when you next uploaded foo 1.2.3, bar could not be 1.2.2. I
designed this this way by following my own practice of always incrementing the
version numbers of all modules included in my <a href="https://search.cpan.org/~dwheeler/">CPAN distributions</a>. So all
modules have unique version numbers, with never a duplicate.</p>
<p>However, CPAN itself only cares that distributions have unique version
numbers. It doesn&rsquo;t care about the version numbers of included modules. See,
for example, <a href="https://search.cpan.org/dist/HTML-Mason/">HTML::Mason</a>. Note that the various included modules have all
sorts of different version numbers, and many have no version numbers at all.
So while the core module has a version number (1.45 at the time of this
writing), if you click to look at older versions, say <a href="https://search.cpan.org/~drolsky/HTML-Mason-1.44/">Mason 1.44</a>, you&rsquo;ll see
that no other modules have their version numbers changed.</p>
<p>I don&rsquo;t think I want to allow extensions without version numbers on PGXN.
That&rsquo;s been a recipe for many annoyances with CPAN. But maybe I should allow
extension version numbers to repeat? The upside is less maintenance for
multi-extension distribution authors. The downside is potentially less
accuracy in the determination of prerequisites.</p>
<p>For example, say that we have two versions of distribution foo:</p>
<pre tabindex="0"><code>  -----------------------------------------------------------------------------
  Distribution                           Extensions
  -------------------------------------- --------------------------------------
  foo 1.2.2                              foo 1.2.2\
                                         bar 1.2.2
<h2 id="bar-122">foo 1.2.3                              foo 1.2.3<br>
bar 1.2.2</h2>
<p></code></pre><p>Note how the version number of extension bar has not changed between releases.
But if I was a user of both foo and bar, and I specified that I required bar
1.2.2 but was actually using stuff in foo 1.2.3, I could end up with errors
because I would only have foo 1.2.3.</p></p>
<p>This is an issue periodically faced by Perl hackers. I might require
HTML::Mason::ApacheHandler 1.69 but really what I need is HTML::Mason 1.44. Of
course, as a Perl hacker, it&rsquo;s my responsibility to make sure I require
exactly what I need, but sometimes it&rsquo;s not clear what&rsquo;s the primary module in
a distribution. And if I don&rsquo;t realize that the version number of
HTML::Mason::ApacheHandler doesn&rsquo;t change with every release, I might make
mistakes.</p>
<p>Maybe that&rsquo;s okay. Maybe PGXN should be less rigid like this. But I&rsquo;m leaning
toward keeping things as they are and requiring new versions of every
extension in every upload of a distribution. It&rsquo;s a bit tougher for those
(few?) hackers who will create multi-extension distributions, but probably
more reliable for everyone else.</p>
<p>What do you think?</p>
<p>Anyway, I&rsquo;m getting to work on the Web app this week while this issue
percolates. Been studying up on <a href="https://plackperl.org/">Plack</a>. So nice!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/1053165383</id><title type="html">ALIAS Back in Vogue</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/alias-in-vogue/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-09-02T13:00:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="alias" label="Alias"/><category scheme="https://blog.pgxn.org/tags" term="postgresql-9" label="PostgreSQL 9"/><category scheme="https://blog.pgxn.org/tags" term="pl/pgsql" label="PL/pgSQL"/><category scheme="https://blog.pgxn.org/tags" term="postgresql" label="PostgreSQL"/><category scheme="https://blog.pgxn.org/tags" term="function" label="Function"/><category scheme="https://blog.pgxn.org/tags" term="parameter" label="Parameter"/><category scheme="https://blog.pgxn.org/tags" term="named-parameter" label="Named Parameter"/><summary type="html"><![CDATA[<p>I&rsquo;ve been hard at work on <a href="https://github.com/theory/pgxn-manager">PGXN Manager</a>, the app for users to upload
distributions to PGXN. I am of course following my own dictum: &ldquo;the database
<em>is</em> the model.&rdquo; As a result, I&rsquo;ve been creating an API for creating,
updating, and deleting entities, as well as generating JSON (more on that
later).</p>
<p>I&rsquo;m also using PostgreSQL 9. The motivation to make the jump to 9.0 was to to
try to use the <a href="https://commitfest.postgresql.org/action/patch_view?id=351">JSON data type patch</a>, but I abandoned it when I couldn&rsquo;t get
it to compile. (I might come back to it later, but right now I&rsquo;m trying to
practice <a href="https://en.wikipedia.org/wiki/YAGNI">YAGNI</a> and <a href="https://www.urbandictionary.com/define.php?term=JFDI">JFDI</a> so that I don&rsquo;t end up owning a yak farm). But
there are other reasons to stick with 9.0, like improved <a href="https://developer.postgresql.org/pgdocs/postgres/hstore.html">hstore</a> support, the
<a href="https://developer.postgresql.org/pgdocs/postgres/sql-do.html">DO</a> statement, and <a href="https://developer.postgresql.org/pgdocs/postgres/sql-syntax-calling-funcs.html#SQL-SYNTAX-CALLING-FUNCS-NAMED">named parameters</a>.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;ve been hard at work on <a href="https://github.com/theory/pgxn-manager">PGXN Manager</a>, the app for users to upload
distributions to PGXN. I am of course following my own dictum: &ldquo;the database
<em>is</em> the model.&rdquo; As a result, I&rsquo;ve been creating an API for creating,
updating, and deleting entities, as well as generating JSON (more on that
later).</p>
<p>I&rsquo;m also using PostgreSQL 9. The motivation to make the jump to 9.0 was to to
try to use the <a href="https://commitfest.postgresql.org/action/patch_view?id=351">JSON data type patch</a>, but I abandoned it when I couldn&rsquo;t get
it to compile. (I might come back to it later, but right now I&rsquo;m trying to
practice <a href="https://en.wikipedia.org/wiki/YAGNI">YAGNI</a> and <a href="https://www.urbandictionary.com/define.php?term=JFDI">JFDI</a> so that I don&rsquo;t end up owning a yak farm). But
there are other reasons to stick with 9.0, like improved <a href="https://developer.postgresql.org/pgdocs/postgres/hstore.html">hstore</a> support, the
<a href="https://developer.postgresql.org/pgdocs/postgres/sql-do.html">DO</a> statement, and <a href="https://developer.postgresql.org/pgdocs/postgres/sql-syntax-calling-funcs.html#SQL-SYNTAX-CALLING-FUNCS-NAMED">named parameters</a>.</p>
<p>Actually, this last one is very nice, as it allows me to use SQL syntax to
specify function parameters instead of using an <code>hstore</code> value to hack it. For
example, I have this function for update a user record:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">CREATE</span><span class="w"> </span><span class="k">OR</span><span class="w"> </span><span class="k">REPLACE</span><span class="w"> </span><span class="k">FUNCTION</span><span class="w"> </span><span class="n">update_user</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">nick</span><span class="w">  </span><span class="n">LABEL</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">name</span><span class="w">  </span><span class="nb">TEXT</span><span class="w">  </span><span class="k">DEFAULT</span><span class="w"> </span><span class="k">NULL</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">email</span><span class="w"> </span><span class="n">EMAIL</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="k">NULL</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">uri</span><span class="w">   </span><span class="n">URI</span><span class="w">   </span><span class="k">DEFAULT</span><span class="w"> </span><span class="k">NULL</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w"> </span><span class="k">RETURNS</span><span class="w"> </span><span class="nb">BOOLEAN</span><span class="w"> </span><span class="k">LANGUAGE</span><span class="w"> </span><span class="n">plpgsql</span><span class="w"> </span><span class="k">SECURITY</span><span class="w"> </span><span class="k">DEFINER</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="err">$$</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">DECLARE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">_email</span><span class="w"> </span><span class="k">ALIAS</span><span class="w"> </span><span class="k">FOR</span><span class="w"> </span><span class="n">email</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">_uri</span><span class="w">   </span><span class="k">ALIAS</span><span class="w"> </span><span class="k">FOR</span><span class="w"> </span><span class="n">uri</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">BEGIN</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">UPDATE</span><span class="w"> </span><span class="n">users</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">SET</span><span class="w"> </span><span class="n">full_name</span><span class="w">  </span><span class="o">=</span><span class="w"> </span><span class="n">COALESCE</span><span class="p">(</span><span class="n">name</span><span class="p">,</span><span class="w">   </span><span class="n">full_name</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">email</span><span class="w">      </span><span class="o">=</span><span class="w"> </span><span class="n">COALESCE</span><span class="p">(</span><span class="n">_email</span><span class="p">,</span><span class="w"> </span><span class="n">users</span><span class="p">.</span><span class="n">email</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">uri</span><span class="w">        </span><span class="o">=</span><span class="w"> </span><span class="n">COALESCE</span><span class="p">(</span><span class="n">_uri</span><span class="p">,</span><span class="w">   </span><span class="n">users</span><span class="p">.</span><span class="n">uri</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">updated_at</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">NOW</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">WHERE</span><span class="w"> </span><span class="n">nickname</span><span class="w">   </span><span class="o">=</span><span class="w"> </span><span class="n">nick</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">AND</span><span class="w"> </span><span class="n">status</span><span class="w">     </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;active&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">RETURN</span><span class="w"> </span><span class="k">FOUND</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">END</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="err">$$</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>The nice thing about named parameters is that I can call this function like
so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">update_user</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">nick</span><span class="w">  </span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;theory&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">name</span><span class="w">  </span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;David E. Wheeler&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">email</span><span class="w"> </span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;justatheory@pgxn.org&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">uri</span><span class="w">   </span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;https://www.justatheory.com/&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>Hell, since the parameters are named, I can specify them in any order. And
because there are defaults, I can omit one or more of them:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">update_user</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">name</span><span class="w">  </span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;David E. Wheeler&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">email</span><span class="w"> </span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;justatheory@pgxn.org&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">nick</span><span class="w">  </span><span class="p">:</span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;theory&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>And it will just work. Nice!</p>
<p>One thing I did discover, though: Now that I&rsquo;m using named parameters, I don&rsquo;t
want to use <a href="https://en.wikipedia.org/wiki/Hungarian_notation">Hungarian notation</a> for the parameter names. Back before 9.0,
when callers never saw argument names when calling a function, I could call
them whatever I wanted. This was especially important when executing queries
in PL/pgSQL, because one needs to be aware of parameter (and variable!) names
that conflict with SQL identifiers, especially columns. (Another nice feature
in 9.0 is that it will throw an exception if you create a PL/pgSQL function
with conflicting parameter and identifier names.) But now that the parameter
names are more likely to be exposed and <em>used</em> to the function caller, I want
them to be meaningful.</p>
<p>I messed with this for a while. I was okay with using &ldquo;nick&rdquo; for &ldquo;nickname&rdquo;
and &ldquo;pass&rdquo; for &ldquo;password&rdquo;, but was annoyed with the alternatives for &ldquo;email&rdquo;
and &ldquo;uri,&rdquo; especially since they&rsquo;re used with those names elsewhere (queries
against the table, generated JSON). I really wanted to use parameter names
that were the same as the columns. I tried just using the dollar variables
(<code>$1</code>, <code>$2</code>, etc.), but got errors for them, too: they seem to be compiled
into the parameter names.</p>
<p>Then, on a guess, I tried creating aliases for the variables. From the example
above, its:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="w">    </span><span class="n">_email</span><span class="w"> </span><span class="k">ALIAS</span><span class="w"> </span><span class="k">FOR</span><span class="w"> </span><span class="n">email</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">_uri</span><span class="w">   </span><span class="k">ALIAS</span><span class="w"> </span><span class="k">FOR</span><span class="w"> </span><span class="n">uri</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>Then I used <code>_email</code> and <code>_uri</code> in my <code>UPDATE</code> statement. And what do you
know, it worked! I found this somewhat humorous, given the history of <code>ALIAS</code>.</p>
<p>It used to be, long ago, that you couldn&rsquo;t use the parameter names in the body
of PL/pgSQL functions, so <code>ALIAS</code> was there to let you alias the dollar
variable names to other names. But somewhere around, oh, 8.0 or so, we were
blessed with the ability to use the parameter names directly. Suddenly <code>ALIAS</code>
seemed superfluous. I&rsquo;ve hardly ever used it myself. It&rsquo;s just been sitting
there, like my appendix, waiting for another use.</p>
<p>And now there is one. A really good one! I can safely use parameter names that
are the same as column names in my PL/pgSQL functions as long as I alias them.
And it just works!</p>
<p>Well, almost. It seems that <code>ALIAS</code> means what it says: the parameter names
are still around can can be used. So sometimes you might run into an error
like</p>
<pre tabindex="0"><code>ERROR:  column reference &#34;email&#34; is ambiguous
</code></pre><p>Even though you&rsquo;re not using the variable. I ran into this in the update
function where I was using the column names in the left-hand side of the <code>SET</code>
expressions. The solution, fortunately, is simple: table-qualify the column
names as appropriate:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">UPDATE</span><span class="w"> </span><span class="n">users</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="k">SET</span><span class="w"> </span><span class="n">full_name</span><span class="w">  </span><span class="o">=</span><span class="w"> </span><span class="n">COALESCE</span><span class="p">(</span><span class="n">name</span><span class="p">,</span><span class="w">   </span><span class="n">full_name</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">       </span><span class="n">email</span><span class="w">      </span><span class="o">=</span><span class="w"> </span><span class="n">COALESCE</span><span class="p">(</span><span class="n">_email</span><span class="p">,</span><span class="w"> </span><span class="n">users</span><span class="p">.</span><span class="n">email</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">       </span><span class="n">uri</span><span class="w">        </span><span class="o">=</span><span class="w"> </span><span class="n">COALESCE</span><span class="p">(</span><span class="n">_uri</span><span class="p">,</span><span class="w">   </span><span class="n">users</span><span class="p">.</span><span class="n">uri</span><span class="p">),</span><span class="w">
</span></span></span></code></pre></div><p>Note the use of <code>users.email</code> instead of just <code>email</code> in the <code>COALESCE()</code>
function. Seems like a reasonable workaround in exchange for the ability to
have parameter names that match column names. I&rsquo;m sold!</p>
<p>Now just to consider whether to change the <code>nick</code> and <code>pass</code> parameter names
to <code>nickname</code> and <code>password</code> in order to be completely consistent. I guess
it&rsquo;s a good idea.</p>
<p>More next week. I&rsquo;ve been doing lots of hacking and have much to share, but
have another project that will take up my time between now and Monday, so I&rsquo;ll
have to come back to it.</p>
<p><strong>Update 2010-08-07:</strong> I turns out that there is a much better way to do this:
Just function-name-qualify parameter names where they might conflict with
database object names:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">UPDATE</span><span class="w"> </span><span class="n">users</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">   </span><span class="k">SET</span><span class="w"> </span><span class="n">full_name</span><span class="w">  </span><span class="o">=</span><span class="w"> </span><span class="n">COALESCE</span><span class="p">(</span><span class="n">name</span><span class="p">,</span><span class="w">   </span><span class="n">full_name</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">       </span><span class="n">email</span><span class="w">      </span><span class="o">=</span><span class="w"> </span><span class="n">COALESCE</span><span class="p">(</span><span class="n">update_users</span><span class="p">.</span><span class="n">email</span><span class="p">,</span><span class="w"> </span><span class="n">users</span><span class="p">.</span><span class="n">email</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">       </span><span class="n">uri</span><span class="w">        </span><span class="o">=</span><span class="w"> </span><span class="n">COALESCE</span><span class="p">(</span><span class="n">update_users</span><span class="p">.</span><span class="n">uri</span><span class="p">,</span><span class="w">   </span><span class="n">users</span><span class="p">.</span><span class="n">uri</span><span class="p">),</span><span class="w">
</span></span></span></code></pre></div><p>No need for the aliases at all! I had no idea about this feature. Many thanks
to Colin &rsquo;t Hart for the comment below about how this is available in Oracle
and to Tom Lane for <a href="https://archives.postgresql.org/pgsql-hackers/2010-09/msg00404.php">smacking me upside the head</a> with <a href="https://www.postgresql.org/docs/9.0/static/plpgsql-structure.html">the fine manual</a> (look
for the &ldquo;note&rdquo; at the bottom) when I asked about it.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/988613682</id><title type="html">A RESTful Directory</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/restful-directory/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-08-21T18:52:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="mirror" label="Mirror"/><category scheme="https://blog.pgxn.org/tags" term="directory" label="Directory"/><category scheme="https://blog.pgxn.org/tags" term="structure" label="Structure"/><category scheme="https://blog.pgxn.org/tags" term="meta" label="Meta"/><category scheme="https://blog.pgxn.org/tags" term="metadata" label="Metadata"/><category scheme="https://blog.pgxn.org/tags" term="json" label="JSON"/><category scheme="https://blog.pgxn.org/tags" term="api" label="API"/><category scheme="https://blog.pgxn.org/tags" term="scalability" label="Scalability"/><category scheme="https://blog.pgxn.org/tags" term="rest" label="REST"/><summary type="html"><![CDATA[Following my post outlining a possible network <a href="https://blog.pgxn.org/post/954535657/thoughts-on-the-network-directory-structure">directory structure</a>,
<a href="https://plasmasturm.org/">Aristotle Pagaltzis</a> saw fit to bug me via email about a different approach.
I couldn&rsquo;t understand WTF he was talking about until today. Then it lit my
brain on fire. As a result, I now think that there is a much better way to
organize the metadata files for the PGXN &ndash; one that happens not to include
any symbolic links (which is something that <a href="https://search.cpan.org/~andk/">Andreas König</a> has been flagging,
via email, as a possible bottleneck).]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Following my post outlining a possible network <a href="https://blog.pgxn.org/post/954535657/thoughts-on-the-network-directory-structure">directory structure</a>,
<a href="https://plasmasturm.org/">Aristotle Pagaltzis</a> saw fit to bug me via email about a different approach.
I couldn&rsquo;t understand WTF he was talking about until today. Then it lit my
brain on fire. As a result, I now think that there is a much better way to
organize the metadata files for the PGXN &ndash; one that happens not to include
any symbolic links (which is something that <a href="https://search.cpan.org/~andk/">Andreas König</a> has been flagging,
via email, as a possible bottleneck).</p>
<p>First, the <code>/dist</code> directory will be the same as before. Releases of pgTAP
would be in:</p>
<pre tabindex="0"><code>dist/p/pg/pgtap/pgtap-0.23.pgz
dist/p/pg/pgtap/pgtap-0.23.json
dist/p/pg/pgtap/pgtap-0.23.readme
dist/p/pg/pgtap/pgtap-0.24.pgz
dist/p/pg/pgtap/pgtap-0.24.json
dist/p/pg/pgtap/pgtap-0.24.readme
dist/p/pg/pgtap/pgtap-0.25.pgz
dist/p/pg/pgtap/pgtap-0.25.json
dist/p/pg/pgtap/pgtap-0.25.readme
</code></pre><p>The only change is that the <code>pgtap.json</code> symlink is gone.</p>
<p>Now, the new stuff. In the root directory will be a file, <code>index.json</code>, that
contains templates for URIs. It will look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;dist&#34;</span><span class="p">:</span>   <span class="s2">&#34;/dist/$a/$ab/$dist/$dist-$version.pgz&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;readme&#34;</span><span class="p">:</span> <span class="s2">&#34;/dist/$a/$ab/$dist/$dist-$version.readme&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;meta&#34;</span><span class="p">:</span>   <span class="s2">&#34;/dist/$a/$ab/$dist/$dist-$version.json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;by-dist&#34;</span><span class="p">:</span>      <span class="s2">&#34;/by/dist/$a/$ab/$dist.json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;by-extension&#34;</span><span class="p">:</span> <span class="s2">&#34;/by/extension/$a/$ab/$extension.json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;by-owner&#34;</span><span class="p">:</span>     <span class="s2">&#34;/by/owner/$a/$ab/$owner.json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;by-manager&#34;</span><span class="p">:</span>   <span class="s2">&#34;/by/manager/$a/$ab/$manager.json&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The PGXN client will always fetch this file before it does anything else,
because the file tells it how to find stuff. The advantage here is that the
client doesn&rsquo;t have to know anything about how the directory is actually
organized, just what the template variables might be. They are:</p>
<ul>
<li><code>$dist</code>: A <a href="https://github.com/theory/pgxn/wiki/PGXN-Meta-Spec#name">distribution name</a></li>
<li><code>$version</code>: A <a href="https://github.com/theory/pgxn/wiki/PGXN-Meta-Spec#version">version number</a></li>
<li><code>$extension</code>: An <a href="https://github.com/theory/pgxn/wiki/PGXN-Meta-Spec#provides">extension name</a></li>
<li><code>$owner</code>: An <a href="https://github.com/theory/pgxn/wiki/PGXN-Meta-Spec#owner">owner&rsquo;s name</a></li>
<li><code>$manager</code>: A release manager&rsquo;s name (managers are the people who upload
distributions to PGXN)</li>
<li><code>$a</code>: The first letter of a distribution, extension, owner, or manager name.</li>
<li><code>$ab</code>: The first two letters of a distribution, extension, owner, or manager
name.</li>
</ul>
<p>I&rsquo;m not thrilled about using prefix-staggering to avoid having too many files
in a directory. But the truth is that this approach allows me to punt. I could
also make sure the client supports, for example, <code>$bc</code> and <code>$cd</code>, so that one
could stagger things differently. And then the nice thing is that I don&rsquo;t have
to use those at all. The templates will tell the client exactly how to
construct the URIs for things, and the templates needn&rsquo;t include those
staggering variables if they&rsquo;re not appropriate. The client won&rsquo;t care because
it will have no built-in knowledge of how things are organized. It will have
to find out from <code>index.json</code>.</p>
<p>From the URI templates, you can now see where the other metadata will be
stored. For extension names, a hypothetical pgTAP distribution with two
extensions will have a JSON file for each extension:</p>
<pre tabindex="0"><code>/by/extension/p/pg/pgtap.json
/by/extension/s/sc/schematap.json
</code></pre><p>The <code>pgtap.json</code> file will look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;stable&#34;</span><span class="err">:</span>   <span class="s2">&#34;0.25.0&#34;</span><span class="err">,</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;testing&#34;</span><span class="err">:</span>  <span class="s2">&#34;0.26.0b1&#34;</span><span class="err">,</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;unstable&#34;</span><span class="err">:</span> <span class="s2">&#34;0.30.0u&#34;</span><span class="err">,</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;versions&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;0.26.0b1&#34;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;pgtap&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.26.0b1&#34;</span><span class="p">,</span> <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;testing&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;0.30.0u&#34;</span><span class="p">:</span>  <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;pgtap&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.30.0u&#34;</span><span class="p">,</span>  <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;unstable&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;0.25.0&#34;</span><span class="p">:</span>   <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;pgtap&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.25.0&#34;</span><span class="p">,</span>   <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;stable&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;0.24.0&#34;</span><span class="p">:</span>   <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;pgtap&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.24.0&#34;</span><span class="p">,</span>   <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;stable&#34;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;0.25.0&#34;</span><span class="p">:</span>   <span class="p">{</span> <span class="nt">&#34;dist&#34;</span><span class="p">:</span> <span class="s2">&#34;pgtap&#34;</span><span class="p">,</span> <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.23.0&#34;</span><span class="p">,</span>   <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;stable&#34;</span>  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Right at the top, it would always list the most recent stable, testing, and
unstable version number, and then it would have a list metadata for all
versions. Said metadata would include the associated distribution name,
version, and release status.</p>
<p>Here&rsquo;s how it would work. Say I ask the client to install pgtap:</p>
<pre tabindex="0"><code>PGXN&gt; install extension pgtap
</code></pre><p>The client would first fetch <code>/index.json</code>, then look for the URI template for
&ldquo;by-extension&rdquo;, which is <code>/by/extension/$a/$ab/$extension.json</code>. Filling in
the template, it would know to request <code>/by/extension/p/pg/pgtap.json</code>. With
that file, it would see that the most recent stable version is in the &ldquo;pgtap&rdquo;
distribution version 0.25.0. Using the <code>dist</code> URI template, which is
<code>/dist/$a/$ab/$dist-$version.pgz</code>, it would then fetch
<code>/dist/p/pg/pgtap/pgtap-0.25.0.pgz</code>.</p>
<p>The advantage here is that there are no symbolic links and no knowledge of the
directory structure built into clients. The client just knows to fetch
<code>/index.json</code> and then to use the templates in that file to fetch other
information. That&rsquo;s the whole interface. Very <a href="https://en.wikipedia.org/wiki/REST">REST</a>ful.</p>
<p>The structure of the other <code>/by</code> files would be similar. For</p>
<pre><code>PGXN&gt; install dist pgtap
</code></pre>
<p>the client would use the &ldquo;by-dist&rdquo; URI template to construct the URL
<code>/by/dist/p/pg/pgtap.json</code>. That file would have something like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;stable&#34;</span><span class="err">:</span>   <span class="s2">&#34;0.25.0&#34;</span><span class="err">,</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;testing&#34;</span><span class="err">:</span>  <span class="s2">&#34;0.26.0b1&#34;</span><span class="err">,</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;unstable&#34;</span><span class="err">:</span> <span class="s2">&#34;0.30.0u&#34;</span><span class="err">,</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;versions&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;0.26.0b1&#34;</span><span class="p">:</span> <span class="s2">&#34;testing&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;0.30.0u&#34;</span><span class="p">:</span>  <span class="s2">&#34;unstable&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;0.25.0&#34;</span><span class="p">:</span>   <span class="s2">&#34;stable&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;0.24.0&#34;</span><span class="p">:</span>   <span class="s2">&#34;stable&#34;</span> <span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;0.23.0&#34;</span><span class="p">:</span>   <span class="s2">&#34;stable&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>So then the client would know that &ldquo;0.25.0&rdquo; was the most recent version, and
use the <code>dist</code> URI template to request <code>/dist/p/pg/pgtap/pgtap-0.25.0.pgz</code>.</p>
<p>If The client command had been:</p>
<pre tabindex="0"><code>PGXN&gt; readme dist pgtap
</code></pre><p>It would use the <code>readme</code> URI template. And the command:</p>
<pre tabindex="0"><code>PGXN&gt; meta dist pgtap
</code></pre><p>Would use the <code>meta</code> URI template to fetch the metadata for the distribution.</p>
<p>If the client had requested a specific version:</p>
<pre tabindex="0"><code>PGXN&gt; install dist 0.23.0
</code></pre><p>It could either use the <code>by-dist</code> URI template to download the list of all
versions to see if 0.23.0 was valid, or just use the <code>dist</code> URI template to
try to download the distribution itself.</p>
<p>And finally, the owner and manager JSON files, such as</p>
<pre tabindex="0"><code>/owner/t/th/theory.json
</code></pre><p>Would look something like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;full_name&#34;</span><span class="err">:</span> <span class="s2">&#34;David Wheeler&#34;</span><span class="err">,</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;email&#34;</span><span class="err">:</span> <span class="s2">&#34;theory@pgxn.org&#34;</span><span class="err">,</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;uri&#34;</span><span class="err">:</span> <span class="s2">&#34;https://justatheory.com&#34;</span><span class="err">,</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;distributions&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;pgtap&#34;</span><span class="p">:</span> <span class="p">[</span> <span class="s2">&#34;0.25.0&#34;</span><span class="p">,</span> <span class="s2">&#34;0.24.0&#34;</span><span class="p">,</span> <span class="s2">&#34;0.23.0&#34;</span> <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;pair&#34;</span><span class="p">:</span> <span class="p">[</span> <span class="s2">&#34;0.2.0&#34;</span><span class="p">,</span> <span class="s2">&#34;0.1.0&#34;</span><span class="p">,</span> <span class="s2">&#34;0.0.5&#34;</span> <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>With that, the client can be asked to fetch metadata for a given owner name
and use it to figure out what distributions and versions the the owner, um,
owns. One could then fetch the metadata, readme, or distribution file for any
of those distributions and versions.</p>
<p>Overall, I think that this is a much better solution than I outlined
<a href="https://blog.pgxn.org/post/954535657/thoughts-on-the-network-directory-structure">before</a>. If only I could figure out something more
elegant that the prefix-staggering/hashing stuff, it would be just about
perfect.</p>
<p>Thoughts?</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/966989188</id><title type="html">RFC: The PGXN Metadata Specification</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/meta-spec-rfc/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-08-17T13:00:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="meta" label="Meta"/><category scheme="https://blog.pgxn.org/tags" term="meta-spec" label="Meta Spec"/><category scheme="https://blog.pgxn.org/tags" term="metadata" label="Metadata"/><category scheme="https://blog.pgxn.org/tags" term="json" label="JSON"/><category scheme="https://blog.pgxn.org/tags" term="cpan-meta-spec" label="CPAN Meta Spec"/><category scheme="https://blog.pgxn.org/tags" term="david-golden" label="David Golden"/><category scheme="https://blog.pgxn.org/tags" term="version-numbers" label="Version Numbers"/><category scheme="https://blog.pgxn.org/tags" term="prerequisites" label="Prerequisites"/><category scheme="https://blog.pgxn.org/tags" term="license" label="License"/><summary type="html"><![CDATA[<p>I&rsquo;ve posted a draft of the &ldquo;PGXN distribution metadata specification,&rdquo; or
<a href="https://github.com/theory/pgxn/wiki/PGXN-Meta-Spec">PGXN Meta Spec</a>.&quot; This document specifies the structure and format of the
<code>META.json</code> file that PGXN will require in every distribution. In fact, this
is the <em>only</em> required file in a distribution. Its job is to describe the
distribution, its extensions, and its dependencies, among other things. This
file is key to the whole thing.</p>
<p>To create it, I&rsquo;ve ported the <a href="https://search.cpan.org/perldoc?CPAN::Meta::Sepc">CPAN Meta Spec</a>, version 2, but with all
deprecated fields removed, and some of the more complex stuff taken out. I
also made a couple of the &ldquo;required&rdquo; fields &ldquo;optional.&rdquo; At its simplest, the
file might look something like this:</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;ve posted a draft of the &ldquo;PGXN distribution metadata specification,&rdquo; or
<a href="https://github.com/theory/pgxn/wiki/PGXN-Meta-Spec">PGXN Meta Spec</a>.&quot; This document specifies the structure and format of the
<code>META.json</code> file that PGXN will require in every distribution. In fact, this
is the <em>only</em> required file in a distribution. Its job is to describe the
distribution, its extensions, and its dependencies, among other things. This
file is key to the whole thing.</p>
<p>To create it, I&rsquo;ve ported the <a href="https://search.cpan.org/perldoc?CPAN::Meta::Sepc">CPAN Meta Spec</a>, version 2, but with all
deprecated fields removed, and some of the more complex stuff taken out. I
also made a couple of the &ldquo;required&rdquo; fields &ldquo;optional.&rdquo; At its simplest, the
file might look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;pgTAP&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;abstract&#34;</span><span class="p">:</span> <span class="s2">&#34;Unit testing for PostgreSQL&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.25.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;owner&#34;</span><span class="p">:</span> <span class="s2">&#34;David E. Wheeler &lt;theory@pgxn.org&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;license&#34;</span><span class="p">:</span> <span class="s2">&#34;postgresql&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;meta-spec&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://github.com/theory/pgxn/wiki/PGXN-Meta-Spec&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Not too bad, eh? The URL for the spec might change (might move it to the main
site and/or the mirrors), but otherwise, I think this is pretty solid. Not too
much work to deal with, and reasonably easy to create by hand (which is likely
how we&rsquo;ll all start out).</p>
<p>And additional key that will be really important for the PGXN client is
<code>prereqs</code>. This key allows you to identify the prerequisites (from PGXN or the
PostgreSQL core contrib extensions) required to build, test, and/or use a
distribution. For example, if I were to release an <a href="https://justatheory.com/computers/databases/postgresql/key-value-pairs.html">ordered pair</a> extension,
it of course would include tests written with <a href="https://pgtap.org/">pgTAP</a>. So I&rsquo;d have something
like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;pair&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;abstract&#34;</span><span class="p">:</span> <span class="s2">&#34;An ordered pair data type&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.1.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;owner&#34;</span><span class="p">:</span> <span class="s2">&#34;David E. Wheeler &lt;theory@pgxn.org&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="err">license:</span> <span class="nt">&#34;postgresql&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;meta-spec&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;1.0.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://github.com/theory/pgxn/wiki/PGXN-Meta-Spec&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;prereqs&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;runtime&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;requires&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;PostgreSQL&#34;</span><span class="p">:</span> <span class="s2">&#34;8.0.0&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;recommends&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;PostgreSQL&#34;</span><span class="p">:</span> <span class="s2">&#34;8.4.0&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;test&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;requires&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;pgTAP&#34;</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>That&rsquo;s saying that the &ldquo;pair&rdquo; distribution requires PostgreSQL 8.0.0 or higher
and any version of pgTAP to run the test suite. I&rsquo;ve also recommended
PostgreSQL 8.4, as that&rsquo;s where it will run best.</p>
<p>Of course, to get the real power of PGXN, you&rsquo;ll also want to use the
<code>provides</code> key, which allows you to identify the extensions included in your
distribution. Say that I finally got around to breaking out the schema testing
assertions from the logical testing assertions in pgTAP. I might call the
second module &ldquo;schematap.&rdquo; So to spell it out, I&rsquo;d add this to the first
example above:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl">  <span class="s2">&#34;pgtap&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;file&#34;</span><span class="p">:</span> <span class="s2">&#34;sql/pgtap.sql.in&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;0.25.0&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span><span class="err">,</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;schematap&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;file&#34;</span><span class="p">:</span> <span class="s2">&#34;sql/schematap.sql.in&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span></code></pre></div><p>So now the indexer will know that the &ldquo;pgtap&rdquo; extension is in
<code>sql/pgtap.sql.in</code> and the &ldquo;schematap&rdquo; extension is in <code>sql/schematap.sql.in</code>.
This is important because it allows other distributions to specify &ldquo;schematap&rdquo;
as a prerequisite. It also means that, in the PGXN client, you can type
something like:</p>
<pre tabindex="0"><code>PGXN&gt; install schematap
</code></pre><p>And, because &ldquo;schematap&rdquo; will have been indexed on the network, the client
will be able to find the pgTAP distribution and install it, complete with the
&ldquo;schematap&rdquo; extension.</p>
<p>A final note. Version numbers in the Perl community are a <a href="https://www.dagolden.com/index.php/369/version-numbers-should-be-boring/" title="Version numbers should be boring">disaster</a>. Wanting
to avoid that whole morass, I had originally intended to require numeric
version numbers. But <a href="https://www.dagolden.com/">David Golden</a> &ndash; the current maintainer of the CPAN Meta
Spec &ndash; pointed me to <a href="https://semver.org/">Semantic Versioning</a>, a version number specification by
GitHub&rsquo;s <a href="https://tom.preston-werner.com/">Tom Preston-Werner</a>. This style of version numbering is great for
PGXN for a few reasons:</p>
<ul>
<li>It closely matches how PostgreSQL itself is versioned.</li>
<li>It&rsquo;s very easy to compare version strings.</li>
<li>Someone else has already dealt with the pain of writing a spec</li>
</ul>
<p>So this is the standard that PGXN will require. Every version number will be
dotted-integer with three integers (X.Y.Z) and an optional ASCII string at the
end. That&rsquo;s it. PGXN won&rsquo;t invest any special meaning in the version string
the way CPAN does. It will just compare version numbers.</p>
<p>Anyway, please review <a href="https://github.com/theory/pgxn/wiki/PGXN-Meta-Spec">the spec</a> itself and leave any comments
or questions below. I expect to start hacking on this stuff this week!</p>
<p><strong>Update 2010-08-24:</strong> I&rsquo;ve just updated <a href="https://github.com/theory/pgxn/wiki/PGXN-Meta-Spec">the spec</a> to change
&ldquo;owner&rdquo; to &ldquo;maintainer.&rdquo; I think that the latter term is much better for this
purpose. And then I can use &ldquo;owner&rdquo; in PGXN to identify the person who uploads
a distribution.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/962576397</id><title type="html">About the Logo</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/about-the-logo/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-08-16T13:00:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="logo" label="Logo"/><category scheme="https://blog.pgxn.org/tags" term="gear" label="Gear"/><category scheme="https://blog.pgxn.org/tags" term="database" label="Database"/><category scheme="https://blog.pgxn.org/tags" term="icon" label="Icon"/><category scheme="https://blog.pgxn.org/tags" term="iconography" label="Iconography"/><category scheme="https://blog.pgxn.org/tags" term="design" label="Design"/><category scheme="https://blog.pgxn.org/tags" term="graphic-design" label="Graphic Design"/><category scheme="https://blog.pgxn.org/tags" term="identity" label="Identity"/><summary type="html"><![CDATA[<p>I decided to get on creating the PGXN logo sooner rather that later, both to
create a stronger identity for the project and to have a uniform look across
the various services (the <a href="https://pgxn.org/">site</a>, <a href="https://blog.pgxn.org/">blog</a>, <a href="https://twitter.com/pgxn/">twitter stream</a>, <a href="https://ident.ca/pgxn/">identi.ca</a>,
etc.). So I began hunting around for useful iconography to inspire a design
direction.</p>
<p><a href="https://www.iconfinder.com/icondetails/40094/128/data_database_seta_icon"><img src="https://cdn.iconfinder.net/data/icons/database/PNG/128/Database_1.png" alt="Database Icon"></a></p>
<p>Let me tell, you, that&rsquo;s a lot easier than it sounds. After browsing through
images returned for queries such as &ldquo;pixin,&rdquo; &ldquo;pig skin,&rdquo; &ldquo;zen,&rdquo; &ldquo;zen stones,&rdquo;
&ldquo;pixies,&rdquo; and god knows what else, I finally focused on &ldquo;database icon.&rdquo; This
was much more useful, as I hit on images such as the one to the right. Pretty
standard database icon, right? Three disks stacked up. I&rsquo;m not sure why this
is the classic graphical abstraction for a database, but I&rsquo;ve seen it a
zillion times. So often that it&rsquo;s a cliché.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I decided to get on creating the PGXN logo sooner rather that later, both to
create a stronger identity for the project and to have a uniform look across
the various services (the <a href="https://pgxn.org/">site</a>, <a href="https://blog.pgxn.org/">blog</a>, <a href="https://twitter.com/pgxn/">twitter stream</a>, <a href="https://ident.ca/pgxn/">identi.ca</a>,
etc.). So I began hunting around for useful iconography to inspire a design
direction.</p>
<p><a href="https://www.iconfinder.com/icondetails/40094/128/data_database_seta_icon"><img src="https://cdn.iconfinder.net/data/icons/database/PNG/128/Database_1.png" alt="Database Icon"></a></p>
<p>Let me tell, you, that&rsquo;s a lot easier than it sounds. After browsing through
images returned for queries such as &ldquo;pixin,&rdquo; &ldquo;pig skin,&rdquo; &ldquo;zen,&rdquo; &ldquo;zen stones,&rdquo;
&ldquo;pixies,&rdquo; and god knows what else, I finally focused on &ldquo;database icon.&rdquo; This
was much more useful, as I hit on images such as the one to the right. Pretty
standard database icon, right? Three disks stacked up. I&rsquo;m not sure why this
is the classic graphical abstraction for a database, but I&rsquo;ve seen it a
zillion times. So often that it&rsquo;s a cliché.</p>
<p><a href="https://dryicons.com/free-icons/icons-list/grace-icons-set/"><img src="https://dryicons.com/images/icon_sets/grace_icons_set/png/128x128/database.png" alt="Abstract Database Icon"></a></p>
<p>Much more interesting was stumbling onto the icon to the left. I love how
three disks have been broken down to their simplest possible representation.
Because I&rsquo;m familiar with the cliché, I can see at a glance exactly what this
icon is supposed to be. So I thought that might be useful to use.</p>
<p><a href="https://findicons.com/icon/920/extension_manager_cs3"><img src="https://png-2.findicons.com/files/icons/33/adobe_family/128/extension_manager_cs3.png" alt=""></a></p>
<p>Searching for &ldquo;extension icon&rdquo; was a bust. Most of the relevant images, like
the one to the right, seemed to be related to Adobe Extension Manager (try to
find a link for that!). I didn&rsquo;t want to go near that, of course. But,
inspired by the <a href="https://www.cpan.org/">CPAN</a> logo, I searched for &ldquo;library icon.&rdquo;</p>
<p><a href="https://www.cpan.org"><img src="https://www.cpan.org/misc/jpg/cpan.jpg" alt="CPAN Logo"></a></p>
<p><a href="https://www.iconarchive.com/show/icons-10-bundle-icons-by-icontoaster/library-icon.html"><img src="https://icons.iconarchive.com/icons/icontoaster/icons-10-bundle/128/library-icon.png" alt=""></a></p>
<p>Bingo! I hit on the amazing icon at right. <em>Of course!</em> Gears make a great
icon for extensions, don&rsquo;t you think? This folder is like an extension library
folder. So then I thought, how can I combine these two icons, the abstract
database and the gear?</p>
<p>Well it&rsquo;s simple, isn&rsquo;t it? The database icon can be the holes in the
extension gear. It&rsquo;s punched out, right? PGXN will offer loads of database
extensions to download (we hope), and when you use one in your database,
PostgreSQL will, you know: <em>turn the gear.</em> It just fits.</p>
<p><a href="https://pgxn.org/"><img src="/gear.svg" alt="PGXN Logo"></a></p>
<p><a href="https://strongrrl.com/">Strongrrl</a> created the logo and type treatment based on these ideas, adding
the tilt and the figure/ground reversal, and I really love it. It&rsquo;s a really
great logo for a database extension site. I imagine I can use variants of this
throughout the final design.</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/954535657</id><title type="html">Thoughts on the Network Directory Structure</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/thoughts-on-the-network-directory-structure/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-08-14T23:59:12Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="mirror" label="Mirror"/><category scheme="https://blog.pgxn.org/tags" term="directory" label="Directory"/><category scheme="https://blog.pgxn.org/tags" term="structure" label="Structure"/><category scheme="https://blog.pgxn.org/tags" term="metadata" label="Metadata"/><category scheme="https://blog.pgxn.org/tags" term="json" label="JSON"/><category scheme="https://blog.pgxn.org/tags" term="api" label="API"/><category scheme="https://blog.pgxn.org/tags" term="scalability" label="Scalability"/><category scheme="https://blog.pgxn.org/tags" term="rest" label="REST"/><summary type="html"><![CDATA[<p>I&rsquo;ve been thinking about the arrangement of stuff to be distributed to the
mirrors. In doing so, I&rsquo;ve kept three goals in mind:</p>
<ol>
<li>Allow things to scale.</li>
<li>Try to put things in intuitive locations.</li>
<li>Allow for metadata to be stored to act as a Web API.</li>
</ol>
<p>The first two are a bit mutually-contradicting. Thinking scalability mainly
means minimizing the chances that too many files can be put into a single
directory. <a href="https://www.cpan.org/misc/ZCAN.html" title="Zen of Comprehensive Archive Networks (look for “Naming”)">CPAN</a> started out with all author directories in a single
directory. Given the sheer number of CPAN contributors, this quickly got to be
a bottleneck. To correct for that, they started &ldquo;hashing&rdquo; the first two
letters of author names to create subdirectories. My CPAN directory, for
example, is <a href="https://www.cpan.org/authors/id/D/DW/DWHEELER/"><code>D/DW/DWHEELER</code></a>. That doesn&rsquo;t quite make for an intuitive
location, but it&rsquo;s not bad, and the tradeoff seems sufficient to keep things
sane.</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;ve been thinking about the arrangement of stuff to be distributed to the
mirrors. In doing so, I&rsquo;ve kept three goals in mind:</p>
<ol>
<li>Allow things to scale.</li>
<li>Try to put things in intuitive locations.</li>
<li>Allow for metadata to be stored to act as a Web API.</li>
</ol>
<p>The first two are a bit mutually-contradicting. Thinking scalability mainly
means minimizing the chances that too many files can be put into a single
directory. <a href="https://www.cpan.org/misc/ZCAN.html" title="Zen of Comprehensive Archive Networks (look for “Naming”)">CPAN</a> started out with all author directories in a single
directory. Given the sheer number of CPAN contributors, this quickly got to be
a bottleneck. To correct for that, they started &ldquo;hashing&rdquo; the first two
letters of author names to create subdirectories. My CPAN directory, for
example, is <a href="https://www.cpan.org/authors/id/D/DW/DWHEELER/"><code>D/DW/DWHEELER</code></a>. That doesn&rsquo;t quite make for an intuitive
location, but it&rsquo;s not bad, and the tradeoff seems sufficient to keep things
sane.</p>
<p>The storage of metadata is important, too, as I plan to have the <a href="https://pgxn.org/status.html#client">client</a> send
requests for JSON files to mirrors in order to find distributions. Basically,
this came down to a naming convention, as well as a recipe for how to find
metadata files for people and extensions. What I&rsquo;ve come up with is three
directories:</p>
<ul>
<li><code>meta</code> will contain PGXN metadata</li>
<li><code>dist</code> will contain distributions</li>
<li><code>by</code> will contain query-able JSON files</li>
</ul>
<p>The <code>meta</code> directory will contain PGXN metadata (<code>mirrors.json</code>, timestamp,
other detritus). If I ever create an index file that lists all distributions,
it would go there, too. I&rsquo;m not going to discuss it any further here, except
to note that it already has <code>mirrors.json</code>, which clients will be able to use
to present users with a list of mirrors to choose from.</p>
<p>The <code>dist</code> directory will be organized with directories named with &ldquo;hashes&rdquo; of
the first two letters of distribution names. Let&rsquo;s say that I&rsquo;m releasing
<a href="https://pgtan.org/">pgTAP</a> 0.25 on PGXN. To distribute it, the management application will create
the directory (if it doesn&rsquo;t already exist):</p>
<pre tabindex="0"><code>dist/p/pg/pgtap/
</code></pre><p>Then, for the 0.25 release, it will add three files to that directory:</p>
<pre tabindex="0"><code>dist/p/pg/pgtap/pgtap-0.25.pgz
dist/p/pg/pgtap/pgtap-0.25.json
dist/p/pg/pgtap/pgtap-0.25.readme
</code></pre><p>The <code>pgtap-0.25.pgz</code> file will contain the <a href="https://wiki.postgresql.org/wiki/PGXN#Distribution_Layout">zipped distribution</a> ready for
download. <code>pgtap-0.25.json</code> will contain metadata about the distribution, such
as the owner&rsquo;s name, the manager&rsquo;s name (more on these folks below), list of
included extensions, location of the <code>.pgz</code> and <code>.readme</code>, and its SHA1.
<code>pgtap-0.25.readme</code> will of course contain the <code>README</code> for the distribution
(if it has one).</p>
<p>Every release of pgTAP will have these three files, so after several releases,
the <code>pgtap</code> directory might have these files:</p>
<pre tabindex="0"><code>dist/p/pg/pgtap/pgtap-0.23.pgz
dist/p/pg/pgtap/pgtap-0.23.json
dist/p/pg/pgtap/pgtap-0.23.readme
dist/p/pg/pgtap/pgtap-0.24.pgz
dist/p/pg/pgtap/pgtap-0.24.json
dist/p/pg/pgtap/pgtap-0.24.readme
dist/p/pg/pgtap/pgtap-0.25.pgz
dist/p/pg/pgtap/pgtap-0.25.json
dist/p/pg/pgtap/pgtap-0.25.readme
dist/p/pg/pgtap/pgtap.json
</code></pre><p>The last file there, <code>pgtap.json</code>, will actually be a symlink to the JSON file
for latest production release of pgTAP. In this case, it would link to
<code>pgtap-0.25.json</code>. The nice thing about this is that, if a client wants to
find the information about the latest release of the pgtap distribution, all
it will have to do is send an HTTP GET request for
<code>dist/p/pg/pgtap/pgtap.json</code> to any mirror.</p>
<p>The <code>by</code> directory will also contain JSON files for clients to request. The
idea is that you want to find information &ldquo;by&rdquo; something. To start with, there
will be three subdirectories:</p>
<pre tabindex="0"><code>by/extension/
by/manager/
by/owner/
</code></pre><p>The first directory, <code>by/extension/</code>, will contain links to JSON files for
extensions. Say that the pgTAP distribution offers two extensions to
PostgreSQL named &ldquo;pgtap&rdquo; and &ldquo;schematap&rdquo;. The links would be:</p>
<pre tabindex="0"><code>by/extension/p/pg/pgtap/pgtap-0.23.json
by/extension/p/pg/pgtap/pgtap-0.24.json
by/extension/p/pg/pgtap/pgtap-0.25.json
by/extension/p/pg/pgtap/pgtap.json
</code></pre><p>Each of these will simply be symlinks pointing to the appropriate distribution
files:</p>
<pre tabindex="0"><code>dist/p/pg/pgtap/pgtap-0.23.json
dist/p/pg/pgtap/pgtap-0.24.json
dist/p/pg/pgtap/pgtap-0.25.json
dist/p/pg/pgtap/pgtap.json
</code></pre><p>Yes, the last one is a symlink to a symlink. Similarly, these files for
&ldquo;schematap&rdquo;:</p>
<pre tabindex="0"><code>by/extension/s/sc/schematap/schematap-0.23.json
by/extension/s/sc/schematap/schematap-0.24.json
by/extension/s/sc/schematap/schematap-0.25.json
by/extension/s/sc/schematap/schematap.json
</code></pre><p>Point to exactly the same files. Essentially, this is a way for extensions to
point to the distributions that contain them.
<code>by/extension/s/sc/schematap/schematap.json</code> points to
<code>dist/p/pg/pgtap/pgtap.json</code>, which contains path to the <code>.pgz</code> file to
download (and lots of other metadata, too).</p>
<p>The idea is that, whatever the name of the extension you want, the client will
be able to easily find the metadata file that tells it where to find the
distribution.</p>
<p>The <code>by/manager</code> and <code>by/owner</code> directories, on the other hand, contain JSON
files with information about managers and owners and their distributions.
Definitions:</p>
<p>An &ldquo;owner&rdquo; is someone who <em>owns</em> a distribution. This will often be the
original author of an extension, but may be someone else if maintenance has
been passed on. Basically, the &ldquo;owner&rdquo; is the person who should be contacted
with bug reports and the like</p>
<p>A &ldquo;manager&rdquo; is someone who manages the release process. This is the user who
will log into the PGXN management application and upload a distribution for
release.</p>
<p>These two people will often be the same person, but not always. I&rsquo;ve avoided
the term &ldquo;author&rdquo; (the term used by CPAN) because the author of an extension
may no longer maintain it. &ldquo;Owner&rdquo; seemed like a better choice (individual
distributions are free to describe their contributors however they wish).</p>
<p>As the <em>owner</em> of a few extensions on PGXN, I&rsquo;d have this file:</p>
<pre tabindex="0"><code>by/owner/t/th/theory.json
</code></pre><p>This file would contain a list of my distributions and perhaps some other
information (like my full name and blog URL).</p>
<p>As the <em>manager</em> of extensions on PGXN (that is, I actually uploaded them), I
would also have:</p>
<pre tabindex="0"><code>by/manager/t/th/theory.json
</code></pre><p>This file would contain a list of the distributions I&rsquo;ve released on PGXN.
This might be exactly the same as the list in my owner file, but may not be.
Perhaps for one release of pgTAP, say 0.26, <a href="https://leto.net/dukeleto.pl/">Duke Leto</a> uploaded a release. In
that case, 0.26 would probably be in my owner file, but not in my manager
file: it would be in Duke&rsquo;s manager file, instead.</p>
<p>So these are the basics of the directory structure for the networked mirrors.
Note that I haven&rsquo;t thought much about everything that will go into the JSON
files (or whether or not they&rsquo;d be versioned). That will likely depend quite a
lot on what the management database ends up looking like. I&rsquo;ll be working on
that next.</p>
<p>But other than that, comments? Questions? Criticisms? Recommendations? Leave a
comment and let me know!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/949562598</id><title type="html">Start Your Mirrors!</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/start-your-mirrors/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-08-13T23:45:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="getting-started" label="Getting Started"/><category scheme="https://blog.pgxn.org/tags" term="jfdi" label="JFDI"/><category scheme="https://blog.pgxn.org/tags" term="mirror" label="Mirror"/><category scheme="https://blog.pgxn.org/tags" term="progress" label="Progress"/><category scheme="https://blog.pgxn.org/tags" term="rsync" label="rsync"/><category scheme="https://blog.pgxn.org/tags" term="httpd" label="HTTPD"/><summary type="html"><![CDATA[<p>Work has finally started. We now have <a href="https://pgxn.org/mirroring.html">mirroring</a>. This was the first task for
the project, and it&rsquo;s now checked off on the <a href="https://pgxn.org/status.html">status</a> page. Hurrah!</p>
<p>This turned out to be a pretty simple task, of course. For now I&rsquo;m using the
<a href="https://www.kineticode.com/">Kineticode</a> (my other employer) server to host both the <a href="https://www.pgxn.org/">PGXN site</a> and the
master mirror. This is temporary, just a way to get things up and going as
quickly as possible. The master mirror runs <code>rsyncd</code> with read-only access to
the <code>pgxn</code> path. So all you have to do to mirror it is set up a <code>cron</code> job
like:</p>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>Work has finally started. We now have <a href="https://pgxn.org/mirroring.html">mirroring</a>. This was the first task for
the project, and it&rsquo;s now checked off on the <a href="https://pgxn.org/status.html">status</a> page. Hurrah!</p>
<p>This turned out to be a pretty simple task, of course. For now I&rsquo;m using the
<a href="https://www.kineticode.com/">Kineticode</a> (my other employer) server to host both the <a href="https://www.pgxn.org/">PGXN site</a> and the
master mirror. This is temporary, just a way to get things up and going as
quickly as possible. The master mirror runs <code>rsyncd</code> with read-only access to
the <code>pgxn</code> path. So all you have to do to mirror it is set up a <code>cron</code> job
like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">rsync -az --delete rsync://master.pgxn.org/pgxn /path/to/pgxn
</span></span></code></pre></div><p>If the destination directory is under an HTTP root, you&rsquo;re done. Otherwise,
throw a web server over it and <em>then</em> you&rsquo;re done.</p>
<p>Of course, the web server isn&rsquo;t really necessary yet. Soon it will be the
interface for downloading distributions and metadata from the mirrors. Right
now, the mirrors just have two files in them (a <code>README</code> and an <code>index.html</code>).
This is just the start of things. The next step is to figure out the directory
structure for the mirrors. I&rsquo;m working on that right now and will soon be
asking for feedback. Watch this space for details!</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/904171036</id><title type="html">Fundraising Update</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/fundraising-update/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-08-04T19:50:24Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="fundraising" label="Fundraising"/><category scheme="https://blog.pgxn.org/tags" term="totals" label="Totals"/><category scheme="https://blog.pgxn.org/tags" term="update" label="Update"/><category scheme="https://blog.pgxn.org/tags" term="myyearbook" label="myYearbook"/><category scheme="https://blog.pgxn.org/tags" term="dalibo" label="Dalibo"/><category scheme="https://blog.pgxn.org/tags" term="etsy" label="Etsy"/><category scheme="https://blog.pgxn.org/tags" term="tigerlead" label="TigerLead"/><category scheme="https://blog.pgxn.org/tags" term="richard-broersma" label="Richard Broersma"/><summary type="html"><![CDATA[This project started when I wrote up the <a href="https://wiki.postgresql.org/wiki/PGXN">specification</a> and got general
approval from <a href="https://www.mail-archive.com/pgsql-hackers@postgresql.org/msg143645.html" title="RFC: PostgreSQL Add-On Network">pgsql-hackers</a>. Then, at <a href="https://www.pgcon.org/2010/">PGCon</a>, it made the list of
<a href="https://wiki.postgresql.org/wiki/PgCon_2010_Developer_Meeting#Development_Priorities_for_9.1">development priorities for PostgreSQL 9.1</a> (when I was then calling it
&ldquo;PGAN&rdquo;). It was when <a href="https://www.myyearbook.com/">MyYearbook.com</a>&rsquo;s Gavin Roy pledged a founding
contribution of $5,000 (also at PGCon) that I was able to put in the time to
create the fundraising site and start soliciting donations. I&rsquo;m really
grateful to Gavin for stepping up like that, putting his money on the line on
the assumption that I would be as good as my word and get the work done.]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>This project started when I wrote up the <a href="https://wiki.postgresql.org/wiki/PGXN">specification</a> and got general
approval from <a href="https://www.mail-archive.com/pgsql-hackers@postgresql.org/msg143645.html" title="RFC: PostgreSQL Add-On Network">pgsql-hackers</a>. Then, at <a href="https://www.pgcon.org/2010/">PGCon</a>, it made the list of
<a href="https://wiki.postgresql.org/wiki/PgCon_2010_Developer_Meeting#Development_Priorities_for_9.1">development priorities for PostgreSQL 9.1</a> (when I was then calling it
&ldquo;PGAN&rdquo;). It was when <a href="https://www.myyearbook.com/">MyYearbook.com</a>&rsquo;s Gavin Roy pledged a founding
contribution of $5,000 (also at PGCon) that I was able to put in the time to
create the fundraising site and start soliciting donations. I&rsquo;m really
grateful to Gavin for stepping up like that, putting his money on the line on
the assumption that I would be as good as my word and get the work done.</p>
<p>Since then, we&rsquo;ve done quite well, and are getting closer to our goal every
day. <a href="https://www.pgexperts.com/">PostgreSQL Experts</a> soon committed to cover $5,000 in expenses,
<a href="https://www.dalibo.org/en/">Dalibo</a> pledged €5,000 last month, and just yesterday <a href="https://www.etsy.com/">Etsy</a> promised
$1,000. We&rsquo;ve also received contributions from:</p>
<ul>
<li>Richard Broersma ($250)</li>
<li><a href="https://tigerlead.com/">TigerLead</a> ($250)</li>
<li><a href="https://www.hubbellgrp.com/">Hubbell Group Inc.</a> ($100)</li>
<li>John S. Gage ($100)</li>
<li>Damian Soto-Renou ($100)</li>
<li>Thom Brown ($100)</li>
<li><a href="https://www.kineticode.com/">Kineticode, Inc.</a> ($25)</li>
<li><a href="https://www.cxnet.cl/">CxNet (Chile)</a> ($25)</li>
<li><a href="https://www.schemaverse.com/">Schemaverse</a> ($25)</li>
</ul>
<p>So far, we&rsquo;ve raised $18,359. Yay! I&rsquo;m really happy with how well this is
going. But we still need to raise another $6,641 to meet our goal. I&rsquo;m hoping
to get that done in the next couple of weeks. Can you <a href="https://pgxn.org/contributors.html">help out</a>?</p>
]]></content></entry><entry><id>https://blog.pgxn.org/post/900982991</id><title type="html">Blog and Twitter</title><link rel="alternate" type="text/html" href="https://blog.pgxn.org/2010/blog-and-twitter/"/><updated>2026-10-07T16:13:48Z</updated><published>2010-08-04T03:01:00Z</published><author><name>David E. Wheeler</name></author><category scheme="https://blog.pgxn.org/tags" term="twitter" label="Twitter"/><category scheme="https://blog.pgxn.org/tags" term="news" label="News"/><category scheme="https://blog.pgxn.org/tags" term="announcement" label="Announcement"/><category scheme="https://blog.pgxn.org/tags" term="fundraising" label="Fundraising"/><summary type="html"><![CDATA[<p>I&rsquo;m pleased to announce two things:</p>
<ul>
<li>
<p><a href="https://blog.pgxn.org/">This blog</a>. Here I&rsquo;ll make regular posts as I develop <a href="https://pgxn.org/">PGXN</a>, and to keep
it up to date with regular operational updates and release announcements
(for extensions on PGXN, not just PGXN itself) once the site goes live.
Subscribe to <a href="https://blog.pgxn.org/rss">the feed</a> to stay up-to-date in your favorite reader.</p>
</li>
<li>
<p>The <a href="https://twitter.com/pgxn/">PGXN Twitter stream</a>. Follow along as our fundraising draws to an
exciting conclusion and development begins. Once the site is launched, new
extension releases will be tweeted, as well.</p>
</li>
</ul>]]></summary><content type="html" xml:base="https://blog.pgxn.org/" xml:space="preserve"><![CDATA[<p>I&rsquo;m pleased to announce two things:</p>
<ul>
<li>
<p><a href="https://blog.pgxn.org/">This blog</a>. Here I&rsquo;ll make regular posts as I develop <a href="https://pgxn.org/">PGXN</a>, and to keep
it up to date with regular operational updates and release announcements
(for extensions on PGXN, not just PGXN itself) once the site goes live.
Subscribe to <a href="https://blog.pgxn.org/rss">the feed</a> to stay up-to-date in your favorite reader.</p>
</li>
<li>
<p>The <a href="https://twitter.com/pgxn/">PGXN Twitter stream</a>. Follow along as our fundraising draws to an
exciting conclusion and development begins. Once the site is launched, new
extension releases will be tweeted, as well.</p>
</li>
</ul>
<p>Thank you for your support! More news to come, so stay tuned!</p>
]]></content></entry></feed>