fix sql query for selecting specific properties webdav

Tue, 26 Apr 2022 14:33:58 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Tue, 26 Apr 2022 14:33:58 +0200
branch
webdav
changeset 315
b608b7aa43a6
parent 314
6b1a6066ee43
child 316
4090fc1b1c52

fix sql query for selecting specific properties

src/server/plugins/postgresql/pgtest.c file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/pgtest.h file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/webdav.c file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/webdav.h file | annotate | diff | comparison | revisions
--- a/src/server/plugins/postgresql/pgtest.c	Mon Apr 25 22:38:05 2022 +0200
+++ b/src/server/plugins/postgresql/pgtest.c	Tue Apr 26 14:33:58 2022 +0200
@@ -203,7 +203,8 @@
 
 
 void test_multistatus_destroy(TestMultistatus *ms) {
-    
+    xmlFreeDoc(ms->doc);
+    ucx_mempool_destroy(ms->mp);
 }
 
 
@@ -497,6 +498,13 @@
     UCX_TEST_ASSERT(f1, "res3 create failed");
     vfs_close(f1);
     
+    int r = vfs_mkdir(vfs, "/propfind/sub");
+    UCX_TEST_ASSERT(r == 0, "sub create failed");
+    
+    f1 = vfs_open(vfs, "/propfind/sub/res4", O_WRONLY|O_CREAT);
+    UCX_TEST_ASSERT(f1, "res4 create failed");
+    vfs_close(f1);
+    
     // 2 properties for res1
     char idstr[32];
     snprintf(idstr, 32, "%" PRId64, res1_id);
@@ -566,6 +574,7 @@
     // Test 1
     init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_PROPFIND1);
     rq->davCollection = create_test_pgdav(sn, rq);
+    pblock_nvinsert("depth", "0", rq->headers);
     
     ret = webdav_propfind(pb, sn, rq);
     
@@ -577,16 +586,70 @@
     TestResponse *r1 = ucx_map_cstr_get(ms->responses, "/propfind/");
     UCX_TEST_ASSERT(r1, "propfind1: missing /propfind/ response");
     
+    UCX_TEST_ASSERT(ms->responses->count == 1, "propfind1: wrong response count");
+    
     TestProperty *p = ucx_map_cstr_get(r1->properties, "D:resourcetype");
     UCX_TEST_ASSERT(p, "propfind1: missing property 'resourcetype'");
     UCX_TEST_ASSERT(p->status == 200, "propfind1: wrong status code for property 'resourcetype'");
     
     p = ucx_map_cstr_get(r1->properties, "D:getlastmodified");
     UCX_TEST_ASSERT(p, "propfind1: missing property 'getlastmodified'");
-    UCX_TEST_ASSERT(p->status == 200, "propfind1: wrong status code for property 'getlastmodified'");
+    UCX_TEST_ASSERT(p->status == 200, "propfind1: wrong status code for property 'getlastmodified'"); 
+    
+    testutil_destroy_session(sn);
+    test_multistatus_destroy(ms);
+    testutil_iostream_destroy(st);
+    
+    
+    // Test 2
+    init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_PROPFIND2);
+    rq->davCollection = create_test_pgdav(sn, rq);
+    pblock_nvinsert("depth", "1", rq->headers);
+    
+    ret = webdav_propfind(pb, sn, rq);
+    
+    //printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space);
+    
+    UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (2) failed");
+    
+    ms = test_parse_multistatus(st->buf->space, st->buf->size);
+    UCX_TEST_ASSERT(ms, "propfind2: response is not valid xml");
+    
+    r1 = ucx_map_cstr_get(ms->responses, "/propfind/");
+    UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/ response");
+    
+    UCX_TEST_ASSERT(ms->responses->count == 5, "propfind2: wrong response count");
+    
+    r1 = ucx_map_cstr_get(ms->responses, "/propfind/res2");
+    UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/res2 response");
+    
+    testutil_destroy_session(sn);
+    test_multistatus_destroy(ms);
+    testutil_iostream_destroy(st);
+    
+    
+    
+    // Test 3
+    init_test_webdav_method(&sn, &rq, &st, &pb, "PROPFIND", "/propfind/", PG_TEST_PROPFIND2);
+    rq->davCollection = create_test_pgdav(sn, rq);
+    pblock_nvinsert("depth", "infinity", rq->headers);
+    
+    ret = webdav_propfind(pb, sn, rq);
     
     printf("\n\n%.*s\n", (int)st->buf->size, st->buf->space);
     
+    UCX_TEST_ASSERT(ret == REQ_PROCEED, "webdav_propfind (3) failed");
+    
+    ms = test_parse_multistatus(st->buf->space, st->buf->size);
+    UCX_TEST_ASSERT(ms, "propfind3: response is not valid xml");
+    
+    r1 = ucx_map_cstr_get(ms->responses, "/propfind/");
+    UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/ response");
+    
+    UCX_TEST_ASSERT(ms->responses->count == 6, "propfind3: wrong response count");
+    
+    r1 = ucx_map_cstr_get(ms->responses, "/propfind/sub/res4");
+    UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/sub/res4 response");
     
     testutil_destroy_session(sn);
     test_multistatus_destroy(ms);
--- a/src/server/plugins/postgresql/pgtest.h	Mon Apr 25 22:38:05 2022 +0200
+++ b/src/server/plugins/postgresql/pgtest.h	Tue Apr 26 14:33:58 2022 +0200
@@ -63,7 +63,7 @@
 /* --------------------------- PROPFIND --------------------------- */
 
 #define PG_TEST_PROPFIND1 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \
-        <D:propfind xmlns:D=\"DAV:\"> \
+        <D:propfind xmlns:D=\"DAV:\" xmlns:X=\"http://example.com/\" > \
             <D:prop> \
                 <D:displayname/> \
                 <D:getcontentlength/> \
@@ -75,6 +75,21 @@
             </D:prop> \
         </D:propfind>"
 
+#define PG_TEST_PROPFIND2 "<?xml version=\"1.0\" encoding=\"utf-8\" ?> \
+        <D:propfind xmlns:D=\"DAV:\" xmlns:X=\"http://example.com/\" > \
+            <D:prop> \
+                <D:displayname/> \
+                <D:getcontentlength/> \
+                <D:getcontenttype/> \
+                <D:getlastmodified/> \
+                <D:creationdate/> \
+                <D:resourcetype/> \
+                <D:getetag/> \
+                <X:test /> \
+                <X:prop2 /> \
+            </D:prop> \
+        </D:propfind>"
+
 
 #ifdef __cplusplus
 }
--- a/src/server/plugins/postgresql/webdav.c	Mon Apr 25 22:38:05 2022 +0200
+++ b/src/server/plugins/postgresql/webdav.c	Tue Apr 26 14:33:58 2022 +0200
@@ -31,6 +31,7 @@
 
 #include "../../util/util.h"
 
+#include <ucx/buffer.h>
 #include <libxml/tree.h>
 
 static WebdavBackend pg_webdav_backend = {
@@ -88,9 +89,11 @@
     p.pname,\n\
     p.pvalue\n\
 from Resource r\n\
-left join Property p on r.resource_id = p.resource_id\n\
-inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\
-   on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\
+left join (\n\
+    select p.* from Property p\
+    inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\
+       on p.xmlns = n.xmlns and p.pname = n.pname\n\
+) p on r.resource_id = p.resource_id\n\
 where r.resource_id = $1;";
 
 // propfind with depth = 1
@@ -113,7 +116,7 @@
 from Resource r\n\
 left join Property p on r.resource_id = p.resource_id\n\
 where r.resource_id = $1 or r.parent_id = $1\n\
-order by order by case when resource_id = $1 then 0 else 1 end, nodename, resource_id;";
+order by case when r.resource_id = $1 then 0 else 1 end, nodename, resource_id;";
 
 
 // propfind with depth = 1 for specific properties
@@ -134,11 +137,13 @@
     p.pname,\n\
     p.pvalue\n\
 from Resource r\n\
-left join Property p on r.resource_id = p.resource_id\n\
-inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\
-   on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\
+left join (\n\
+    select p.* from Property p\
+    inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\
+       on p.xmlns = n.xmlns and p.pname = n.pname\n\
+) p on r.resource_id = p.resource_id\n\
 where r.resource_id = $1 or r.parent_id = $1\n\
-order by order by case when resource_id = $1 then 0 else 1 end, nodename, resource_id;";
+order by case when r.resource_id = $1 then 0 else 1 end, nodename, resource_id;";
 
 // recursive propfind
 // params: $1: resource_id
@@ -194,7 +199,7 @@
     )\n\
 select\n\
     case when r.resource_id = $1 then $2\n\
-         else $2 || '/' || r.ppath\n\
+         else $2 || r.ppath\n\
     end as ppath,\n\
     r.resource_id,\n\
     r.parent_id,\n\
@@ -207,9 +212,11 @@
     p.pname,\n\
     p.pvalue\n\
 from resolvepath r\n\
-left join Property p on r.resource_id = p.resource_id\n\
-inner join (select unnest($2::text[]) as xmlns, unnest($3::text[]) as pname) n\n\
-   on ( p.xmlns = n.xmlns and p.pname = n.pname ) or p.property_id is null\n\
+left join (\n\
+    select p.* from Property p\
+    inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\
+       on p.xmlns = n.xmlns and p.pname = n.pname\n\
+) p on r.resource_id = p.resource_id\n\
 order by replace(ppath, '/', chr(1)), resource_id;";
 
 WebdavBackend* pg_webdav_create(Session *sn, Request *rq, pblock *pb) {
@@ -254,6 +261,49 @@
     return NULL;
 }
 
+/*
+ * adds str to the buffer
+ * some characters will be escaped: \,{}
+ */
+static void buf_addstr_escaped(UcxBuffer *buf, const char *str) {
+    size_t len = strlen(str);
+    for(size_t i=0;i<len;i++) {
+        char c = str[i];
+        if(c == '{' || c == '}' || c == ',' || c == '\\') {
+            ucx_buffer_putc(buf, '\\');
+        }
+        ucx_buffer_putc(buf, c);
+    }
+}
+
+/*
+ * convert a property list to two pg array parameter strings
+ * array format: {elm1,elm2,elm3}
+ * xmlns: buffer for the xmlns array
+ * pname: buffer for the property name array
+ * 
+ * returns 0 on success, 1 otherwise
+ */
+int pg_create_property_param_arrays(WebdavPList *plist, UcxBuffer *xmlns, UcxBuffer *pname) {
+    ucx_buffer_putc(xmlns, '{');
+    ucx_buffer_putc(pname, '{');
+    while(plist) {
+        WebdavProperty *property = plist->property;
+        if(property && property->namespace && property->namespace->href && property->name) {
+            buf_addstr_escaped(xmlns, (const char*)property->namespace->href);
+            buf_addstr_escaped(pname, (const char*)property->name);
+            if(plist->next) {
+                ucx_buffer_putc(xmlns, ',');
+                ucx_buffer_putc(pname, ',');
+            }
+        }
+        plist = plist->next;
+    }
+    int r1 = ucx_buffer_write("}\0", 2, 1, xmlns) == 0;
+    int r2 = ucx_buffer_write("}\0", 2, 1, pname) == 0;
+    return r1+r2 != 0;
+}
+
 
 int pg_dav_propfind_init(
         WebdavPropfindRequest *rq,
@@ -312,12 +362,38 @@
     }
     href_param[href_len] = '\0';
     
+    // if allprop is false, create array pair for xmlns/property names
+    UcxBuffer *xmlns_buf = NULL;
+    UcxBuffer *pname_buf = NULL;
+    char *xmlns_param = NULL;
+    char *pname_param = NULL;
+    int nparam = 2;
+    if(!rq->allprop) {
+        size_t bufsize = rq->propcount < 200 ? 8 + rq->propcount * 32 : 4096;
+        xmlns_buf = ucx_buffer_new(NULL, bufsize, UCX_BUFFER_AUTOEXTEND);
+        if(!xmlns_buf) {
+            return 1;
+        }
+        pname_buf = ucx_buffer_new(NULL, bufsize, UCX_BUFFER_AUTOEXTEND);
+        if(!pname_buf) {
+            ucx_buffer_free(xmlns_buf);
+            return 1;
+        }
+        if(pg_create_property_param_arrays(rq->properties, xmlns_buf, pname_buf)) {
+            ucx_buffer_free(xmlns_buf);
+            ucx_buffer_free(pname_buf);
+            return 1;
+        }
+        xmlns_param = xmlns_buf->space;
+        pname_param = pname_buf->space;
+        nparam = 4;
+    }
     
-    const char* params[2] = { resource_id_str, href_param };
+    const char* params[4] = { resource_id_str, href_param, xmlns_param, pname_param };
     PGresult *result = PQexecParams(
             pgdav->connection,
-            sql_propfind_allprop_recursive,
-            2,     // number of parameters
+            query,
+            nparam,     // number of parameters
             NULL,
             params, // parameter value
             NULL,
@@ -325,6 +401,10 @@
             0);    // 0: result in text format
     int nrows = PQntuples(result);
     pool_free(rq->sn->pool, href_param);
+    if(xmlns_buf) {
+        ucx_buffer_free(xmlns_buf);
+        ucx_buffer_free(pname_buf);
+    }
     if(nrows < 1) {
         PQclear(result);
         return 1;
--- a/src/server/plugins/postgresql/webdav.h	Mon Apr 25 22:38:05 2022 +0200
+++ b/src/server/plugins/postgresql/webdav.h	Tue Apr 26 14:33:58 2022 +0200
@@ -33,6 +33,7 @@
 #include "../../public/webdav.h"
 
 #include <libpq-fe.h>
+#include <ucx/buffer.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -56,6 +57,8 @@
 
 WebdavBackend* pg_webdav_prop_create(Session *sn, Request *rq, pblock *pb);
 
+int pg_create_property_param_arrays(WebdavPList *plist, UcxBuffer *xmlns, UcxBuffer *pname);
+
 /* ----------------- webdav backend functions ----------------- */
 int pg_dav_propfind_init(
         WebdavPropfindRequest *rq,

mercurial