<?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/tags/meta/</id><title>Meta</title><updated>2011-09-08T06:39:00Z</updated><link rel="self" type="application/atom+xml" href="https://blog.pgxn.org/tags/meta/feed.xml"/><link rel="alternate" type="text/html" href="https://blog.pgxn.org/tags/meta/"/><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/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/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/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></feed>