commit 67a513742a0b790229a8e4049d53446851cbdc75 Author: Peter Eisentraut Date: Tue Feb 17 23:23:18 2015 -0500 First commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..73e2e67 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.out -whitespace diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..acd93ea --- /dev/null +++ b/Makefile @@ -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 $^ >$@ diff --git a/README.md b/README.md new file mode 100644 index 0000000..e34904e --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +`uri` type for PostgreSQL + +https://twitter.com/pvh/status/567395527357001728 diff --git a/pguri.c b/pguri.c new file mode 100644 index 0000000..17f7481 --- /dev/null +++ b/pguri.c @@ -0,0 +1,365 @@ +#include +#include +#include +#include +#include +#include + +#include + + +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)); +} diff --git a/pguri.control b/pguri.control new file mode 100644 index 0000000..ddf5a46 --- /dev/null +++ b/pguri.control @@ -0,0 +1,4 @@ +comment = 'uri type' +default_version = 0 +module_pathname = '$libdir/pguri' +relocatable = true diff --git a/pguri.sql b/pguri.sql new file mode 100644 index 0000000..0fe9929 --- /dev/null +++ b/pguri.sql @@ -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); diff --git a/test/expected/init.out b/test/expected/init.out new file mode 100644 index 0000000..3d54192 --- /dev/null +++ b/test/expected/init.out @@ -0,0 +1 @@ +CREATE EXTENSION pguri; diff --git a/test/expected/test.out b/test/expected/test.out new file mode 100644 index 0000000..53a1c99 --- /dev/null +++ b/test/expected/test.out @@ -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) + diff --git a/test/sql/init.sql b/test/sql/init.sql new file mode 100644 index 0000000..3d54192 --- /dev/null +++ b/test/sql/init.sql @@ -0,0 +1 @@ +CREATE EXTENSION pguri; diff --git a/test/sql/test.sql b/test/sql/test.sql new file mode 100644 index 0000000..0faa0cb --- /dev/null +++ b/test/sql/test.sql @@ -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;