cozy-pouch-link 60.30.0 → 60.30.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/db/sqlite/sql.js
CHANGED
|
@@ -144,9 +144,9 @@ var quoteSQLValue = function quoteSQLValue(value) {
|
|
|
144
144
|
return value;
|
|
145
145
|
};
|
|
146
146
|
|
|
147
|
-
var parseCondition = function parseCondition(field, condition) {
|
|
147
|
+
var parseCondition = function parseCondition(field, condition, columnName) {
|
|
148
148
|
var conditions = [];
|
|
149
|
-
var sqlField = transformMangoFieldInJSONSQL(field);
|
|
149
|
+
var sqlField = transformMangoFieldInJSONSQL(field, columnName);
|
|
150
150
|
|
|
151
151
|
if (typeof condition === 'object' && !Array.isArray(condition)) {
|
|
152
152
|
for (var operator in condition) {
|
|
@@ -189,27 +189,45 @@ var parseCondition = function parseCondition(field, condition) {
|
|
|
189
189
|
return conditions.join(' AND ');
|
|
190
190
|
};
|
|
191
191
|
|
|
192
|
-
var parseLogicalOperator = function parseLogicalOperator(operator, conditionsArray) {
|
|
192
|
+
var parseLogicalOperator = function parseLogicalOperator(operator, conditionsArray, columnName) {
|
|
193
193
|
var sqlOperator = operator === '$and' ? 'AND' : 'OR';
|
|
194
194
|
var parsedConditions = conditionsArray.map(function (cond) {
|
|
195
|
-
return "(".concat(mangoSelectorToSQL(cond).replace(/^WHERE /, ''), ")");
|
|
195
|
+
return "(".concat(mangoSelectorToSQL(cond, columnName).replace(/^WHERE /, ''), ")");
|
|
196
196
|
});
|
|
197
197
|
return parsedConditions.join(" ".concat(sqlOperator, " "));
|
|
198
|
+
}; // PouchDB keeps a document's id and rev in by-sequence COLUMNS and strips them
|
|
199
|
+
// from the stored JSON, so json_extract(json, '$._id') is NULL on every row. A
|
|
200
|
+
// selector on `_id` would therefore match nothing it is meant to match, and a
|
|
201
|
+
// `$nin` on it would filter nothing at all.
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
var PHYSICAL_COLUMNS = {
|
|
205
|
+
_id: 'doc_id',
|
|
206
|
+
_rev: 'rev'
|
|
198
207
|
};
|
|
199
208
|
|
|
200
209
|
var transformMangoFieldInJSONSQL = function transformMangoFieldInJSONSQL(field) {
|
|
201
210
|
var columnName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'data';
|
|
211
|
+
var physicalColumn = PHYSICAL_COLUMNS[field];
|
|
212
|
+
|
|
213
|
+
if (physicalColumn) {
|
|
214
|
+
// `data` is the query alias, where by-sequence is joined with document-store
|
|
215
|
+
// and the column needs qualifying; `json` is the CREATE INDEX context, which
|
|
216
|
+
// is scoped to by-sequence alone and must stay unqualified.
|
|
217
|
+
return columnName === 'json' ? physicalColumn : "'by-sequence'.".concat(physicalColumn);
|
|
218
|
+
}
|
|
219
|
+
|
|
202
220
|
return "json_extract(".concat(columnName, ", '$.").concat(field, "')");
|
|
203
221
|
};
|
|
204
222
|
|
|
205
|
-
var mangoSelectorToSQL = function mangoSelectorToSQL(selector) {
|
|
223
|
+
var mangoSelectorToSQL = function mangoSelectorToSQL(selector, columnName) {
|
|
206
224
|
var conditions = [];
|
|
207
225
|
|
|
208
226
|
for (var key in selector) {
|
|
209
227
|
if (key === '$and' || key === '$or') {
|
|
210
|
-
conditions.push(parseLogicalOperator(key, selector[key]));
|
|
228
|
+
conditions.push(parseLogicalOperator(key, selector[key], columnName));
|
|
211
229
|
} else {
|
|
212
|
-
conditions.push(parseCondition(key, selector[key]));
|
|
230
|
+
conditions.push(parseCondition(key, selector[key], columnName));
|
|
213
231
|
}
|
|
214
232
|
}
|
|
215
233
|
|
|
@@ -218,14 +236,14 @@ var mangoSelectorToSQL = function mangoSelectorToSQL(selector) {
|
|
|
218
236
|
|
|
219
237
|
exports.mangoSelectorToSQL = mangoSelectorToSQL;
|
|
220
238
|
|
|
221
|
-
var makeWhereClause = function makeWhereClause(selector) {
|
|
239
|
+
var makeWhereClause = function makeWhereClause(selector, columnName) {
|
|
222
240
|
var baseWhere = 'DELETED = 0';
|
|
223
241
|
|
|
224
242
|
if (!selector) {
|
|
225
243
|
return baseWhere;
|
|
226
244
|
}
|
|
227
245
|
|
|
228
|
-
var mangoWhere = mangoSelectorToSQL(selector);
|
|
246
|
+
var mangoWhere = mangoSelectorToSQL(selector, columnName);
|
|
229
247
|
|
|
230
248
|
if (!mangoWhere) {
|
|
231
249
|
return baseWhere;
|
|
@@ -260,11 +278,24 @@ var makeSQLQueryFromMango = function makeSQLQueryFromMango(_ref2) {
|
|
|
260
278
|
var selector = _ref2.selector,
|
|
261
279
|
sort = _ref2.sort,
|
|
262
280
|
indexName = _ref2.indexName,
|
|
281
|
+
partialFilter = _ref2.partialFilter,
|
|
263
282
|
_ref2$limit = _ref2.limit,
|
|
264
283
|
limit = _ref2$limit === void 0 ? -1 : _ref2$limit,
|
|
265
284
|
_ref2$skip = _ref2.skip,
|
|
266
285
|
skip = _ref2$skip === void 0 ? 0 : _ref2$skip;
|
|
267
|
-
var whereClause = makeWhereClause(selector);
|
|
286
|
+
var whereClause = makeWhereClause(selector); // Restate the partial filter in the query itself. Relying on the partial index
|
|
287
|
+
// alone is unsafe: CREATE INDEX IF NOT EXISTS never rewrites an index built
|
|
288
|
+
// under an older definition, so a stale unfiltered index would silently return
|
|
289
|
+
// the rows the filter is meant to hide.
|
|
290
|
+
|
|
291
|
+
if (partialFilter) {
|
|
292
|
+
var partialWhere = mangoSelectorToSQL(partialFilter);
|
|
293
|
+
|
|
294
|
+
if (partialWhere) {
|
|
295
|
+
whereClause += " AND (".concat(partialWhere, ")");
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
268
299
|
var sortClause = makeSortClause(sort); // Scan by-sequence via the mango index, then join document-store on its
|
|
269
300
|
// winningseq so only WINNING revisions survive (matches CouchDB mango, which
|
|
270
301
|
// queries the winning rev). `data` aliases 'by-sequence'.json; `deleted` lives
|
|
@@ -342,7 +373,10 @@ var makeSQLCreateMangoIndex = function makeSQLCreateMangoIndex(indexName, fields
|
|
|
342
373
|
var sql = "\n CREATE INDEX IF NOT EXISTS '".concat(indexName, "'\n ON 'by-sequence'\n (").concat(jsonIndex, ")\n ");
|
|
343
374
|
|
|
344
375
|
if (partialFilter) {
|
|
345
|
-
|
|
376
|
+
// `data` only exists as a SELECT alias of 'by-sequence'.json; inside CREATE
|
|
377
|
+
// INDEX the column is `json`. Emitting the alias made every partial index
|
|
378
|
+
// fail with "no such column: data", so the filter was never enforced.
|
|
379
|
+
var whereClause = makeWhereClause(partialFilter, 'json');
|
|
346
380
|
sql += " WHERE ".concat(whereClause);
|
|
347
381
|
}
|
|
348
382
|
|
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
makeSQLQueryForIds,
|
|
8
8
|
keepDocWitHighestRev,
|
|
9
9
|
makeSQLQueryAll,
|
|
10
|
-
parseResults
|
|
10
|
+
parseResults,
|
|
11
|
+
makeSQLCreateMangoIndex
|
|
11
12
|
} from './sql'
|
|
12
13
|
|
|
13
14
|
describe('mangoSelectorToSQL', () => {
|
|
@@ -435,3 +436,52 @@ describe('parseResults', () => {
|
|
|
435
436
|
})
|
|
436
437
|
})
|
|
437
438
|
})
|
|
439
|
+
|
|
440
|
+
describe('partialFilter', () => {
|
|
441
|
+
it('should build the index filter against the json column', () => {
|
|
442
|
+
const sql = makeSQLCreateMangoIndex('by_name', ['name'], {
|
|
443
|
+
partialFilter: { trashed: false }
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
// `data` is only a SELECT alias, CREATE INDEX would reject it.
|
|
447
|
+
expect(sql).toContain(`json_extract(json, '$.trashed') = false`)
|
|
448
|
+
expect(sql).not.toContain(`json_extract(data`)
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
it('should restate the partial filter in the query', () => {
|
|
452
|
+
const sql = makeSQLQueryFromMango({
|
|
453
|
+
selector: { dir_id: 'root' },
|
|
454
|
+
indexName: 'by_dir_id',
|
|
455
|
+
partialFilter: { trashed: false }
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
expect(sql).toContain(`json_extract(data, '$.trashed') = false`)
|
|
459
|
+
})
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
describe('_id and _rev selectors', () => {
|
|
463
|
+
it('should resolve _id to the by-sequence column in a query', () => {
|
|
464
|
+
// PouchDB strips _id from the stored JSON, so json_extract would be NULL.
|
|
465
|
+
expect(mangoSelectorToSQL({ _id: 'abc' })).toEqual(
|
|
466
|
+
`'by-sequence'.doc_id = 'abc'`
|
|
467
|
+
)
|
|
468
|
+
})
|
|
469
|
+
|
|
470
|
+
it('should resolve _rev to the by-sequence column in a query', () => {
|
|
471
|
+
expect(mangoSelectorToSQL({ _rev: '1-abc' })).toEqual(
|
|
472
|
+
`'by-sequence'.rev = '1-abc'`
|
|
473
|
+
)
|
|
474
|
+
})
|
|
475
|
+
|
|
476
|
+
it('should leave the column unqualified in an index context', () => {
|
|
477
|
+
expect(mangoSelectorToSQL({ _id: 'abc' }, 'json')).toEqual(`doc_id = 'abc'`)
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
it('should keep filtering hidden ids out of a partial index', () => {
|
|
481
|
+
const sql = makeSQLCreateMangoIndex('by_name', ['name'], {
|
|
482
|
+
partialFilter: { _id: { $nin: ['io.cozy.files.trash-dir'] } }
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
expect(sql).toContain(`doc_id NOT IN ('io.cozy.files.trash-dir')`)
|
|
486
|
+
})
|
|
487
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-pouch-link",
|
|
3
|
-
"version": "60.30.
|
|
3
|
+
"version": "60.30.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"url": "git+https://github.com/linagora/cozy-client.git"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"cozy-client": "^60.30.
|
|
16
|
+
"cozy-client": "^60.30.1",
|
|
17
17
|
"pouchdb-browser": "^7.2.2",
|
|
18
18
|
"pouchdb-find": "^7.2.2"
|
|
19
19
|
},
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"typecheck": "tsc -p tsconfig.json"
|
|
42
42
|
},
|
|
43
43
|
"sideEffects": false,
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "65d4da4479058f57d0e77f15eb40b9cc89cb7aec"
|
|
45
45
|
}
|
package/types/db/sqlite/sql.d.ts
CHANGED
|
@@ -16,13 +16,14 @@ export function parseResults(client: any, result: any, doctype: any, { isSingleD
|
|
|
16
16
|
skip: number;
|
|
17
17
|
next: boolean;
|
|
18
18
|
};
|
|
19
|
-
export function mangoSelectorToSQL(selector: any): string;
|
|
20
|
-
export function makeWhereClause(selector: any): string;
|
|
19
|
+
export function mangoSelectorToSQL(selector: any, columnName: any): string;
|
|
20
|
+
export function makeWhereClause(selector: any, columnName: any): string;
|
|
21
21
|
export function makeSortClause(mangoSortBy: any): string;
|
|
22
|
-
export function makeSQLQueryFromMango({ selector, sort, indexName, limit, skip }: {
|
|
22
|
+
export function makeSQLQueryFromMango({ selector, sort, indexName, partialFilter, limit, skip }: {
|
|
23
23
|
selector: any;
|
|
24
24
|
sort: any;
|
|
25
25
|
indexName: any;
|
|
26
|
+
partialFilter: any;
|
|
26
27
|
limit?: number;
|
|
27
28
|
skip?: number;
|
|
28
29
|
}): string;
|