53 NULL, |
53 NULL, |
54 NULL |
54 NULL |
55 }; |
55 }; |
56 |
56 |
57 |
57 |
|
58 |
58 /* |
59 /* |
59 * SQL Queries |
60 * SQL query components |
60 */ |
61 */ |
61 |
62 |
62 // propfind with depth = 0 |
63 /* |
63 // params: $1: resource_id |
64 * PROPFIND queries are build from components: |
64 static const char *sql_propfind_allprop_depth0 = "\ |
65 * |
65 select\n\ |
66 * cte cte_recursive or empty |
66 $2::text as ppath,\n\ |
67 * select |
67 r.resource_id,\n\ |
68 * ppath ppath column |
68 r.parent_id,\n\ |
69 * cols list of columns |
69 r.nodename,\n\ |
70 * ext_cols* list of extension columns |
70 r.iscollection,\n\ |
71 * from from [table] / from cte |
71 r.lastmodified,\n\ |
72 * prop_join join with property table, allprop or plist |
72 r.creationdate,\n\ |
73 * ext_join* extension table join |
73 r.contentlength,\n\ |
74 * where different where clauses for depth0 and depth1 |
74 r.etag,\n\ |
75 * order depth0 doesn't need order |
75 p.prefix,\n\ |
76 */ |
76 p.xmlns,\n\ |
77 |
77 p.pname,\n\ |
78 static const char *sql_propfind_cte_recursive = "\ |
78 p.lang,\n\ |
|
79 p.nsdeflist,\n\ |
|
80 p.pvalue\n\ |
|
81 from Resource r\n\ |
|
82 left join Property p on r.resource_id = p.resource_id\n\ |
|
83 where r.resource_id = $1;"; |
|
84 |
|
85 // propfind with depth = 0 for specific properties |
|
86 // params: $1: resource_id |
|
87 static const char *sql_propfind_depth0 = "\ |
|
88 select\n\ |
|
89 $2::text as ppath,\n\ |
|
90 r.resource_id,\n\ |
|
91 r.parent_id,\n\ |
|
92 r.nodename,\n\ |
|
93 r.iscollection,\n\ |
|
94 r.lastmodified,\n\ |
|
95 r.creationdate,\n\ |
|
96 r.contentlength,\n\ |
|
97 r.etag,\n\ |
|
98 p.prefix,\n\ |
|
99 p.xmlns,\n\ |
|
100 p.pname,\n\ |
|
101 p.lang,\n\ |
|
102 p.nsdeflist,\n\ |
|
103 p.pvalue\n\ |
|
104 from Resource r\n\ |
|
105 left join (\n\ |
|
106 select p.* from Property p\ |
|
107 inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\ |
|
108 on p.xmlns = n.xmlns and p.pname = n.pname\n\ |
|
109 ) p on r.resource_id = p.resource_id\n\ |
|
110 where r.resource_id = $1;"; |
|
111 |
|
112 // propfind with depth = 1 |
|
113 // params: $1: resource_id |
|
114 static const char *sql_propfind_allprop_depth1 = "\ |
|
115 select\n\ |
|
116 case when r.resource_id = $1 then $2\n\ |
|
117 else $2 || '/' || r.nodename\n\ |
|
118 end as ppath,\n\ |
|
119 r.resource_id,\n\ |
|
120 r.parent_id,\n\ |
|
121 r.nodename,\n\ |
|
122 r.iscollection,\n\ |
|
123 r.lastmodified,\n\ |
|
124 r.creationdate,\n\ |
|
125 r.contentlength,\n\ |
|
126 r.etag,\n\ |
|
127 p.prefix,\n\ |
|
128 p.xmlns,\n\ |
|
129 p.pname,\n\ |
|
130 p.lang,\n\ |
|
131 p.nsdeflist,\n\ |
|
132 p.pvalue\n\ |
|
133 from Resource r\n\ |
|
134 left join Property p on r.resource_id = p.resource_id\n\ |
|
135 where r.resource_id = $1 or r.parent_id = $1\n\ |
|
136 order by case when r.resource_id = $1 then 0 else 1 end, nodename, resource_id;"; |
|
137 |
|
138 |
|
139 // propfind with depth = 1 for specific properties |
|
140 // params: $1: resource_id |
|
141 static const char *sql_propfind_depth1 = "\ |
|
142 select\n\ |
|
143 case when r.resource_id = $1 then $2\n\ |
|
144 else $2 || '/' || r.nodename\n\ |
|
145 end as ppath,\n\ |
|
146 r.resource_id,\n\ |
|
147 r.parent_id,\n\ |
|
148 r.nodename,\n\ |
|
149 r.iscollection,\n\ |
|
150 r.lastmodified,\n\ |
|
151 r.creationdate,\n\ |
|
152 r.contentlength,\n\ |
|
153 r.etag,\n\ |
|
154 p.prefix,\n\ |
|
155 p.xmlns,\n\ |
|
156 p.pname,\n\ |
|
157 p.lang,\n\ |
|
158 p.nsdeflist,\n\ |
|
159 p.pvalue\n\ |
|
160 from Resource r\n\ |
|
161 left join (\n\ |
|
162 select p.* from Property p\ |
|
163 inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\ |
|
164 on p.xmlns = n.xmlns and p.pname = n.pname\n\ |
|
165 ) p on r.resource_id = p.resource_id\n\ |
|
166 where r.resource_id = $1 or r.parent_id = $1\n\ |
|
167 order by case when r.resource_id = $1 then 0 else 1 end, nodename, resource_id;"; |
|
168 |
|
169 // recursive propfind |
|
170 // params: $1: resource_id |
|
171 static const char *sql_propfind_allprop_recursive = "\ |
|
172 with recursive resolvepath as (\n\ |
79 with recursive resolvepath as (\n\ |
173 select\n\ |
80 select\n\ |
174 '' as ppath,\n\ |
81 '' as ppath,\n\ |
175 *\n\ |
82 *\n\ |
176 from Resource\n\ |
83 from Resource\n\ |
179 select\n\ |
86 select\n\ |
180 p.ppath || '/' || r.nodename,\n\ |
87 p.ppath || '/' || r.nodename,\n\ |
181 r.*\n\ |
88 r.*\n\ |
182 from Resource r\n\ |
89 from Resource r\n\ |
183 inner join resolvepath p on r.parent_id = p.resource_id\n\ |
90 inner join resolvepath p on r.parent_id = p.resource_id\n\ |
184 )\n\ |
91 )\n"; |
185 select\n\ |
92 |
186 case when r.resource_id = $1 then $2\n\ |
93 static const char *sql_propfind_select = "\ |
187 else $2 || r.ppath\n\ |
94 select\n"; |
188 end as ppath,\n\ |
95 |
189 r.resource_id,\n\ |
96 static const char *sql_propfind_ppath_depth0 = "\ |
190 r.parent_id,\n\ |
97 $2::text as ppath,\n"; |
191 r.nodename,\n\ |
98 |
192 r.iscollection,\n\ |
99 static const char *sql_propfind_ppath_depth1 = "\ |
193 r.lastmodified,\n\ |
100 case when r.resource_id = $1 then $2\n\ |
194 r.creationdate,\n\ |
101 else $2 || '/' || r.nodename\n\ |
195 r.contentlength,\n\ |
102 end as ppath,\n"; |
196 r.etag,\n\ |
103 |
197 p.prefix,\n\ |
104 static const char *sql_propfind_ppath_depth_infinity = "\ |
198 p.xmlns,\n\ |
105 case when r.resource_id = $1 then $2\n\ |
199 p.pname,\n\ |
106 else $2 || r.ppath\n\ |
200 p.lang,\n\ |
107 end as ppath,\n"; |
201 p.nsdeflist,\n\ |
108 |
202 p.pvalue\n\ |
109 static const char *sql_propfind_cols = "\ |
203 from resolvepath r\n\ |
110 r.resource_id,\n\ |
204 left join Property p on r.resource_id = p.resource_id\n\ |
111 r.parent_id,\n\ |
205 order by replace(ppath, '/', chr(1)), resource_id;"; |
112 r.nodename,\n\ |
206 |
113 r.iscollection,\n\ |
207 // recursive propfind for specific properties |
114 r.lastmodified,\n\ |
208 // params: $1: resource_id |
115 r.creationdate,\n\ |
209 // $2: array of property xmlns |
116 r.contentlength,\n\ |
210 // $3: array of property names |
117 r.etag,\n\ |
211 static const char *sql_propfind_recursive = "\ |
118 p.prefix,\n\ |
212 with recursive resolvepath as (\n\ |
119 p.xmlns,\n\ |
213 select\n\ |
120 p.pname,\n\ |
214 '' as ppath,\n\ |
121 p.lang,\n\ |
215 *\n\ |
122 p.nsdeflist,\n\ |
216 from Resource\n\ |
123 p.pvalue\n"; |
217 where resource_id = $1 \n\ |
124 |
218 union\n\ |
125 static const char *sql_propfind_from_table = "\ |
219 select\n\ |
126 from Resource r\n"; |
220 p.ppath || '/' || r.nodename,\n\ |
127 |
221 r.*\n\ |
128 static const char *sql_propfind_from_cte = "\ |
222 from Resource r\n\ |
129 from resolvepath r\n"; |
223 inner join resolvepath p on r.parent_id = p.resource_id\n\ |
130 |
224 )\n\ |
131 static const char *sql_propfind_propjoin_allprop = "\ |
225 select\n\ |
132 left join Property p on r.resource_id = p.resource_id\n"; |
226 case when r.resource_id = $1 then $2\n\ |
133 |
227 else $2 || r.ppath\n\ |
134 static const char *sql_propfind_propjoin_plist = "\ |
228 end as ppath,\n\ |
|
229 r.resource_id,\n\ |
|
230 r.parent_id,\n\ |
|
231 r.nodename,\n\ |
|
232 r.iscollection,\n\ |
|
233 r.lastmodified,\n\ |
|
234 r.creationdate,\n\ |
|
235 r.contentlength,\n\ |
|
236 r.etag,\n\ |
|
237 p.prefix,\n\ |
|
238 p.xmlns,\n\ |
|
239 p.pname,\n\ |
|
240 p.lang,\n\ |
|
241 p.nsdeflist,\n\ |
|
242 p.pvalue\n\ |
|
243 from resolvepath r\n\ |
|
244 left join (\n\ |
135 left join (\n\ |
245 select p.* from Property p\ |
136 select p.* from Property p\ |
246 inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\ |
137 inner join (select unnest($3::text[]) as xmlns, unnest($4::text[]) as pname) n\n\ |
247 on p.xmlns = n.xmlns and p.pname = n.pname\n\ |
138 on p.xmlns = n.xmlns and p.pname = n.pname\n\ |
248 ) p on r.resource_id = p.resource_id\n\ |
139 ) p on r.resource_id = p.resource_id\n"; |
249 order by replace(ppath, '/', chr(1)), resource_id;"; |
140 |
|
141 static const char *sql_propfind_where_depth0 = "\ |
|
142 where r.resource_id = $1\n"; |
|
143 |
|
144 static const char *sql_propfind_where_depth1 = "\ |
|
145 where r.resource_id = $1 or r.parent_id = $1\n"; |
|
146 |
|
147 static const char *sql_propfind_order_depth1 = "\ |
|
148 order by case when r.resource_id = $1 then 0 else 1 end, nodename, resource_id"; |
|
149 |
|
150 static const char *sql_propfind_order_depth_infinity = "\ |
|
151 order by replace(ppath, '/', chr(1)), resource_id"; |
|
152 |
|
153 /* |
|
154 * SQL Queries |
|
155 */ |
|
156 |
250 |
157 |
251 // proppatch: set property |
158 // proppatch: set property |
252 // params: $1: resource_id |
159 // params: $1: resource_id |
253 // $2: xmlns prefix |
160 // $2: xmlns prefix |
254 // $3: xmlns href |
161 // $3: xmlns href |
368 int r2 = ucx_buffer_write("}\0", 2, 1, pname) == 0; |
275 int r2 = ucx_buffer_write("}\0", 2, 1, pname) == 0; |
369 return r1+r2 != 0; |
276 return r1+r2 != 0; |
370 } |
277 } |
371 |
278 |
372 |
279 |
|
280 static int pg_create_propfind_query(WebdavPropfindRequest *rq, WSBool iscollection, UcxBuffer *sql) { |
|
281 PgWebdavBackend *pgdav = rq->dav->instance; |
|
282 int depth = !iscollection ? 0 : rq->depth; |
|
283 |
|
284 /* |
|
285 * PROPFIND queries are build from components: |
|
286 * |
|
287 * cte cte_recursive or empty |
|
288 * select |
|
289 * ppath ppath column |
|
290 * cols list of columns |
|
291 * ext_cols* list of extension columns |
|
292 * from from [table] / from cte |
|
293 * prop_join join with property table, allprop or plist |
|
294 * ext_join* extension table join |
|
295 * where different where clauses for depth0 and depth1 |
|
296 * order depth0 doesn't need order |
|
297 */ |
|
298 |
|
299 // CTE |
|
300 if(depth == -1) { |
|
301 ucx_buffer_puts(sql, sql_propfind_cte_recursive); |
|
302 } |
|
303 |
|
304 // select |
|
305 ucx_buffer_puts(sql, sql_propfind_select); |
|
306 |
|
307 // ppath |
|
308 switch(depth) { |
|
309 case 0: ucx_buffer_puts(sql, sql_propfind_ppath_depth0); break; |
|
310 case 1: ucx_buffer_puts(sql, sql_propfind_ppath_depth1); break; |
|
311 case -1: ucx_buffer_puts(sql, sql_propfind_ppath_depth_infinity); break; |
|
312 } |
|
313 |
|
314 // cols |
|
315 ucx_buffer_puts(sql, sql_propfind_cols); |
|
316 |
|
317 // from |
|
318 ucx_buffer_puts(sql, depth == -1 ? sql_propfind_from_cte : sql_propfind_from_table); |
|
319 |
|
320 // prop join |
|
321 ucx_buffer_puts(sql, rq->allprop ? sql_propfind_propjoin_allprop : sql_propfind_propjoin_plist); |
|
322 |
|
323 // where |
|
324 if(depth == 0) { |
|
325 ucx_buffer_puts(sql, sql_propfind_where_depth0); |
|
326 } else if(depth == 1) { |
|
327 ucx_buffer_puts(sql, sql_propfind_where_depth1); |
|
328 } |
|
329 |
|
330 // order |
|
331 if(depth == 1) { |
|
332 ucx_buffer_puts(sql, sql_propfind_order_depth1); |
|
333 } else if(depth == -1) { |
|
334 ucx_buffer_puts(sql, sql_propfind_order_depth_infinity); |
|
335 } |
|
336 |
|
337 // end |
|
338 ucx_buffer_puts(sql, ";\0"); |
|
339 |
|
340 //printf("\n\n%s\n\n", sql->space); |
|
341 //fflush(stdout); |
|
342 |
|
343 return 0; |
|
344 } |
|
345 |
373 int pg_dav_propfind_init( |
346 int pg_dav_propfind_init( |
374 WebdavPropfindRequest *rq, |
347 WebdavPropfindRequest *rq, |
375 const char *path, |
348 const char *path, |
376 const char *href, |
349 const char *href, |
377 WebdavPList **outplist) |
350 WebdavPList **outplist) |
402 protocol_status(rq->sn, rq->rq, PROTOCOL_NOT_FOUND, NULL); |
375 protocol_status(rq->sn, rq->rq, PROTOCOL_NOT_FOUND, NULL); |
403 } |
376 } |
404 return 1; |
377 return 1; |
405 } |
378 } |
406 |
379 |
407 // choose sql query |
380 // create sql query |
408 const char *query = NULL; |
381 const char *query = NULL; |
409 if(!iscollection || rq->depth == 0) { |
382 UcxBuffer *sql = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND); |
410 query = rq->allprop ? sql_propfind_allprop_depth0 : sql_propfind_depth0; |
383 if(pg_create_propfind_query(rq, iscollection, sql)) { |
411 } else if(rq->depth == 1) { |
|
412 query = rq->allprop ? sql_propfind_allprop_depth1 : sql_propfind_depth1; |
|
413 } else if(rq->depth == -1) { |
|
414 query = rq->allprop ? sql_propfind_allprop_recursive : sql_propfind_recursive; |
|
415 } else { |
|
416 log_ereport(LOG_FAILURE, "%s", "pg_dav_propfind_init: invalid depth"); |
|
417 return 1; |
384 return 1; |
418 } |
385 } |
|
386 query = sql->space; |
419 |
387 |
420 // get all resources and properties |
388 // get all resources and properties |
421 char resource_id_str[32]; |
389 char resource_id_str[32]; |
422 snprintf(resource_id_str, 32, "%" PRId64, resource_id); |
390 snprintf(resource_id_str, 32, "%" PRId64, resource_id); |
423 |
391 |