diff --git a/META.json b/META.json index 6685747..689435b 100644 --- a/META.json +++ b/META.json @@ -2,7 +2,7 @@ "name": "http", "abstract": "HTTP client for PostgreSQL", "description": "HTTP allows you to get the content of a web page in a SQL function call.", - "version": "1.4.0", + "version": "1.5.0", "maintainer": [ "Paul Ramsey " ], @@ -21,9 +21,9 @@ }, "provides": { "http": { - "file": "http--1.4.sql", + "file": "http--1.5.sql", "docfile": "README.md", - "version": "1.4.0", + "version": "1.5.0", "abstract": "HTTP client for PostgreSQL" } }, diff --git a/README.md b/README.md index 560dccd..7e13097 100644 --- a/README.md +++ b/README.md @@ -232,7 +232,7 @@ As seen in the examples, you can unspool the array of `http_header` tuples into * `http_post(uri VARCHAR, data JSONB)` returns `http_response` * `http_put(uri VARCHAR, content VARCHAR, content_type VARCHAR)` returns `http_response` * `http_patch(uri VARCHAR, content VARCHAR, content_type VARCHAR)` returns `http_response` -* `http_delete(uri VARCHAR)` returns `http_response` +* `http_delete(uri VARCHAR, content VARCHAR, content_type VARCHAR))` returns `http_response` * `http_head(uri VARCHAR)` returns `http_response` * `http_set_curlopt(curlopt VARCHAR, value varchar)` returns `boolean` * `http_reset_curlopt()` returns `boolean` diff --git a/expected/http.out b/expected/http.out index 40c1590..9c061d3 100644 --- a/expected/http.out +++ b/expected/http.out @@ -62,60 +62,64 @@ from http(('GET', 'https://httpbin.org/anything', NULL, 'application/json', '{"s -- DELETE SELECT status, -content::json->'args' AS args, +content::json->'args'->>'foo' AS args, content::json->'url' AS url, content::json->'method' AS method FROM http_delete('https://httpbin.org/anything?foo=bar'); - status | args | url | method ---------+------------------+----------------------------------------+---------- - 200 | { +| "https://httpbin.org/anything?foo=bar" | "DELETE" - | "foo": "bar"+| | - | } | | + status | args | url | method +--------+------+----------------------------------------+---------- + 200 | bar | "https://httpbin.org/anything?foo=bar" | "DELETE" +(1 row) + +-- DELETE with payload +SELECT status, +content::json->'args'->>'foo' AS args, +content::json->'url' AS url, +content::json->'method' AS method, +content::json->'data' AS data +FROM http_delete('https://httpbin.org/anything?foo=bar', 'payload', 'text/plain'); + status | args | url | method | data +--------+------+----------------------------------------+----------+----------- + 200 | bar | "https://httpbin.org/anything?foo=bar" | "DELETE" | "payload" (1 row) -- PUT SELECT status, content::json->'data' AS data, -content::json->'args' AS args, +content::json->'args'->>'foo' AS args, content::json->'url' AS url, content::json->'method' AS method FROM http_put('https://httpbin.org/anything?foo=bar','payload','text/plain'); - status | data | args | url | method ---------+-----------+------------------+----------------------------------------+-------- - 200 | "payload" | { +| "https://httpbin.org/anything?foo=bar" | "PUT" - | | "foo": "bar"+| | - | | } | | + status | data | args | url | method +--------+-----------+------+----------------------------------------+-------- + 200 | "payload" | bar | "https://httpbin.org/anything?foo=bar" | "PUT" (1 row) -- PATCH SELECT status, content::json->'data' AS data, -content::json->'args' AS args, +content::json->'args'->>'foo' AS args, content::json->'url' AS url, content::json->'method' AS method FROM http_patch('https://httpbin.org/anything?foo=bar','{"this":"that"}','application/json'); - status | data | args | url | method ---------+-----------------------+------------------+----------------------------------------+--------- - 200 | "{\"this\":\"that\"}" | { +| "https://httpbin.org/anything?foo=bar" | "PATCH" - | | "foo": "bar"+| | - | | } | | + status | data | args | url | method +--------+-----------------------+------+----------------------------------------+--------- + 200 | "{\"this\":\"that\"}" | bar | "https://httpbin.org/anything?foo=bar" | "PATCH" (1 row) -- POST SELECT status, content::json->'data' AS data, -content::json->'args' AS args, +content::json->'args'->>'foo' AS args, content::json->'url' AS url, content::json->'method' AS method FROM http_post('https://httpbin.org/anything?foo=bar','payload','text/plain'); - status | data | args | url | method ---------+-----------+------------------+----------------------------------------+-------- - 200 | "payload" | { +| "https://httpbin.org/anything?foo=bar" | "POST" - | | "foo": "bar"+| | - | | } | | + status | data | args | url | method +--------+-----------+------+----------------------------------------+-------- + 200 | "payload" | bar | "https://httpbin.org/anything?foo=bar" | "POST" (1 row) --- POST with data +-- POST with json data SELECT status, content::json->'form'->>'this' AS args, content::json->'url' AS url, @@ -126,6 +130,18 @@ FROM http_post('https://httpbin.org/anything', jsonb_build_object('this', 'that' 200 | that | "https://httpbin.org/anything" | "POST" (1 row) +-- POST with data +SELECT status, +content::json->'form'->>'key1' AS key1, +content::json->'form'->>'key2' AS key2, +content::json->'url' AS url, +content::json->'method' AS method +FROM http_post('https://httpbin.org/anything', 'key1=value1&key2=value2','application/x-www-form-urlencoded'); + status | key1 | key2 | url | method +--------+--------+--------+--------------------------------+-------- + 200 | value1 | value2 | "https://httpbin.org/anything" | "POST" +(1 row) + -- HEAD SELECT lower(field) AS field, value FROM ( diff --git a/http--1.0--1.1.sql b/http--1.0--1.1.sql index 1065ad9..1ea2555 100644 --- a/http--1.0--1.1.sql +++ b/http--1.0--1.1.sql @@ -43,26 +43,26 @@ CREATE OR REPLACE FUNCTION http(request http_request) CREATE OR REPLACE FUNCTION http_get(uri VARCHAR) RETURNS http_response - AS $$ SELECT http(('GET', $1, NULL, NULL, NULL)::http_request) $$ + AS $$ SELECT @extschema@.http(('GET', $1, NULL, NULL, NULL)::http_request) $$ LANGUAGE 'sql'; CREATE OR REPLACE FUNCTION http_post(uri VARCHAR, content VARCHAR, content_type VARCHAR) RETURNS http_response - AS $$ SELECT http(('POST', $1, NULL, $3, $2)::http_request) $$ + AS $$ SELECT @extschema@.http(('POST', $1, NULL, $3, $2)::http_request) $$ LANGUAGE 'sql'; CREATE OR REPLACE FUNCTION http_put(uri VARCHAR, content VARCHAR, content_type VARCHAR) RETURNS http_response - AS $$ SELECT http(('PUT', $1, NULL, $3, $2)::http_request) $$ + AS $$ SELECT @extschema@.http(('PUT', $1, NULL, $3, $2)::http_request) $$ LANGUAGE 'sql'; CREATE OR REPLACE FUNCTION http_delete(uri VARCHAR) RETURNS http_response - AS $$ SELECT http(('DELETE', $1, NULL, NULL, NULL)::http_request) $$ + AS $$ SELECT @extschema@.http(('DELETE', $1, NULL, NULL, NULL)::http_request) $$ LANGUAGE 'sql'; CREATE OR REPLACE FUNCTION urlencode(string VARCHAR) RETURNS TEXT AS 'MODULE_PATHNAME' LANGUAGE 'c' - IMMUTABLE STRICT; \ No newline at end of file + IMMUTABLE STRICT; diff --git a/http--1.1--1.2.sql b/http--1.1--1.2.sql index 4764e52..c07465b 100644 --- a/http--1.1--1.2.sql +++ b/http--1.1--1.2.sql @@ -10,7 +10,7 @@ ALTER DOMAIN http_method ADD CHECK ( CREATE OR REPLACE FUNCTION http_head(uri VARCHAR) RETURNS http_response - AS $$ SELECT http(('HEAD', $1, NULL, NULL, NULL)::http_request) $$ + AS $$ SELECT @extschema@.http(('HEAD', $1, NULL, NULL, NULL)::http_request) $$ LANGUAGE 'sql'; CREATE OR REPLACE FUNCTION http_set_curlopt (curlopt VARCHAR, value VARCHAR) diff --git a/http--1.2--1.3.sql b/http--1.2--1.3.sql index 59ccf0c..406311f 100644 --- a/http--1.2--1.3.sql +++ b/http--1.2--1.3.sql @@ -10,5 +10,5 @@ ALTER DOMAIN http_method ADD CHECK ( CREATE OR REPLACE FUNCTION http_patch(uri VARCHAR, content VARCHAR, content_type VARCHAR) RETURNS http_response - AS $$ SELECT http(('PATCH', $1, NULL, $3, $2)::http_request) $$ + AS $$ SELECT @extschema@.http(('PATCH', $1, NULL, $3, $2)::http_request) $$ LANGUAGE 'sql'; diff --git a/http--1.3--1.4.sql b/http--1.3--1.4.sql index a78de63..283b25e 100644 --- a/http--1.3--1.4.sql +++ b/http--1.3--1.4.sql @@ -17,10 +17,10 @@ CREATE OR REPLACE FUNCTION urlencode(data JSONB) CREATE OR REPLACE FUNCTION http_get(uri VARCHAR, data JSONB) RETURNS http_response - AS $$ SELECT http(('GET', $1 || '?' || urlencode($2), NULL, NULL, NULL)::http_request) $$ + AS $$ SELECT @extschema@.http(('GET', $1 || '?' || urlencode($2), NULL, NULL, NULL)::http_request) $$ LANGUAGE 'sql'; CREATE OR REPLACE FUNCTION http_post(uri VARCHAR, data JSONB) RETURNS http_response - AS $$ SELECT http(('POST', $1, NULL, 'application/x-www-form-urlencoded', urlencode($2))::http_request) $$ + AS $$ SELECT @extschema@.http(('POST', $1, NULL, 'application/x-www-form-urlencoded', urlencode($2))::http_request) $$ LANGUAGE 'sql'; diff --git a/http--1.4--1.5.sql b/http--1.4--1.5.sql new file mode 100644 index 0000000..a42268c --- /dev/null +++ b/http--1.4--1.5.sql @@ -0,0 +1,5 @@ + +CREATE OR REPLACE FUNCTION http_delete(uri VARCHAR, content VARCHAR, content_type VARCHAR) + RETURNS http_response + AS $$ SELECT @extschema@.http(('DELETE', $1, NULL, $3, $2)::@extschema@.http_request) $$ + LANGUAGE 'sql'; diff --git a/http--1.4.sql b/http--1.5.sql similarity index 94% rename from http--1.4.sql rename to http--1.5.sql index f6054b7..ea2e19b 100644 --- a/http--1.4.sql +++ b/http--1.5.sql @@ -86,6 +86,11 @@ CREATE OR REPLACE FUNCTION http_delete(uri VARCHAR) AS $$ SELECT @extschema@.http(('DELETE', $1, NULL, NULL, NULL)::@extschema@.http_request) $$ LANGUAGE 'sql'; +CREATE OR REPLACE FUNCTION http_delete(uri VARCHAR, content VARCHAR, content_type VARCHAR) + RETURNS http_response + AS $$ SELECT @extschema@.http(('DELETE', $1, NULL, $3, $2)::@extschema@.http_request) $$ + LANGUAGE 'sql'; + CREATE OR REPLACE FUNCTION http_head(uri VARCHAR) RETURNS http_response AS $$ SELECT @extschema@.http(('HEAD', $1, NULL, NULL, NULL)::@extschema@.http_request) $$ diff --git a/http.c b/http.c index 4971d5b..487a44c 100644 --- a/http.c +++ b/http.c @@ -28,7 +28,7 @@ ***********************************************************************/ /* Constants */ -#define HTTP_VERSION "1.4.0" +#define HTTP_VERSION "1.5.0" #define HTTP_ENCODING "gzip" #define CURL_MIN_VERSION 0x071400 /* 7.20.0 */ @@ -1138,7 +1138,7 @@ Datum http_request(PG_FUNCTION_ARGS) headers = header_array_to_slist(array, headers); } - /* If we have a payload we send it, assuming we're either POST, GET or PUT */ + /* If we have a payload we send it, assuming we're either POST, GET, PATCH, PUT or DELETE */ if ( ! nulls[REQ_CONTENT] && values[REQ_CONTENT] ) { text *content_text; @@ -1160,7 +1160,7 @@ Datum http_request(PG_FUNCTION_ARGS) content_text = DatumGetTextP(values[REQ_CONTENT]); content_size = VARSIZE_ANY_EXHDR(content_text); - if ( method == HTTP_GET || method == HTTP_POST ) + if ( method == HTTP_GET || method == HTTP_POST || method == HTTP_DELETE ) { /* Add the content to the payload */ CURL_SETOPT(g_http_handle, CURLOPT_POST, 1); @@ -1168,7 +1168,13 @@ Datum http_request(PG_FUNCTION_ARGS) { /* Force the verb to be GET */ CURL_SETOPT(g_http_handle, CURLOPT_CUSTOMREQUEST, "GET"); + } + else if( method == HTTP_DELETE ) + { + /* Force the verb to be DELETE */ + CURL_SETOPT(g_http_handle, CURLOPT_CUSTOMREQUEST, "DELETE"); } + CURL_SETOPT(g_http_handle, CURLOPT_POSTFIELDS, text_to_cstring(content_text)); } else if ( method == HTTP_PUT || method == HTTP_PATCH ) diff --git a/http.control b/http.control index ef36fec..7a8896a 100644 --- a/http.control +++ b/http.control @@ -1,3 +1,3 @@ -default_version = '1.4' +default_version = '1.5' module_pathname = '$libdir/http' comment = 'HTTP client for PostgreSQL, allows web page retrieval inside the database.' diff --git a/sql/http.sql b/sql/http.sql index a126280..8cb9162 100644 --- a/sql/http.sql +++ b/sql/http.sql @@ -39,15 +39,23 @@ from http(('GET', 'https://httpbin.org/anything', NULL, 'application/json', '{"s -- DELETE SELECT status, -content::json->'args' AS args, +content::json->'args'->>'foo' AS args, content::json->'url' AS url, content::json->'method' AS method FROM http_delete('https://httpbin.org/anything?foo=bar'); +-- DELETE with payload +SELECT status, +content::json->'args'->>'foo' AS args, +content::json->'url' AS url, +content::json->'method' AS method, +content::json->'data' AS data +FROM http_delete('https://httpbin.org/anything?foo=bar', 'payload', 'text/plain'); + -- PUT SELECT status, content::json->'data' AS data, -content::json->'args' AS args, +content::json->'args'->>'foo' AS args, content::json->'url' AS url, content::json->'method' AS method FROM http_put('https://httpbin.org/anything?foo=bar','payload','text/plain'); @@ -55,7 +63,7 @@ FROM http_put('https://httpbin.org/anything?foo=bar','payload','text/plain'); -- PATCH SELECT status, content::json->'data' AS data, -content::json->'args' AS args, +content::json->'args'->>'foo' AS args, content::json->'url' AS url, content::json->'method' AS method FROM http_patch('https://httpbin.org/anything?foo=bar','{"this":"that"}','application/json'); @@ -63,18 +71,26 @@ FROM http_patch('https://httpbin.org/anything?foo=bar','{"this":"that"}','applic -- POST SELECT status, content::json->'data' AS data, -content::json->'args' AS args, +content::json->'args'->>'foo' AS args, content::json->'url' AS url, content::json->'method' AS method FROM http_post('https://httpbin.org/anything?foo=bar','payload','text/plain'); --- POST with data +-- POST with json data SELECT status, content::json->'form'->>'this' AS args, content::json->'url' AS url, content::json->'method' AS method FROM http_post('https://httpbin.org/anything', jsonb_build_object('this', 'that')); +-- POST with data +SELECT status, +content::json->'form'->>'key1' AS key1, +content::json->'form'->>'key2' AS key2, +content::json->'url' AS url, +content::json->'method' AS method +FROM http_post('https://httpbin.org/anything', 'key1=value1&key2=value2','application/x-www-form-urlencoded'); + -- HEAD SELECT lower(field) AS field, value FROM (