First commit
This commit is contained in:
commit
67a513742a
|
@ -0,0 +1 @@
|
||||||
|
*.out -whitespace
|
|
@ -0,0 +1,25 @@
|
||||||
|
PG_CONFIG = pg_config
|
||||||
|
PKG_CONFIG = pkg-config
|
||||||
|
|
||||||
|
extension_version = 0
|
||||||
|
|
||||||
|
EXTENSION = pguri
|
||||||
|
MODULE_big = pguri
|
||||||
|
OBJS = pguri.o
|
||||||
|
DATA_built = pguri--$(extension_version).sql
|
||||||
|
|
||||||
|
ifeq (no,$(shell $(PKG_CONFIG) liburiparser || echo no))
|
||||||
|
$(warning liburiparser not registed with pkg-config, build might fail)
|
||||||
|
endif
|
||||||
|
|
||||||
|
PG_CPPFLAGS += $(shell $(PKG_CONFIG) --cflags-only-I liburiparser)
|
||||||
|
SHLIB_LINK += $(shell $(PKG_CONFIG) --libs liburiparser)
|
||||||
|
|
||||||
|
REGRESS = init test
|
||||||
|
REGRESS_OPTS = --inputdir=test
|
||||||
|
|
||||||
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
|
include $(PGXS)
|
||||||
|
|
||||||
|
pguri--$(extension_version).sql: pguri.sql
|
||||||
|
cat $^ >$@
|
|
@ -0,0 +1,3 @@
|
||||||
|
`uri` type for PostgreSQL
|
||||||
|
|
||||||
|
https://twitter.com/pvh/status/567395527357001728
|
|
@ -0,0 +1,365 @@
|
||||||
|
#include <postgres.h>
|
||||||
|
#include <catalog/pg_type.h>
|
||||||
|
#include <fmgr.h>
|
||||||
|
#include <utils/array.h>
|
||||||
|
#include <utils/builtins.h>
|
||||||
|
#include <utils/inet.h>
|
||||||
|
|
||||||
|
#include <uriparser/Uri.h>
|
||||||
|
|
||||||
|
|
||||||
|
PG_MODULE_MAGIC;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct varlena uritype;
|
||||||
|
|
||||||
|
|
||||||
|
#define DatumGetUriP(X) ((uritype *) PG_DETOAST_DATUM(X))
|
||||||
|
#define UriPGetDatum(X) PointerGetDatum(X)
|
||||||
|
|
||||||
|
#define PG_GETARG_URI_P(n) DatumGetUriP(PG_GETARG_DATUM(n))
|
||||||
|
#define PG_RETURN_URI_P(x) PG_RETURN_POINTER(x)
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
parse_uri(const char *s, UriUriA *urip)
|
||||||
|
{
|
||||||
|
UriParserStateA state;
|
||||||
|
|
||||||
|
state.uri = urip;
|
||||||
|
uriParseUriA(&state, s);
|
||||||
|
|
||||||
|
switch (state.errorCode)
|
||||||
|
{
|
||||||
|
case URI_SUCCESS:
|
||||||
|
return;
|
||||||
|
case URI_ERROR_SYNTAX:
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||||
|
errmsg("invalid input syntax for type uri at or near \"%s\"",
|
||||||
|
state.errorPos)));
|
||||||
|
default:
|
||||||
|
elog(ERROR, "liburiparser error code %d", state.errorCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_in);
|
||||||
|
Datum
|
||||||
|
uri_in(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
char *s = PG_GETARG_CSTRING(0);
|
||||||
|
uritype *vardata;
|
||||||
|
UriUriA uri;
|
||||||
|
|
||||||
|
parse_uri(s, &uri);
|
||||||
|
uriFreeUriMembersA(&uri);
|
||||||
|
|
||||||
|
vardata = (uritype *) cstring_to_text(s);
|
||||||
|
PG_RETURN_URI_P(vardata);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_out);
|
||||||
|
Datum
|
||||||
|
uri_out(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum arg = PG_GETARG_DATUM(0);
|
||||||
|
|
||||||
|
PG_RETURN_CSTRING(TextDatumGetCString(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static text *
|
||||||
|
uri_text_range_to_text(UriTextRangeA r)
|
||||||
|
{
|
||||||
|
if (!r.first || !r.afterLast)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return cstring_to_text_with_len(r.first, r.afterLast - r.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_scheme);
|
||||||
|
Datum
|
||||||
|
uri_scheme(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum arg = PG_GETARG_DATUM(0);
|
||||||
|
char *s = TextDatumGetCString(arg);
|
||||||
|
UriUriA uri;
|
||||||
|
text *result;
|
||||||
|
|
||||||
|
parse_uri(s, &uri);
|
||||||
|
result = uri_text_range_to_text(uri.scheme);
|
||||||
|
uriFreeUriMembersA(&uri);
|
||||||
|
if (result)
|
||||||
|
PG_RETURN_TEXT_P(result);
|
||||||
|
else
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_userinfo);
|
||||||
|
Datum
|
||||||
|
uri_userinfo(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum arg = PG_GETARG_DATUM(0);
|
||||||
|
char *s = TextDatumGetCString(arg);
|
||||||
|
UriUriA uri;
|
||||||
|
text *result;
|
||||||
|
|
||||||
|
parse_uri(s, &uri);
|
||||||
|
result = uri_text_range_to_text(uri.userInfo);
|
||||||
|
uriFreeUriMembersA(&uri);
|
||||||
|
if (result)
|
||||||
|
PG_RETURN_TEXT_P(result);
|
||||||
|
else
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_host);
|
||||||
|
Datum
|
||||||
|
uri_host(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum arg = PG_GETARG_DATUM(0);
|
||||||
|
char *s = TextDatumGetCString(arg);
|
||||||
|
UriUriA uri;
|
||||||
|
text *result;
|
||||||
|
|
||||||
|
parse_uri(s, &uri);
|
||||||
|
result = uri_text_range_to_text(uri.hostText);
|
||||||
|
uriFreeUriMembersA(&uri);
|
||||||
|
if (result)
|
||||||
|
PG_RETURN_TEXT_P(result);
|
||||||
|
else
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_host_inet);
|
||||||
|
Datum
|
||||||
|
uri_host_inet(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum arg = PG_GETARG_DATUM(0);
|
||||||
|
char *s = TextDatumGetCString(arg);
|
||||||
|
UriUriA uri;
|
||||||
|
text *result;
|
||||||
|
|
||||||
|
parse_uri(s, &uri);
|
||||||
|
if (uri.hostData.ip4)
|
||||||
|
{
|
||||||
|
unsigned char *data = uri.hostData.ip4;
|
||||||
|
char *tmp = psprintf("%u.%u.%u.%u", data[0], data[1], data[2], data[3]);
|
||||||
|
uriFreeUriMembersA(&uri);
|
||||||
|
PG_RETURN_INET_P(DirectFunctionCall1(inet_in, CStringGetDatum(tmp)));
|
||||||
|
}
|
||||||
|
else if (uri.hostData.ip6)
|
||||||
|
{
|
||||||
|
unsigned char *data = uri.hostData.ip6;
|
||||||
|
char *tmp = psprintf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
|
||||||
|
data[0], data[1], data[2], data[3],
|
||||||
|
data[4], data[5], data[6], data[7],
|
||||||
|
data[8], data[9], data[10], data[11],
|
||||||
|
data[12], data[13], data[14], data[15]);
|
||||||
|
uriFreeUriMembersA(&uri);
|
||||||
|
PG_RETURN_INET_P(DirectFunctionCall1(inet_in, CStringGetDatum(tmp)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uriFreeUriMembersA(&uri);
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_port);
|
||||||
|
Datum
|
||||||
|
uri_port(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum arg = PG_GETARG_DATUM(0);
|
||||||
|
char *s = TextDatumGetCString(arg);
|
||||||
|
UriUriA uri;
|
||||||
|
text *result;
|
||||||
|
|
||||||
|
parse_uri(s, &uri);
|
||||||
|
result = uri_text_range_to_text(uri.portText);
|
||||||
|
uriFreeUriMembersA(&uri);
|
||||||
|
if (result)
|
||||||
|
PG_RETURN_TEXT_P(result);
|
||||||
|
else
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_query);
|
||||||
|
Datum
|
||||||
|
uri_query(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum arg = PG_GETARG_DATUM(0);
|
||||||
|
char *s = TextDatumGetCString(arg);
|
||||||
|
UriUriA uri;
|
||||||
|
text *result;
|
||||||
|
|
||||||
|
parse_uri(s, &uri);
|
||||||
|
result = uri_text_range_to_text(uri.query);
|
||||||
|
uriFreeUriMembersA(&uri);
|
||||||
|
if (result)
|
||||||
|
PG_RETURN_TEXT_P(result);
|
||||||
|
else
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_fragment);
|
||||||
|
Datum
|
||||||
|
uri_fragment(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum arg = PG_GETARG_DATUM(0);
|
||||||
|
char *s = TextDatumGetCString(arg);
|
||||||
|
UriUriA uri;
|
||||||
|
text *result;
|
||||||
|
|
||||||
|
parse_uri(s, &uri);
|
||||||
|
result = uri_text_range_to_text(uri.fragment);
|
||||||
|
uriFreeUriMembersA(&uri);
|
||||||
|
if (result)
|
||||||
|
PG_RETURN_TEXT_P(result);
|
||||||
|
else
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_path);
|
||||||
|
Datum
|
||||||
|
uri_path(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum arg = PG_GETARG_DATUM(0);
|
||||||
|
char *s = TextDatumGetCString(arg);
|
||||||
|
UriUriA uri;
|
||||||
|
ArrayBuildState *astate = initArrayResult(TEXTOID, CurrentMemoryContext);
|
||||||
|
UriPathSegmentA *pa;
|
||||||
|
|
||||||
|
parse_uri(s, &uri);
|
||||||
|
for (pa = uri.pathHead; pa; pa = pa->next)
|
||||||
|
{
|
||||||
|
text *piece = uri_text_range_to_text(pa->text);
|
||||||
|
astate = accumArrayResult(astate,
|
||||||
|
PointerGetDatum(piece),
|
||||||
|
!piece,
|
||||||
|
TEXTOID,
|
||||||
|
CurrentMemoryContext);
|
||||||
|
}
|
||||||
|
uriFreeUriMembersA(&uri);
|
||||||
|
|
||||||
|
if (astate)
|
||||||
|
PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate,
|
||||||
|
CurrentMemoryContext));
|
||||||
|
else
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cmp_text_range(UriTextRangeA a, UriTextRangeA b)
|
||||||
|
{
|
||||||
|
if (!a.first || !a.afterLast)
|
||||||
|
{
|
||||||
|
if (!b.first || !b.afterLast)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (!b.first || !b.afterLast)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int x = strncmp(a.first, b.first,
|
||||||
|
Min(a.afterLast - a.first, b.afterLast - b.first));
|
||||||
|
if (x == 0)
|
||||||
|
return (a.afterLast - a.first) - (b.afterLast - b.first);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_uri_cmp(Datum a, Datum b)
|
||||||
|
{
|
||||||
|
const char *sa = TextDatumGetCString(a);
|
||||||
|
const char *sb = TextDatumGetCString(b);
|
||||||
|
UriUriA ua;
|
||||||
|
UriUriA ub;
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
parse_uri(sa, &ua);
|
||||||
|
parse_uri(sa, &ub);
|
||||||
|
|
||||||
|
if (res == 0)
|
||||||
|
res = cmp_text_range(ua.scheme, ub.scheme);
|
||||||
|
if (res == 0)
|
||||||
|
res = cmp_text_range(ua.hostText, ub.hostText);
|
||||||
|
if (res == 0)
|
||||||
|
res = strcmp(sa, sb);
|
||||||
|
uriFreeUriMembersA(&ua);
|
||||||
|
uriFreeUriMembersA(&ub);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_lt);
|
||||||
|
Datum
|
||||||
|
uri_lt(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum *arg1 = PG_GETARG_DATUM(0);
|
||||||
|
Datum *arg2 = PG_GETARG_DATUM(1);
|
||||||
|
|
||||||
|
PG_RETURN_BOOL(_uri_cmp(arg1, arg2) < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_le);
|
||||||
|
Datum
|
||||||
|
uri_le(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum *arg1 = PG_GETARG_DATUM(0);
|
||||||
|
Datum *arg2 = PG_GETARG_DATUM(1);
|
||||||
|
|
||||||
|
PG_RETURN_BOOL(_uri_cmp(arg1, arg2) <= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_eq);
|
||||||
|
Datum
|
||||||
|
uri_eq(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum *arg1 = PG_GETARG_DATUM(0);
|
||||||
|
Datum *arg2 = PG_GETARG_DATUM(1);
|
||||||
|
|
||||||
|
PG_RETURN_BOOL(_uri_cmp(arg1, arg2) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_ne);
|
||||||
|
Datum
|
||||||
|
uri_ne(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum *arg1 = PG_GETARG_DATUM(0);
|
||||||
|
Datum *arg2 = PG_GETARG_DATUM(1);
|
||||||
|
|
||||||
|
PG_RETURN_BOOL(_uri_cmp(arg1, arg2) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_ge);
|
||||||
|
Datum
|
||||||
|
uri_ge(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum *arg1 = PG_GETARG_DATUM(0);
|
||||||
|
Datum *arg2 = PG_GETARG_DATUM(1);
|
||||||
|
|
||||||
|
PG_RETURN_BOOL(_uri_cmp(arg1, arg2) >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_gt);
|
||||||
|
Datum
|
||||||
|
uri_gt(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum *arg1 = PG_GETARG_DATUM(0);
|
||||||
|
Datum *arg2 = PG_GETARG_DATUM(1);
|
||||||
|
|
||||||
|
PG_RETURN_BOOL(_uri_cmp(arg1, arg2) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(uri_cmp);
|
||||||
|
Datum
|
||||||
|
uri_cmp(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum *arg1 = PG_GETARG_DATUM(0);
|
||||||
|
Datum *arg2 = PG_GETARG_DATUM(1);
|
||||||
|
|
||||||
|
PG_RETURN_INT32(_uri_cmp(arg1, arg2));
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
comment = 'uri type'
|
||||||
|
default_version = 0
|
||||||
|
module_pathname = '$libdir/pguri'
|
||||||
|
relocatable = true
|
|
@ -0,0 +1,189 @@
|
||||||
|
SET client_min_messages = warning;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TYPE uri;
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_in(cstring) RETURNS uri
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_out(uri) RETURNS cstring
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE TYPE uri (
|
||||||
|
INTERNALLENGTH = -1,
|
||||||
|
INPUT = uri_in,
|
||||||
|
OUTPUT = uri_out
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE CAST (uri AS text) WITH INOUT AS ASSIGNMENT;
|
||||||
|
CREATE CAST (text AS uri) WITH INOUT AS ASSIGNMENT;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_scheme(uri) RETURNS text
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_userinfo(uri) RETURNS text
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_host(uri) RETURNS text
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_host_inet(uri) RETURNS inet
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_port(uri) RETURNS text
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_query(uri) RETURNS text
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_fragment(uri) RETURNS text
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_path(uri) RETURNS text[]
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_lt(uri, uri) RETURNS boolean
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_le(uri, uri) RETURNS boolean
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_eq(uri, uri) RETURNS boolean
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_ne(uri, uri) RETURNS boolean
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_ge(uri, uri) RETURNS boolean
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_gt(uri, uri) RETURNS boolean
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE FUNCTION uri_cmp(uri, uri) RETURNS integer
|
||||||
|
IMMUTABLE
|
||||||
|
STRICT
|
||||||
|
LANGUAGE C
|
||||||
|
AS '$libdir/pguri';
|
||||||
|
|
||||||
|
CREATE OPERATOR < (
|
||||||
|
LEFTARG = uri,
|
||||||
|
RIGHTARG = uri,
|
||||||
|
COMMUTATOR = >,
|
||||||
|
NEGATOR = >=,
|
||||||
|
RESTRICT = scalarltsel,
|
||||||
|
JOIN = scalarltjoinsel,
|
||||||
|
PROCEDURE = uri_lt
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR <= (
|
||||||
|
LEFTARG = uri,
|
||||||
|
RIGHTARG = uri,
|
||||||
|
COMMUTATOR = >=,
|
||||||
|
NEGATOR = >,
|
||||||
|
RESTRICT = scalarltsel,
|
||||||
|
JOIN = scalarltjoinsel,
|
||||||
|
PROCEDURE = uri_le
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR = (
|
||||||
|
LEFTARG = uri,
|
||||||
|
RIGHTARG = uri,
|
||||||
|
COMMUTATOR = =,
|
||||||
|
NEGATOR = <>,
|
||||||
|
RESTRICT = eqsel,
|
||||||
|
JOIN = eqjoinsel,
|
||||||
|
HASHES,
|
||||||
|
MERGES,
|
||||||
|
PROCEDURE = uri_eq
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR <> (
|
||||||
|
LEFTARG = uri,
|
||||||
|
RIGHTARG = uri,
|
||||||
|
COMMUTATOR = <>,
|
||||||
|
NEGATOR = =,
|
||||||
|
RESTRICT = neqsel,
|
||||||
|
JOIN = neqjoinsel,
|
||||||
|
PROCEDURE = uri_ne
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR >= (
|
||||||
|
LEFTARG = uri,
|
||||||
|
RIGHTARG = uri,
|
||||||
|
COMMUTATOR = <=,
|
||||||
|
NEGATOR = <,
|
||||||
|
RESTRICT = scalargtsel,
|
||||||
|
JOIN = scalargtjoinsel,
|
||||||
|
PROCEDURE = uri_ge
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR > (
|
||||||
|
LEFTARG = uri,
|
||||||
|
RIGHTARG = uri,
|
||||||
|
COMMUTATOR = <,
|
||||||
|
NEGATOR = <=,
|
||||||
|
RESTRICT = scalargtsel,
|
||||||
|
JOIN = scalargtjoinsel,
|
||||||
|
PROCEDURE = uri_gt
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS uri_ops
|
||||||
|
DEFAULT FOR TYPE uri USING btree AS
|
||||||
|
OPERATOR 1 < ,
|
||||||
|
OPERATOR 2 <= ,
|
||||||
|
OPERATOR 3 = ,
|
||||||
|
OPERATOR 4 >= ,
|
||||||
|
OPERATOR 5 > ,
|
||||||
|
FUNCTION 1 uri_cmp(uri, uri);
|
|
@ -0,0 +1 @@
|
||||||
|
CREATE EXTENSION pguri;
|
|
@ -0,0 +1,178 @@
|
||||||
|
\pset null _null_
|
||||||
|
CREATE TABLE test (a serial, b uri);
|
||||||
|
INSERT INTO test (b)
|
||||||
|
VALUES ('http://www.postgresql.org/'),
|
||||||
|
('http://www.postgresql.org/docs/devel/static/xfunc-sql.html#XFUNC-SQL-FUNCTION-ARGUMENTS'),
|
||||||
|
('https://duckduckgo.com/?q=postgresql&ia=about'),
|
||||||
|
('ftp://ftp.gnu.org/gnu/bison'),
|
||||||
|
('mailto:foo@example.com'),
|
||||||
|
('ssh://username@review.openstack.org:29418/openstack/nova.git'),
|
||||||
|
('http://admin:password@192.168.0.1'),
|
||||||
|
('http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html'),
|
||||||
|
('http://[1080::8:800:200C:417A]/foo'),
|
||||||
|
(''),
|
||||||
|
('foobar');
|
||||||
|
SELECT * FROM test;
|
||||||
|
a | b
|
||||||
|
----+-----------------------------------------------------------------------------------------
|
||||||
|
1 | http://www.postgresql.org/
|
||||||
|
2 | http://www.postgresql.org/docs/devel/static/xfunc-sql.html#XFUNC-SQL-FUNCTION-ARGUMENTS
|
||||||
|
3 | https://duckduckgo.com/?q=postgresql&ia=about
|
||||||
|
4 | ftp://ftp.gnu.org/gnu/bison
|
||||||
|
5 | mailto:foo@example.com
|
||||||
|
6 | ssh://username@review.openstack.org:29418/openstack/nova.git
|
||||||
|
7 | http://admin:password@192.168.0.1
|
||||||
|
8 | http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html
|
||||||
|
9 | http://[1080::8:800:200C:417A]/foo
|
||||||
|
10 |
|
||||||
|
11 | foobar
|
||||||
|
(11 rows)
|
||||||
|
|
||||||
|
-- error cases
|
||||||
|
SELECT uri ':';
|
||||||
|
ERROR: invalid input syntax for type uri at or near ":"
|
||||||
|
LINE 1: SELECT uri ':';
|
||||||
|
^
|
||||||
|
SELECT uri 'foo bar';
|
||||||
|
ERROR: invalid input syntax for type uri at or near " bar"
|
||||||
|
LINE 1: SELECT uri 'foo bar';
|
||||||
|
^
|
||||||
|
\x on
|
||||||
|
SELECT b AS uri,
|
||||||
|
uri_scheme(b),
|
||||||
|
uri_userinfo(b),
|
||||||
|
uri_host(b),
|
||||||
|
uri_host_inet(b),
|
||||||
|
uri_port(b),
|
||||||
|
uri_path(b),
|
||||||
|
uri_query(b),
|
||||||
|
uri_fragment(b)
|
||||||
|
FROM test;
|
||||||
|
-[ RECORD 1 ]-+----------------------------------------------------------------------------------------
|
||||||
|
uri | http://www.postgresql.org/
|
||||||
|
uri_scheme | http
|
||||||
|
uri_userinfo | _null_
|
||||||
|
uri_host | www.postgresql.org
|
||||||
|
uri_host_inet | _null_
|
||||||
|
uri_port | _null_
|
||||||
|
uri_path | {""}
|
||||||
|
uri_query | _null_
|
||||||
|
uri_fragment | _null_
|
||||||
|
-[ RECORD 2 ]-+----------------------------------------------------------------------------------------
|
||||||
|
uri | http://www.postgresql.org/docs/devel/static/xfunc-sql.html#XFUNC-SQL-FUNCTION-ARGUMENTS
|
||||||
|
uri_scheme | http
|
||||||
|
uri_userinfo | _null_
|
||||||
|
uri_host | www.postgresql.org
|
||||||
|
uri_host_inet | _null_
|
||||||
|
uri_port | _null_
|
||||||
|
uri_path | {docs,devel,static,xfunc-sql.html}
|
||||||
|
uri_query | _null_
|
||||||
|
uri_fragment | XFUNC-SQL-FUNCTION-ARGUMENTS
|
||||||
|
-[ RECORD 3 ]-+----------------------------------------------------------------------------------------
|
||||||
|
uri | https://duckduckgo.com/?q=postgresql&ia=about
|
||||||
|
uri_scheme | https
|
||||||
|
uri_userinfo | _null_
|
||||||
|
uri_host | duckduckgo.com
|
||||||
|
uri_host_inet | _null_
|
||||||
|
uri_port | _null_
|
||||||
|
uri_path | {""}
|
||||||
|
uri_query | q=postgresql&ia=about
|
||||||
|
uri_fragment | _null_
|
||||||
|
-[ RECORD 4 ]-+----------------------------------------------------------------------------------------
|
||||||
|
uri | ftp://ftp.gnu.org/gnu/bison
|
||||||
|
uri_scheme | ftp
|
||||||
|
uri_userinfo | _null_
|
||||||
|
uri_host | ftp.gnu.org
|
||||||
|
uri_host_inet | _null_
|
||||||
|
uri_port | _null_
|
||||||
|
uri_path | {gnu,bison}
|
||||||
|
uri_query | _null_
|
||||||
|
uri_fragment | _null_
|
||||||
|
-[ RECORD 5 ]-+----------------------------------------------------------------------------------------
|
||||||
|
uri | mailto:foo@example.com
|
||||||
|
uri_scheme | mailto
|
||||||
|
uri_userinfo | _null_
|
||||||
|
uri_host | _null_
|
||||||
|
uri_host_inet | _null_
|
||||||
|
uri_port | _null_
|
||||||
|
uri_path | {foo@example.com}
|
||||||
|
uri_query | _null_
|
||||||
|
uri_fragment | _null_
|
||||||
|
-[ RECORD 6 ]-+----------------------------------------------------------------------------------------
|
||||||
|
uri | ssh://username@review.openstack.org:29418/openstack/nova.git
|
||||||
|
uri_scheme | ssh
|
||||||
|
uri_userinfo | username
|
||||||
|
uri_host | review.openstack.org
|
||||||
|
uri_host_inet | _null_
|
||||||
|
uri_port | 29418
|
||||||
|
uri_path | {openstack,nova.git}
|
||||||
|
uri_query | _null_
|
||||||
|
uri_fragment | _null_
|
||||||
|
-[ RECORD 7 ]-+----------------------------------------------------------------------------------------
|
||||||
|
uri | http://admin:password@192.168.0.1
|
||||||
|
uri_scheme | http
|
||||||
|
uri_userinfo | admin:password
|
||||||
|
uri_host | 192.168.0.1
|
||||||
|
uri_host_inet | 192.168.0.1
|
||||||
|
uri_port | _null_
|
||||||
|
uri_path | {}
|
||||||
|
uri_query | _null_
|
||||||
|
uri_fragment | _null_
|
||||||
|
-[ RECORD 8 ]-+----------------------------------------------------------------------------------------
|
||||||
|
uri | http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html
|
||||||
|
uri_scheme | http
|
||||||
|
uri_userinfo | _null_
|
||||||
|
uri_host | FEDC:BA98:7654:3210:FEDC:BA98:7654:3210
|
||||||
|
uri_host_inet | fedc:ba98:7654:3210:fedc:ba98:7654:3210
|
||||||
|
uri_port | 80
|
||||||
|
uri_path | {index.html}
|
||||||
|
uri_query | _null_
|
||||||
|
uri_fragment | _null_
|
||||||
|
-[ RECORD 9 ]-+----------------------------------------------------------------------------------------
|
||||||
|
uri | http://[1080::8:800:200C:417A]/foo
|
||||||
|
uri_scheme | http
|
||||||
|
uri_userinfo | _null_
|
||||||
|
uri_host | 1080::8:800:200C:417A
|
||||||
|
uri_host_inet | 1080::8:800:200c:417a
|
||||||
|
uri_port | _null_
|
||||||
|
uri_path | {foo}
|
||||||
|
uri_query | _null_
|
||||||
|
uri_fragment | _null_
|
||||||
|
-[ RECORD 10 ]+----------------------------------------------------------------------------------------
|
||||||
|
uri |
|
||||||
|
uri_scheme | _null_
|
||||||
|
uri_userinfo | _null_
|
||||||
|
uri_host | _null_
|
||||||
|
uri_host_inet | _null_
|
||||||
|
uri_port | _null_
|
||||||
|
uri_path | {}
|
||||||
|
uri_query | _null_
|
||||||
|
uri_fragment | _null_
|
||||||
|
-[ RECORD 11 ]+----------------------------------------------------------------------------------------
|
||||||
|
uri | foobar
|
||||||
|
uri_scheme | _null_
|
||||||
|
uri_userinfo | _null_
|
||||||
|
uri_host | _null_
|
||||||
|
uri_host_inet | _null_
|
||||||
|
uri_port | _null_
|
||||||
|
uri_path | {foobar}
|
||||||
|
uri_query | _null_
|
||||||
|
uri_fragment | _null_
|
||||||
|
|
||||||
|
\x off
|
||||||
|
SELECT DISTINCT b FROM test ORDER BY b;
|
||||||
|
b
|
||||||
|
-----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
foobar
|
||||||
|
ftp://ftp.gnu.org/gnu/bison
|
||||||
|
http://[1080::8:800:200C:417A]/foo
|
||||||
|
http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html
|
||||||
|
http://admin:password@192.168.0.1
|
||||||
|
http://www.postgresql.org/
|
||||||
|
http://www.postgresql.org/docs/devel/static/xfunc-sql.html#XFUNC-SQL-FUNCTION-ARGUMENTS
|
||||||
|
https://duckduckgo.com/?q=postgresql&ia=about
|
||||||
|
mailto:foo@example.com
|
||||||
|
ssh://username@review.openstack.org:29418/openstack/nova.git
|
||||||
|
(11 rows)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
CREATE EXTENSION pguri;
|
|
@ -0,0 +1,38 @@
|
||||||
|
\pset null _null_
|
||||||
|
|
||||||
|
CREATE TABLE test (a serial, b uri);
|
||||||
|
|
||||||
|
INSERT INTO test (b)
|
||||||
|
VALUES ('http://www.postgresql.org/'),
|
||||||
|
('http://www.postgresql.org/docs/devel/static/xfunc-sql.html#XFUNC-SQL-FUNCTION-ARGUMENTS'),
|
||||||
|
('https://duckduckgo.com/?q=postgresql&ia=about'),
|
||||||
|
('ftp://ftp.gnu.org/gnu/bison'),
|
||||||
|
('mailto:foo@example.com'),
|
||||||
|
('ssh://username@review.openstack.org:29418/openstack/nova.git'),
|
||||||
|
('http://admin:password@192.168.0.1'),
|
||||||
|
('http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html'),
|
||||||
|
('http://[1080::8:800:200C:417A]/foo'),
|
||||||
|
(''),
|
||||||
|
('foobar');
|
||||||
|
|
||||||
|
SELECT * FROM test;
|
||||||
|
|
||||||
|
-- error cases
|
||||||
|
SELECT uri ':';
|
||||||
|
SELECT uri 'foo bar';
|
||||||
|
|
||||||
|
|
||||||
|
\x on
|
||||||
|
SELECT b AS uri,
|
||||||
|
uri_scheme(b),
|
||||||
|
uri_userinfo(b),
|
||||||
|
uri_host(b),
|
||||||
|
uri_host_inet(b),
|
||||||
|
uri_port(b),
|
||||||
|
uri_path(b),
|
||||||
|
uri_query(b),
|
||||||
|
uri_fragment(b)
|
||||||
|
FROM test;
|
||||||
|
\x off
|
||||||
|
|
||||||
|
SELECT DISTINCT b FROM test ORDER BY b;
|
Loading…
Reference in New Issue