├── .github └── FUNDING.yml ├── .gitignore ├── .npmignore ├── index.js ├── inmemory.js ├── license.txt ├── mongo.js ├── mysql.js ├── opendb.js ├── package.json ├── pg-lo.js ├── pg.js ├── readme.md ├── textdb.js └── total.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | patreon: totaljs 4 | open_collective: totalplatform 5 | ko_fi: totaljs 6 | liberapay: totaljs 7 | buy_me_a_coffee: totaljs 8 | custom: https://www.totaljs.com/support/ 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /logs 2 | /test.js 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /logs 2 | /test.js 3 | -------------------------------------------------------------------------------- /inmemory.js: -------------------------------------------------------------------------------- 1 | const BLACKLIST = { dbms: 1 }; 2 | const ISOP = { '+': 1, '-': 1, '*': 1, '/': 1, '=': 1, '!': 1, '#': 1 }; 3 | const InMemory = require('total4/inmemory'); 4 | 5 | var INSTANCES = {}; 6 | 7 | function select(client, cmd) { 8 | 9 | var builder = cmd.builder; 10 | var opt = builder.options; 11 | var filter = WHERE(builder, null, null); 12 | var fields = FIELDS(builder); 13 | 14 | // opt.table 15 | var data = {}; 16 | 17 | if (fields) 18 | data.fields = fields; 19 | 20 | data.filter = filter.filter; 21 | data.filterarg = { arg: filter.arg }; 22 | 23 | if (filter.sort) 24 | data.sort = filter.sort; 25 | 26 | if (filter.take) 27 | data.take = filter.take; 28 | 29 | if (filter.skip) 30 | data.skip = filter.skip; 31 | 32 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 33 | 34 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table] = InMemory.load(PATH.databases(opt.table + '.inmemory'))); 35 | 36 | conn.find().assign(data).callback(function(err, response, meta) { 37 | 38 | builder.db.busy = false; 39 | 40 | var rows = response; 41 | err && client.$opt.onerror && client.$opt.onerror(err, data); 42 | 43 | if (opt.first) 44 | rows = rows[0] || null; 45 | 46 | // checks joins 47 | if (!err && builder.$joins) { 48 | client.$dbms._joins(rows, builder); 49 | setImmediate(builder.db.$next); 50 | } else 51 | builder.$callback(err, rows, meta.count); 52 | }); 53 | } 54 | 55 | function check(client, cmd) { 56 | var builder = cmd.builder; 57 | var opt = builder.options; 58 | var filter = WHERE(builder); 59 | var data = {}; 60 | 61 | data.filter = filter.filter; 62 | data.filterarg = { arg: filter.arg }; 63 | data.take = 1; 64 | data.limit = 1; 65 | data.first = true; 66 | 67 | if (!cmd.value && builder.options.params) 68 | cmd.value = []; 69 | 70 | builder.db.$debug && builder.db.$debug(data); 71 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 72 | 73 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table] = InMemory.load(PATH.databases(opt.table + '.inmemory'))); 74 | 75 | conn.find().assign(data).callback(function(err, response, meta) { 76 | builder.db.busy = false; 77 | var is = !err && !!response; 78 | err && client.$opt.onerror && client.$opt.onerror(err, data); 79 | builder.$callback(err, is, meta.count); 80 | }); 81 | } 82 | 83 | function query(client, cmd) { 84 | var builder = cmd.builder; 85 | var opt = builder.options; 86 | var filter = WHERE(builder); 87 | var data = {}; 88 | 89 | data.filter = filter.filter; 90 | data.filterarg = { arg: filter.arg }; 91 | data.scalar = cmd.query; 92 | data.scalararg = cmd.value || {}; 93 | 94 | builder.db.$debug && builder.db.$debug(data); 95 | F.$events.dbms && EMIT('dbms', 'query', opt.table, opt.db, builder); 96 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table || opt.db] = InMemory.load(PATH.databases(opt.table || opt.db + '.inmemory'))); 97 | 98 | conn.find().assign(data).callback(function(err, response, meta) { 99 | builder.db.busy = false; 100 | err && client.$opt.onerror && client.$opt.onerror(err, data); 101 | builder.$callback(err, response, meta.count); 102 | }); 103 | } 104 | 105 | function scalar(client, cmd) { 106 | 107 | var builder = cmd.builder; 108 | var opt = builder.options; 109 | var scalar = ''; 110 | var data = {}; 111 | var filter = WHERE(builder); 112 | 113 | data.filter = filter.filter; 114 | data.filterarg = { arg: filter.arg }; 115 | data.scalar = scalar; 116 | data.scalararg = {}; 117 | 118 | var name = cmd.name; 119 | if (name) { 120 | var index = name.indexOf(' as '); 121 | if (index !== -1) { 122 | name = name.substring(index + 4); 123 | cmd.name = cmd.name.substring(0, index); 124 | } 125 | } 126 | 127 | switch (cmd.scalar) { 128 | case 'group': 129 | data.scalar = cmd.field ? ('var k=doc.' + cmd.name + '+\'\';if (arg[k]){arg[k]+=doc.' + cmd.field + '||0}else{arg[k]=doc.' + cmd.field + '||0}') : ('var k=doc.' + cmd.name + '+\'\';if (arg[k]){arg[k]++}else{arg[k]=1}'); 130 | break; 131 | default: 132 | if (cmd.field) { 133 | data.scalar = 'var k=doc.' + cmd.name + '+\'\';if (arg[k]){tmp.bk=doc.' + cmd.field + '||0;' + (cmd.scalar === 'max' ? 'if(tmp.bk>arg[k])arg[k]=tmp.bk' : cmd.scalar === 'min' ? 'if(tmp.bktmp.val?tmp.val:arg.min;arg.max=arg.max==null?tmp.val:arg.max': 214 | case '<': 215 | key = key.substring(1); 216 | break; 217 | case '=': 218 | key = key.substring(1); 219 | break; 220 | case '#': 221 | break; 222 | case '!': 223 | // toggle 224 | key = key.substring(1); 225 | if (val) 226 | val = true; 227 | else 228 | val = false; 229 | break; 230 | } 231 | 232 | doc[key] = val == null ? null : typeof(val) === 'function' ? val(builder.value) : val; 233 | } 234 | 235 | // builder.db.$debug && builder.db.$debug(q); 236 | F.$events.dbms && EMIT('dbms', 'insert', opt.table, opt.db, builder); 237 | 238 | var data = {}; 239 | data.payload = doc; 240 | 241 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table] = InMemory.load(PATH.databases(opt.table + '.inmemory'))); 242 | 243 | conn.insert().assign(data).callback(function(err, response) { 244 | builder.db.busy = false; 245 | err && client.$opt.onerror && client.$opt.onerror(err, data); 246 | builder.$callback(err, err == null ? response : 0); 247 | }); 248 | } 249 | 250 | function insertexists(client, cmd) { 251 | 252 | var builder = cmd.builder; 253 | var opt = builder.options; 254 | var filter = WHERE(cmd.builder); 255 | var data = {}; 256 | 257 | data = {}; 258 | data.take = 1; 259 | data.filter = filter.filter; 260 | data.filterarg = { arg: filter.arg }; 261 | data.limit = 1; 262 | data.first = true; 263 | 264 | F.$events.dbms && EMIT('dbms', 'select', opt.table, data, builder); 265 | 266 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table] = InMemory.load(PATH.databases(opt.table + '.inmemory'))); 267 | 268 | conn.find().assign(data).callback(function(err, response) { 269 | builder.db.busy = false; 270 | err && client.$opt.onerror && client.$opt.onerror(err, data); 271 | if (response) 272 | builder.$callback(err, 0); 273 | else 274 | insert(client, cmd); 275 | }); 276 | } 277 | 278 | function modify(client, cmd) { 279 | 280 | cmd.builder.options.transform && cmd.builder.options.transform(cmd.builder.value, cmd.builder.db.$output, cmd.builder.db.$lastoutput); 281 | 282 | var keys = Object.keys(cmd.builder.value); 283 | var params = []; 284 | var arr = []; 285 | var builder = []; 286 | var tmp; 287 | for (var i = 0; i < keys.length; i++) { 288 | var key = keys[i]; 289 | var val = cmd.builder.value[key]; 290 | 291 | if (val === undefined || BLACKLIST[key]) 292 | continue; 293 | 294 | if (cmd.builder.options.equal && cmd.builder.options.equal.indexOf(key) !== -1) 295 | continue; 296 | 297 | if (cmd.builder.options.fields && cmd.builder.options.fields.length) { 298 | var skip = true; 299 | for (var j = 0; j < cmd.builder.options.fields.length; j++) { 300 | var field = cmd.builder.options.fields[j]; 301 | if (field[0] === '-') { 302 | field = field.substring(1); 303 | if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 304 | skip = true; 305 | break; 306 | } 307 | skip = false; 308 | } else if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 309 | skip = false; 310 | break; 311 | } else 312 | skip = false; 313 | } 314 | 315 | if (skip) 316 | continue; 317 | } 318 | 319 | var c = key[0]; 320 | 321 | if (typeof(val) === 'function') 322 | val = val(cmd.builder.value); 323 | 324 | switch (c) { 325 | case '-': 326 | case '+': 327 | case '*': 328 | case '/': 329 | key = key.substring(1); 330 | params.push(val ? val : 0); 331 | builder.push('doc.' + key + '=(doc.' + key + '||0)' + c + push(arr, val ? val : 0)); 332 | break; 333 | case '>': 334 | case '<': 335 | tmp = push(arr, val ? val : 0); 336 | key = key.substring(1); 337 | builder.push('doc.' + key + '=(doc.' + key + '||0)' + c + tmp + '?(doc.' + key + '||0):' + tmp); 338 | break; 339 | case '!': 340 | // toggle 341 | key = key.substring(1); 342 | builder.push('doc.' + key + '=!doc.' + key); 343 | break; 344 | case '=': 345 | case '#': 346 | // raw 347 | builder.push('doc.' + key + '=' + val); 348 | break; 349 | default: 350 | builder.push('doc.' + key + '=' + push(arr, val)); 351 | break; 352 | } 353 | } 354 | 355 | var opt = cmd.builder.options; 356 | 357 | if (opt.equal) { 358 | for (var i = 0; i < opt.equal.length; i++) 359 | cmd.builder.where(opt.equal[i], builder.value[opt.equal[i]]); 360 | } 361 | 362 | var filter = WHERE(cmd.builder); 363 | var data = {}; 364 | 365 | data.filter = filter.filter; 366 | data.filterarg = { arg: filter.arg }; 367 | data.modify = builder.join(';'); 368 | data.modifyarg = { arg: arr }; 369 | 370 | if (filter.take) 371 | data.take = filter.take; 372 | 373 | if (filter.skip) 374 | data.skip = filter.skip; 375 | 376 | cmd.builder.db.$debug && cmd.builder.db.$debug(data); 377 | F.$events.dbms && EMIT('dbms', 'update', data, builder); 378 | 379 | data.db = opt.table; 380 | 381 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table] = InMemory.load(PATH.databases(opt.table + '.inmemory'))); 382 | 383 | conn.update().assign(data).callback(function(err, response, meta) { 384 | cmd.builder.db.busy = false; 385 | err && client.$opt.onerror && client.$opt.onerror(err, data); 386 | if (!response && cmd.insert) { 387 | if (cmd.insert !== true) 388 | cmd.builder.value = cmd.insert; 389 | cmd.builder.options.insert && cmd.builder.options.insert(cmd.builder.value, cmd.builder.options.insertparams); 390 | insert(client, cmd); 391 | } else 392 | cmd.builder.$callback(err, response, meta.count); 393 | }); 394 | } 395 | 396 | function remove(client, cmd) { 397 | var builder = cmd.builder; 398 | var opt = cmd.builder.options; 399 | var filter = WHERE(builder); 400 | var data = {}; 401 | data.filter = filter.filter; 402 | data.filterarg = { arg: filter.arg }; 403 | 404 | if (filter.take) 405 | data.take = filter.take; 406 | 407 | if (filter.skip) 408 | data.skip = filter.skip; 409 | 410 | builder.db.$debug && builder.db.$debug(data); 411 | F.$events.dbms && EMIT('dbms', 'delete', opt.table, opt.db, builder); 412 | 413 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table] = InMemory.load(PATH.databases(opt.table + '.inmemory'))); 414 | 415 | conn.remove().assign(data).callback(function(err, response, meta) { 416 | cmd.builder.db.busy = false; 417 | err && client.$opt.onerror && client.$opt.onerror(err, data); 418 | cmd.builder.$callback(err, response, meta.count); 419 | }); 420 | } 421 | 422 | function clientcommand(cmd, client) { 423 | switch (cmd.type) { 424 | case 'transaction': 425 | case 'end': 426 | case 'commit': 427 | case 'rollback': 428 | break; 429 | case 'find': 430 | case 'read': 431 | case 'list': 432 | select(client, cmd); 433 | break; 434 | case 'check': 435 | check(client, cmd); 436 | break; 437 | case 'scalar': 438 | scalar(client, cmd); 439 | break; 440 | case 'insert': 441 | if (cmd.unique) 442 | insertexists(client, cmd); 443 | else 444 | insert(client, cmd); 445 | break; 446 | 447 | case 'modify2': 448 | var cb = cmd.builder.$callback; 449 | cmd.builder.$callback = function(err, response) { 450 | cmd.builder.options.fields = null; 451 | if (err) { 452 | cb.call(cmd.builder, err, 0); 453 | } else if (response) { 454 | var mod = cmd.fn(response); 455 | if (mod) { 456 | cmd.builder.value = mod; 457 | cmd.builder.$callback = cb; 458 | if (cmd.builder.value.$clean) 459 | cmd.builder.value = cmd.builder.value.$clean(); 460 | modify(client, cmd); 461 | } else 462 | cb.call(cmd.builder, err, 0); 463 | } else { 464 | if (cmd.insert) { 465 | mod = cmd.fn(null); 466 | if (mod) { 467 | cmd.builder.value = mod; 468 | cmd.builder.$callback = cb; 469 | insert(client, cmd); 470 | } else 471 | cb.call(cmd.builder, err, 0); 472 | } else { 473 | cb.call(cmd.builder, err, 0); 474 | } 475 | } 476 | }; 477 | select(client, cmd); 478 | break; 479 | 480 | case 'update': 481 | case 'modify': 482 | modify(client, cmd); 483 | break; 484 | case 'remove': 485 | remove(client, cmd); 486 | break; 487 | case 'query': 488 | query(client, cmd); 489 | break; 490 | default: 491 | cmd.builder.$callback(new Error('Operation "' + cmd.type + '" not found')); 492 | break; 493 | } 494 | } 495 | 496 | exports.run = function(opt, self, cmd) { 497 | self.$op = null; 498 | self.busy = true; 499 | self.$opt = opt; 500 | clientcommand(cmd, opt); 501 | }; 502 | 503 | function push(arr, value) { 504 | return 'arg.arg[' + (arr.push(value) - 1) + ']'; 505 | } 506 | 507 | function WHERE(builder) { 508 | 509 | var condition = []; 510 | var sort = ''; 511 | var op = '&&'; 512 | var opuse = false; 513 | var arg = []; 514 | 515 | for (var i = 0; i < builder.$commands.length; i++) { 516 | var cmd = builder.$commands[i]; 517 | 518 | if (builder.options.islanguage && cmd.name && cmd.name[cmd.name.length - 1] === '§') 519 | cmd.name = cmd.name.substring(0, cmd.name.length - 1) + (builder.options.language || ''); 520 | 521 | switch (cmd.type) { 522 | case 'where': 523 | opuse && condition.length && condition.push(op); 524 | if (cmd.compare === '<>') 525 | cmd.compare = '!='; 526 | else if (cmd.compare === '=') 527 | cmd.compare = '=='; 528 | 529 | if (cmd.value === undefined) 530 | condition.push(cmd.name); 531 | else { 532 | var tmp = push(arg, cmd.value); 533 | condition.push('doc.' + cmd.name + ' instanceof Array?(doc.' + cmd.name + '.indexOf(' + tmp + ')' + (cmd.compare === '==' ? '!=' : '==') + '-1):(doc.' + cmd.name + cmd.compare + tmp + ')'); 534 | } 535 | 536 | break; 537 | case 'custom': 538 | cmd.fn.call(builder, builder, builder.db.$output, builder.db.$lastoutput); 539 | break; 540 | case 'in': 541 | 542 | if (typeof(cmd.value) === 'function') 543 | cmd.value = cmd.value(); 544 | 545 | if (cmd.value instanceof Array) { 546 | 547 | if (cmd.field) { 548 | var tmp = []; 549 | for (var j = 0; j < cmd.value.length; j++) { 550 | if (cmd.value[j]) 551 | tmp.push(cmd.value[j][cmd.field]); 552 | } 553 | cmd.value = tmp; 554 | } 555 | 556 | opuse && condition.length && condition.push(op); 557 | condition.push(push(arg, cmd.value) + '.indexOf(doc.' + cmd.name + ')!==-1'); 558 | } else { 559 | opuse && condition.length && condition.push(op); 560 | condition.push('doc.' + cmd.name + '==' + push(arg, cmd.field ? cmd.value[cmd.field] : cmd.value)); 561 | } 562 | break; 563 | case 'notin': 564 | 565 | if (typeof(cmd.value) === 'function') 566 | cmd.value = cmd.value(); 567 | 568 | if (cmd.value instanceof Array) { 569 | 570 | if (cmd.field) { 571 | var tmp = []; 572 | for (var j = 0; j < cmd.value.length; j++) { 573 | if (cmd.value[j]) 574 | tmp.push(cmd.value[j][cmd.field]); 575 | } 576 | cmd.value = tmp; 577 | } 578 | 579 | opuse && condition.length && condition.push(op); 580 | condition.push(push(arg, cmd.value) + '.indexOf(doc.' + cmd.name + ')===-1'); 581 | } else { 582 | opuse && condition.length && condition.push(op); 583 | condition.push('doc.' + cmd.name + '!=' + push(arg, cmd.field ? cmd.value[cmd.field] : cmd.value)); 584 | } 585 | 586 | break; 587 | case 'between': 588 | opuse && condition.length && condition.push(op); 589 | condition.push('(doc.' + cmd.name + '>=' + push(arg, cmd.a) + '&&doc.' + cmd.name + '<=' + push(arg, cmd.b) + ')'); 590 | break; 591 | case 'search': 592 | // tmp = ESCAPE((!cmd.compare || cmd.compare === '*' ? ('%' + cmd.value + '%') : (cmd.compare === 'beg' ? ('%' + cmd.value) : (cmd.value + '%')))); 593 | opuse && condition.length && condition.push(op); 594 | condition.push('doc.' + cmd.name + '.indexOf(' + push(arg, cmd.value) + ')!==-1'); 595 | break; 596 | 597 | case 'searchfull': 598 | // tmp = ESCAPE('%' + cmd.value.toLowerCase().replace(/y/g, 'i') + '%'); 599 | // opuse && condition.length && condition.push(op); 600 | // condition.push('REPLACE(LOWER(to_tsvector(' + builder.options.table + '::text)::text), \'y\', \'i\') ILIKE ' + tmp); 601 | break; 602 | 603 | case 'searchall': 604 | // tmp = ''; 605 | // for (var j = 0; j < cmd.value.length; j++) 606 | // tmp += (tmp ? ' AND ' : '') + cmd.name + ' ILIKE ' + ESCAPE('%' + cmd.value[j] + '%'); 607 | // opuse && condition.length && condition.push(op); 608 | // condition.push('(' + (tmp || '0=1') + ')'); 609 | break; 610 | 611 | case 'fulltext': 612 | // tmp = ESCAPE('%' + cmd.value.toLowerCase() + '%'); 613 | // opuse && condition.length && condition.push(op); 614 | // condition.push('LOWER(' + cmd.name + ') ILIKE ' + tmp); 615 | break; 616 | case 'contains': 617 | opuse && condition.length && condition.push(op); 618 | condition.push('!!doc.' + cmd.name); 619 | break; 620 | case 'query': 621 | opuse && condition.length && condition.push(op); 622 | condition.push(cmd.query); 623 | break; 624 | case 'permit': 625 | break; 626 | case 'empty': 627 | opuse && condition.length && condition.push(op); 628 | condition.push('!doc.' + cmd.name); 629 | break; 630 | case 'month': 631 | case 'year': 632 | case 'day': 633 | case 'hour': 634 | case 'minute': 635 | opuse && condition.length && condition.push(op); 636 | var type = cmd.type === 'month' ? 'Month' : cmd.type === 'year' ? 'FullYear' : cmd.type === 'day' ? 'Date' : cmd.type === 'minute' ? 'Minutes' : cmd.type === 'hours' ? 'Hours' : 'Seconds'; 637 | condition.push('doc.' + cmd.name + ' instanceof Date?(doc.' + cmd.name + '.get' + type + '()===' + cmd.value + '):false'); 638 | break; 639 | case 'date': 640 | opuse && condition.length && condition.push(op); 641 | condition.push(cmd.value instanceof Date ? ('doc.' + cmd.name + ' instanceof Date?(doc.' + cmd.name + '.getDate()===' + cmd.value.getDate() + '&&doc.' + cmd.name + '.getMonth()===' + cmd.value.getMonth() + '&&doc.' + cmd.name + '.getFullYear()===' + cmd.value.getFullYear() + '):false') : ('!doc.' + cmd.name)); 642 | break; 643 | case 'or': 644 | opuse && condition.length && condition.push(op); 645 | op = '||'; 646 | opuse = false; 647 | condition.push('('); 648 | continue; 649 | case 'end': 650 | condition.push(')'); 651 | op = '&&'; 652 | break; 653 | case 'and': 654 | opuse && condition.length && condition.push(op); 655 | op = '&&'; 656 | break; 657 | case 'sort': 658 | sort = cmd.name + '_' + (cmd.desc ? 'desc' : 'asc'); 659 | break; 660 | case 'regexp': 661 | // tmp = cmd.value.toString().substring(1); 662 | // var g = '~'; 663 | // if (tmp[tmp.length - 1] === 'i') { 664 | // tmp = tmp.substring(0, tmp.length - 2); 665 | // g = '~*'; 666 | // } else 667 | // tmp = tmp.substring(0, tmp.length - 1); 668 | // opuse && condition.length && condition.push(op); 669 | // condition.push(cmd.name + g + '\'' + tmp + '\''); 670 | break; 671 | } 672 | opuse = true; 673 | } 674 | 675 | // var query = (condition.length ? (' WHERE ' + condition.join(' ')) : '') + (group ? (' GROUP BY ' + group) : ''); 676 | return { filter: condition.length ? condition.join('') : 'true', arg: arg, sort: sort, take: builder.options.take, skip: builder.options.skip }; 677 | } 678 | 679 | function FIELDS(builder) { 680 | var fields = builder.options.fields || ''; 681 | return fields + (fields && fields.length && builder.$joinmeta ? (',' + builder.$joinmeta.a) : ''); 682 | } 683 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | Copyright 2018-2021 (c) Peter Širka 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a 5 | copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to permit 9 | persons to whom the Software is furnished to do so, subject to the 10 | following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /mongo.js: -------------------------------------------------------------------------------- 1 | const MongoDB = require('mongodb'); 2 | const MongoClient = MongoDB.MongoClient; 3 | const EMPTYARRAY = []; 4 | const INSERTPROJECTION = { projection: { '_id': 1 }}; 5 | const BUCKETNAME = { bucketName: 'db' }; 6 | const BLACKLIST = { dbms: 1 }; 7 | 8 | global.ObjectID = MongoDB.ObjectID; 9 | 10 | function select(client, cmd) { 11 | 12 | var builder = cmd.builder; 13 | var opt = builder.options; 14 | var filter = WHERE(builder); 15 | var options = {}; 16 | 17 | var fields = FIELDS(builder); 18 | 19 | if (fields) 20 | options.projection = fields; 21 | 22 | if (opt.take) 23 | options.limit = opt.take; 24 | 25 | if (opt.skip) 26 | options.skip = opt.skip; 27 | 28 | if (filter.sort) 29 | options.sort = filter.sort; 30 | 31 | builder.db.$debug && builder.db.$debug({ collection: client.$database + '.' + opt.table, condition: filter.where, options: options }); 32 | 33 | client.db(client.$database).collection(opt.table).find(filter.where, options).toArray(function(err, response) { 34 | 35 | client.close(); 36 | err && client.$opt.onerror && client.$opt.onerror(err, opt, builder); 37 | 38 | var rows = response ? response : EMPTYARRAY; 39 | if (opt.first) 40 | rows = rows.length ? rows[0] : null; 41 | 42 | // checks joins 43 | if (!err && builder.$joins) { 44 | client.$dbms._join(rows, builder); 45 | setImmediate(builder.db.$next); 46 | } else 47 | builder.$callback(err, rows); 48 | }); 49 | } 50 | 51 | function query(client, cmd) { 52 | var builder = cmd.builder; 53 | var opt = builder.options; 54 | var filter = WHERE(builder); 55 | var options = {}; 56 | 57 | var fields = FIELDS(builder); 58 | 59 | if (fields) 60 | options.projection = fields; 61 | 62 | if (opt.take) 63 | options.limit = opt.take; 64 | 65 | if (opt.skip) 66 | options.skip = opt.skip; 67 | 68 | if (filter.sort) 69 | options.sort = filter.sort; 70 | 71 | var col = client.db(client.$database).collection(cmd.query); 72 | cmd.value(col, function(err, response) { 73 | if (opt.first && response instanceof Array) 74 | response = response[0]; 75 | client.close(); 76 | err && client.$opt.onerror && client.$opt.onerror(err, opt, builder); 77 | builder.$callback(err, response); 78 | }, filter.where, options); 79 | } 80 | 81 | function list(client, cmd) { 82 | 83 | var builder = cmd.builder; 84 | var opt = builder.options; 85 | var filter = WHERE(builder); 86 | var options = {}; 87 | var fields = FIELDS(builder); 88 | 89 | if (fields) 90 | options.projection = fields; 91 | 92 | if (opt.take) 93 | options.limit = opt.take; 94 | 95 | if (opt.skip) 96 | options.skip = opt.skip; 97 | 98 | if (filter.sort) 99 | options.sort = filter.sort; 100 | 101 | builder.db.$debug && builder.db.$debug({ collection: client.$database + '.' + opt.table, condition: filter.where, options: options }); 102 | 103 | var db = client.db(client.$database).collection(opt.table); 104 | db.countDocuments(filter.where, function(err, count) { 105 | if (err) { 106 | client.close(); 107 | err && client.$opt.onerror && client.$opt.onerror(err, opt, builder); 108 | builder.$callback(err, null); 109 | } else { 110 | db.find(filter.where, options).toArray(function(err, response) { 111 | client.close(); 112 | if (!err && builder.$joins) { 113 | client.$dbms._joins(response, builder, count || 0); 114 | setImmediate(builder.db.$next); 115 | } else 116 | builder.$callback(err, response, count); 117 | }); 118 | } 119 | }); 120 | } 121 | 122 | function scalar(client, cmd) { 123 | 124 | var builder = cmd.builder; 125 | var opt = builder.options; 126 | var filter = WHERE(builder, true); 127 | var cmdgroup = '$' + cmd.name; 128 | 129 | // builder.db.$debug && builder.db.$debug(q); 130 | 131 | switch (cmd.scalar) { 132 | case 'count': 133 | client.db(client.$database).collection(opt.table).aggregate([ filter, {$count: 'count'} ]).toArray(function(err, response) { 134 | err && client.$opt.onerror && client.$opt.onerror(err, opt, builder); 135 | client.close(); 136 | builder.$callback(err, response); 137 | }); 138 | break; 139 | case 'avg': 140 | client.db(client.$database).collection(opt.table).aggregate([ filter, {$group: { _id: cmdgroup, average:{ $avg: cmdgroup }}}, { $sort: { average: -1 } }]).toArray(function(err, response) { 141 | err && client.$opt.onerror && client.$opt.onerror(err, opt, builder); 142 | client.close(); 143 | builder.$callback(err, response); 144 | }); 145 | break; 146 | case 'min': 147 | client.db(client.$database).collection(opt.table).aggregate([ filter, {$group: { _id: cmdgroup, min:{ $min: cmdgroup }}}, { $sort: { min: -1 } }]).toArray(function(err, response) { 148 | err && client.$opt.onerror && client.$opt.onerror(err, opt, builder); 149 | client.close(); 150 | builder.$callback(err, response); 151 | }); 152 | break; 153 | case 'max': 154 | client.db(client.$database).collection(opt.table).aggregate([ filter, {$group: { _id: cmdgroup, max:{ $max: cmdgroup }}}, { $sort: { max: -1 } }]).toArray(function(err, response) { 155 | err && client.$opt.onerror && client.$opt.onerror(err, opt, builder); 156 | client.close(); 157 | builder.$callback(err, response); 158 | }); 159 | break; 160 | case 'sum': 161 | client.db(client.$database).collection(opt.table).aggregate([ filter, {$group:{ _id:cmdgroup, total:{$sum:cmdgroup}}}, { $sort: { total: -1 } }]).toArray(function(err, response) { 162 | err && client.$opt.onerror && client.$opt.onerror(err, opt, builder); 163 | client.close(); 164 | builder.$callback(err, response); 165 | }); 166 | break; 167 | case 'group': 168 | client.db(client.$database).collection(opt.table).aggregate([ filter, {$group:{ _id:cmdgroup, count:{$sum:1}}}, { $sort: { count: -1 }}]).toArray(function(err, response) { 169 | err && client.$opt.onerror && client.$opt.onerror(err, opt, builder); 170 | client.close(); 171 | builder.$callback(err, response); 172 | }); 173 | break; 174 | } 175 | } 176 | 177 | function insert(client, cmd) { 178 | 179 | var builder = cmd.builder; 180 | var keys = Object.keys(cmd.builder.value); 181 | var opt = builder.options; 182 | var params = {}; 183 | 184 | for (var i = 0; i < keys.length; i++) { 185 | var key = keys[i]; 186 | var val = cmd.builder.value[key]; 187 | if (val === undefined || BLACKLIST[key]) 188 | continue; 189 | 190 | if (cmd.builder.options.fields && cmd.builder.options.fields.length) { 191 | var skip = true; 192 | for (var j = 0; j < cmd.builder.options.fields.length; j++) { 193 | if (cmd.builder.options.fields[j] == key || cmd.builder.options.fields[j] == key.substring(1)) { 194 | skip = false; 195 | break; 196 | } 197 | } 198 | if (skip) 199 | continue; 200 | } 201 | 202 | switch (key[0]) { 203 | case '-': 204 | case '+': 205 | case '*': 206 | case '/': 207 | key = key.substring(1); 208 | break; 209 | } 210 | 211 | params[key] = val == null ? null : typeof(val) === 'function' ? val(cmd.builder.value) : val; 212 | } 213 | 214 | // builder.db.$debug && builder.db.$debug(); 215 | 216 | client.db(client.$database).collection(opt.table).insertOne(params, function(err, response) { 217 | client.close(); 218 | err && client.$opt.onerror && client.$opt.onerror(err, opt, builder); 219 | builder.$callback(err, response && response.result && response.result.n ? 1 : 0); 220 | }); 221 | } 222 | 223 | function insertexists(client, cmd) { 224 | var builder = cmd.builder; 225 | var opt = builder.options; 226 | var filter = WHERE(builder); 227 | 228 | // builder.db.$debug && builder.db.$debug(q); 229 | 230 | client.db(client.$database).collection(opt.table).findOne(filter.where, INSERTPROJECTION, function(err, response) { 231 | if (err || response) { 232 | client.close(); 233 | err && client.$opt.onerror && client.$opt.onerror(err, opt, builder); 234 | builder.$callback(err, 0); 235 | } else 236 | insert(client, cmd); 237 | }); 238 | } 239 | 240 | function modify(client, cmd) { 241 | 242 | var keys = Object.keys(cmd.builder.value); 243 | var params = null; 244 | var increment = null; 245 | 246 | for (var i = 0; i < keys.length; i++) { 247 | var key = keys[i]; 248 | var val = cmd.builder.value[key]; 249 | 250 | if (val === undefined || BLACKLIST[key]) 251 | continue; 252 | 253 | if (cmd.builder.options.fields && cmd.builder.options.fields.length) { 254 | var skip = true; 255 | for (var j = 0; j < cmd.builder.options.fields.length; j++) { 256 | if (cmd.builder.options.fields[j] == key || cmd.builder.options.fields[j] == key.substring(1)) { 257 | skip = false; 258 | break; 259 | } 260 | } 261 | if (skip) 262 | continue; 263 | } 264 | 265 | var c = key[0]; 266 | 267 | if (typeof(val) === 'function') 268 | val = val(cmd.builder.value); 269 | 270 | switch (c) { 271 | case '-': 272 | case '+': 273 | case '*': 274 | case '/': 275 | !increment && (increment = {}); 276 | key = key.substring(1); 277 | increment[key] = val ? val : 0; 278 | break; 279 | default: 280 | !params && (params = {}); 281 | params[key] = val; 282 | break; 283 | } 284 | } 285 | 286 | var builder = cmd.builder; 287 | var opt = builder.options; 288 | var filter = WHERE(builder); 289 | // builder.db.$debug && builder.db.$debug(q); 290 | 291 | var upd = {}; 292 | 293 | increment && (upd.$inc = increment); 294 | params && (upd.$set = params); 295 | 296 | var col = client.db(client.$database).collection(opt.table); 297 | var callback = function(err, response) { 298 | err && client.$opt.onerror && client.$opt.onerror(err, opt, builder); 299 | var count = response && response.result ? response.result.n : 0; 300 | if (!count && cmd.insert) { 301 | if (cmd.insert !== true) 302 | cmd.builder.value = cmd.insert; 303 | cmd.builder.options.insert && cmd.builder.options.insert(cmd.builder.value); 304 | insert(client, cmd); 305 | } else { 306 | client.close(); 307 | builder.$callback(err, count); 308 | } 309 | }; 310 | 311 | if (opt.first) 312 | col.updateOne(filter.where, upd, callback); 313 | else 314 | col.updateMany(filter.where, upd, callback); 315 | } 316 | 317 | function remove(client, cmd) { 318 | 319 | var builder = cmd.builder; 320 | var opt = builder.options; 321 | var filter = WHERE(builder); 322 | 323 | // builder.db.$debug && builder.db.$debug(q); 324 | 325 | var col = client.db(client.$database).collection(opt.table); 326 | var callback = function(err, response) { 327 | client.close(); 328 | err && client.$opt.onerror && client.$opt.onerror(err, opt, builder); 329 | builder.$callback(err, response && response.result ? response.result.n : 0); 330 | }; 331 | 332 | if (opt.first) 333 | col.deleteOne(filter.where, callback); 334 | else 335 | col.deleteMany(filter.where, callback); 336 | } 337 | 338 | exports.run = function(opt, self, cmd) { 339 | var client = new MongoClient(opt.options); 340 | client.connect(function(err) { 341 | 342 | client.$opt = opt; 343 | client.$dbms = self; 344 | client.$database = opt.database; 345 | 346 | if (err) { 347 | opt.onerror && client.$opt.onerror(err); 348 | if (cmd.builder) 349 | cmd.builder.$callback(err); 350 | else 351 | cmd.db.$next(err); 352 | } else { 353 | switch (cmd.type) { 354 | case 'transaction': 355 | case 'commit': 356 | case 'rollback': 357 | cmd.$next(new Error('"' + cmd.type + '" is not implemented.')); 358 | break; 359 | case 'find': 360 | case 'read': 361 | select(client, cmd); 362 | break; 363 | case 'diff': 364 | var cb = cmd.builder.$callback; 365 | cmd.builder.$callback = function(err, response) { 366 | cb.call(cmd.builder, err, err ? EMPTYOBJECT : DIFFARR(cmd.key, response, [cmd.form])); 367 | }; 368 | select(client, cmd); 369 | break; 370 | case 'list': 371 | list(client, cmd); 372 | break; 373 | case 'scalar': 374 | scalar(client, cmd); 375 | break; 376 | case 'insert': 377 | if (cmd.unique) 378 | insertexists(client, cmd); 379 | else 380 | insert(client, cmd); 381 | break; 382 | case 'update': 383 | case 'modify': 384 | modify(client, cmd); 385 | break; 386 | case 'remove': 387 | remove(client, cmd); 388 | break; 389 | case 'query': 390 | query(client, cmd); 391 | break; 392 | default: 393 | client.close(); 394 | cmd.builder.$callback(new Error('Operation "' + cmd.type + '" not found')); 395 | break; 396 | } 397 | } 398 | }); 399 | }; 400 | 401 | exports.blob_read = function(opt, id, callback, conn) { 402 | var client = new MongoClient(opt.options); 403 | 404 | client.connect(function(err) { 405 | 406 | if (err) { 407 | callback(err); 408 | return; 409 | } 410 | 411 | if (conn.table && conn.table !== 'default') 412 | BUCKETNAME.bucketName = conn.table; 413 | else 414 | BUCKETNAME.bucketName = 'db'; 415 | 416 | var done = () => client.close(); 417 | var bucket = new MongoDB.GridFSBucket(client.db(opt.database), BUCKETNAME); 418 | var stream = bucket.openDownloadStream(id); 419 | 420 | stream.on('error', done); 421 | stream.on('end', done); 422 | 423 | callback(null, stream); 424 | }); 425 | }; 426 | 427 | exports.blob_write = function(opt, stream, name, callback, conn) { 428 | var client = new MongoClient(opt.options); 429 | client.connect(function(err) { 430 | 431 | if (err) { 432 | callback(err); 433 | return; 434 | } 435 | 436 | if (conn.table && conn.table !== 'default') 437 | BUCKETNAME.bucketName = conn.table; 438 | else 439 | BUCKETNAME.bucketName = 'db'; 440 | 441 | var options = global.U ? { contentType: U.getContentType(U.getExtension(name)) } : null; 442 | var bucket = new MongoDB.GridFSBucket(client.db(opt.database), BUCKETNAME); 443 | var writer = bucket.openUploadStream(name, options); 444 | 445 | stream.pipe(writer).on('error', function(err) { 446 | client.close(); 447 | callback(err); 448 | }).on('finish', function() { 449 | client.close(); 450 | callback(null, writer.id); 451 | }); 452 | }); 453 | }; 454 | 455 | exports.blob_remove = function(opt, id, callback, conn) { 456 | var client = new MongoClient(opt.options); 457 | client.connect(function(err) { 458 | 459 | if (err) { 460 | callback && callback(err); 461 | return; 462 | } 463 | 464 | if (conn.table && conn.table !== 'default') 465 | BUCKETNAME.bucketName = conn.table; 466 | else 467 | BUCKETNAME.bucketName = 'db'; 468 | 469 | var bucket = new MongoDB.GridFSBucket(client.db(opt.database), BUCKETNAME); 470 | bucket.delete(id, function(err) { 471 | client.close(); 472 | callback && callback(err); 473 | }); 474 | }); 475 | }; 476 | 477 | function eqgtlt(cmd) { 478 | switch (cmd.compare) { 479 | case '>': 480 | return { $gt: cmd.value }; 481 | case '>=': 482 | return { $gte: cmd.value }; 483 | case '<': 484 | return { $lt: cmd.value }; 485 | case '<=': 486 | return { $lte: cmd.value }; 487 | } 488 | } 489 | 490 | function WHERE(builder, scalar) { // , group 491 | 492 | var condition = {}; 493 | var sort = null; 494 | var tmp = null; 495 | var filter; 496 | var value; 497 | 498 | for (var i = 0; i < builder.$commands.length; i++) { 499 | var cmd = builder.$commands[i]; 500 | 501 | switch (cmd.type) { 502 | case 'where': 503 | value = cmd.compare === '=' ? cmd.value : eqgtlt(cmd); 504 | if (tmp) { 505 | filter = {}; 506 | filter[cmd.name] = value; 507 | tmp.push(filter); 508 | } else 509 | condition[cmd.name] = value; 510 | break; 511 | case 'in': 512 | if (typeof(cmd.value) === 'function') 513 | cmd.value = cmd.value(); 514 | value = cmd.value instanceof Array ? { $in: cmd.value } : cmd.value; 515 | if (tmp) { 516 | filter = {}; 517 | filter[cmd.name] = value; 518 | tmp.push(filter); 519 | } else 520 | condition[cmd.name] = value; 521 | break; 522 | case 'notin': 523 | if (typeof(cmd.value) === 'function') 524 | cmd.value = cmd.value(); 525 | value = cmd.value instanceof Array ? { $nin: cmd.value } : { $ne: cmd.value }; 526 | if (tmp) { 527 | filter = {}; 528 | filter[cmd.name] = value; 529 | tmp.push(filter); 530 | } else 531 | condition[cmd.name] = value; 532 | break; 533 | case 'between': 534 | value = { $gte: cmd.a, $lte: cmd.b }; 535 | if (tmp) { 536 | filter = {}; 537 | filter[cmd.name] = value; 538 | tmp.push(filter); 539 | } else 540 | condition[cmd.name] = value; 541 | break; 542 | case 'search': 543 | value = (cmd.compare === '*' ? '' : cmd.compare === 'beg' ? '^' : '') + cmd.value + (cmd.compare === 'end' ? '$' : ''); 544 | var v = ['[aáä]', '[eéë]', '[iíï]', '[oóö]', '[uúü]']; 545 | for (let m of v) 546 | value = value.replace(new RegExp(m, 'gi'), m); 547 | value = { $regex: value, $options: 'i' }; 548 | if (tmp) { 549 | filter = {}; 550 | filter[cmd.name] = value; 551 | tmp.push(filter); 552 | } else 553 | condition[cmd.name] = value; 554 | break; 555 | case 'fulltext': 556 | value = new RegExp((cmd.compare === '*' ? '' : cmd.compare === 'beg' ? '^' : '') + cmd.value + (cmd.compare === 'end' ? '$' : ''), 'i'); 557 | if (tmp) { 558 | filter = {}; 559 | filter[cmd.name] = value; 560 | tmp.push(filter); 561 | } else 562 | condition[cmd.name] = value; 563 | break; 564 | case 'contains': 565 | value = { $exists: true, $nin: [null, ''] }; 566 | if (tmp) { 567 | filter = {}; 568 | filter[cmd.name] = value; 569 | tmp.push(filter); 570 | } else 571 | condition[cmd.name] = value; 572 | break; 573 | case 'empty': 574 | value = { $exists: false, $in: [null, ''] }; 575 | if (tmp) { 576 | filter = {}; 577 | filter[cmd.name] = value; 578 | tmp.push(filter); 579 | } else 580 | condition[cmd.name] = value; 581 | break; 582 | case 'month': 583 | case 'year': 584 | case 'day': 585 | case 'hour': 586 | case 'minute': 587 | new Error('Not implemented', 'MongoDB doesn\'t support date function for quering.'); 588 | break; 589 | case 'query': 590 | if (tmp) { 591 | filter = {}; 592 | tmp.push(cmd.query); 593 | } else { 594 | var arr = Object.keys(cmd.query); 595 | for (var j = 0; j < arr.length; j++) 596 | condition[arr[j]] = cmd.query[arr[j]]; 597 | } 598 | break; 599 | case 'or': 600 | tmp = []; 601 | continue; 602 | case 'end': 603 | condition.$or = tmp; 604 | tmp = null; 605 | break; 606 | case 'and': 607 | break; 608 | case 'sort': 609 | !sort && (sort = {}); 610 | sort[cmd.name] = cmd.desc ? -1 : 1; 611 | break; 612 | case 'regexp': 613 | if (tmp) { 614 | filter = {}; 615 | filter[cmd.name] = cmd.value; 616 | tmp.push(filter); 617 | } else 618 | condition[cmd.name] = cmd.value; 619 | break; 620 | } 621 | } 622 | 623 | if (scalar) 624 | return { $match: condition }; 625 | else 626 | return { where: condition, sort: sort }; 627 | } 628 | 629 | function FIELDS(builder) { 630 | var output = null; 631 | var fields = builder.options.fields; 632 | if (fields && fields.length) { 633 | output = {}; 634 | for (var i = 0; i < fields.length; i++) { 635 | var name = fields[i]; 636 | var is = name[0] === '-'; 637 | if (is) 638 | name = name.substring(1); 639 | output[name] = is ? 0 : 1; 640 | } 641 | 642 | if (builder.$joinmeta) 643 | output[builder.$joinmeta.a] = 1; 644 | } 645 | return output; 646 | } 647 | -------------------------------------------------------------------------------- /mysql.js: -------------------------------------------------------------------------------- 1 | const Database = require('mysql2'); 2 | const REG_PARAMS = /\$\d/g; 3 | const EMPTYARRAY = []; 4 | const BLACKLIST = { dbms: 1 }; 5 | const ISOP = { '+': 1, '-': 1, '*': 1, '/': 1, '=': 1, '!': 1, '#': 1 }; 6 | const CANSTATS = global.F ? (global.F.stats && global.F.stats.performance && global.F.stats.performance.dbrm != null) : false; 7 | const CACHEOPT = {}; 8 | const POOLS = {}; 9 | 10 | var ESCAPE = global.MYSQL_ESCAPE = function(value) { 11 | 12 | if (value == null) 13 | return 'null'; 14 | 15 | var type = typeof(value); 16 | 17 | if (type === 'function') { 18 | value = value(); 19 | if (value == null) 20 | return 'null'; 21 | type = typeof(value); 22 | } 23 | 24 | if (type === 'boolean') 25 | return value === true ? 'true' : 'false'; 26 | 27 | if (type === 'number') 28 | return value.toString(); 29 | 30 | if (type === 'string') 31 | return Database.escape(value); 32 | 33 | if (value instanceof Array) 34 | return Database.escape(value.join(',')); 35 | 36 | if (value instanceof Date) 37 | return Database.escape(dateToString(value)); 38 | 39 | return Database.escape(value.toString()); 40 | }; 41 | 42 | function select(client, cmd) { 43 | 44 | var builder = cmd.builder; 45 | var opt = builder.options; 46 | var params = []; 47 | var q = 'SELECT ' + FIELDS(builder) + ' FROM ' + opt.table + WHERE(builder, null, null, params); 48 | 49 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 50 | builder.db.$debug && builder.db.$debug(q); 51 | 52 | if (CANSTATS) 53 | F.stats.performance.dbrm++; 54 | 55 | client.query(q, params, function(err, response) { 56 | 57 | builder.db.busy = false; 58 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 59 | 60 | var rows = response ? response : EMPTYARRAY; 61 | if (opt.first) 62 | rows = rows.length ? rows[0] : null; 63 | 64 | // checks joins 65 | if (!err && builder.$joins) { 66 | client.$dbms._joins(rows, builder); 67 | setImmediate(builder.db.$next); 68 | } else 69 | builder.$callback(err, rows); 70 | 71 | }); 72 | } 73 | 74 | function check(client, cmd) { 75 | 76 | var builder = cmd.builder; 77 | var opt = builder.options; 78 | var params = []; 79 | var q = 'SELECT 1 FROM ' + opt.table + WHERE(builder, null, null, params); 80 | 81 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 82 | 83 | if (CANSTATS) 84 | F.stats.performance.dbrm++; 85 | 86 | builder.db.$debug && builder.db.$debug(q); 87 | client.query(q, params, function(err, response) { 88 | builder.db.busy = false; 89 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 90 | var is = response ? response[0] != null : false; 91 | builder.$callback(err, is); 92 | }); 93 | } 94 | 95 | function query(client, cmd) { 96 | var builder = cmd.builder; 97 | var opt = builder.options; 98 | 99 | if (!cmd.value && builder.options.params) 100 | cmd.value = []; 101 | 102 | var q = cmd.query + WHERE(builder, null, null, cmd.value); 103 | builder.db.$debug && builder.db.$debug(q); 104 | 105 | if (CANSTATS) 106 | F.stats.performance.dbrm++; 107 | 108 | F.$events.dbms && EMIT('dbms', 'query', cmd.query, opt.db, builder); 109 | client.query(q, cmd.value, function(err, response) { 110 | builder.db.busy = false; 111 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 112 | var rows = response || EMPTYARRAY; 113 | if (opt.first) 114 | rows = rows.length ? rows[0] : null; 115 | builder.$callback(err, rows); 116 | }); 117 | } 118 | 119 | function command(client, sql, cmd) { 120 | cmd.db.$debug && cmd.db.$debug(sql); 121 | F.$events.dbms && EMIT('dbms', 'query', sql, cmd.db); 122 | 123 | if (CANSTATS) 124 | F.stats.performance.dbrm++; 125 | 126 | client.query(sql, function(err) { 127 | cmd.db.busy = false; 128 | err && client.$opt.onerror && client.$opt.onerror(err, sql); 129 | cmd.db.$next(err); 130 | }); 131 | } 132 | 133 | function list(client, cmd) { 134 | 135 | var builder = cmd.builder; 136 | var opt = builder.options; 137 | var params = []; 138 | var query = WHERE(builder, true, null, params); 139 | var q; 140 | 141 | if (cmd.improved && builder.skip) { 142 | 143 | q = 'SELECT ' + FIELDS(builder) + ' FROM ' + opt.table + query + OFFSET(builder); 144 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 145 | builder.db.$debug && builder.db.$debug(q); 146 | builder.db.busy = true; 147 | 148 | if (CANSTATS) 149 | F.stats.performance.dbrm++; 150 | 151 | client.query(q, params, function(err, response) { 152 | builder.db.busy = false; 153 | var rows = response || []; 154 | if (!err && builder.$joins) { 155 | client.$dbms._joins(rows, builder, rows.length); 156 | setImmediate(builder.db.$next); 157 | } else 158 | builder.$callback(err, rows, rows.length); 159 | }); 160 | 161 | } else { 162 | q = 'SELECT COUNT(1) as dbmsvalue FROM ' + opt.table + query; 163 | builder.db.$debug && builder.db.$debug(q); 164 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 165 | 166 | if (CANSTATS) 167 | F.stats.performance.dbrm++; 168 | 169 | client.query(q, params, function(err, response) { 170 | 171 | builder.db.busy = false; 172 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 173 | 174 | var count = err ? 0 : response ? response[0].dbmsvalue : 0; 175 | 176 | var fn = function(err, response) { 177 | builder.db.busy = false; 178 | 179 | var rows = response || []; 180 | 181 | // checks joins 182 | // client.$dbms._joins(rows, builder); 183 | if (!err && builder.$joins) { 184 | client.$dbms._joins(rows, builder, count); 185 | setImmediate(builder.db.$next); 186 | } else 187 | builder.$callback(err, rows, count); 188 | }; 189 | 190 | if (count) { 191 | builder.db.busy = true; 192 | q = 'SELECT ' + FIELDS(builder) + ' FROM ' + opt.table + query + OFFSET(builder); 193 | builder.db.$debug && builder.db.$debug(q); 194 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 195 | 196 | if (CANSTATS) 197 | F.stats.performance.dbrm++; 198 | 199 | client.query(q, params, fn); 200 | } else 201 | fn(err, null); 202 | }); 203 | } 204 | } 205 | 206 | function scalar(client, cmd) { 207 | 208 | var builder = cmd.builder; 209 | var opt = builder.options; 210 | var params = []; 211 | var q; 212 | 213 | switch (cmd.scalar) { 214 | case 'avg': 215 | case 'min': 216 | case 'sum': 217 | case 'max': 218 | case 'count': 219 | q = 'SELECT ' + cmd.scalar.toUpperCase() + (cmd.scalar !== 'count' ? ('(' + (cmd.field || cmd.name) + ')') : '(1)') + ' as dbmsvalue FROM ' + opt.table; 220 | break; 221 | case 'group': 222 | q = 'SELECT ' + cmd.name + ', ' + (cmd.field ? ('SUM(' + cmd.field + ')') : 'COUNT(1)') + ' as count FROM ' + opt.table; 223 | break; 224 | } 225 | 226 | q = q + WHERE(builder, false, cmd.scalar === 'group' ? cmd.name : null, params); 227 | builder.db.$debug && builder.db.$debug(q); 228 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 229 | 230 | if (CANSTATS) 231 | F.stats.performance.dbrm++; 232 | 233 | client.query(q, params, function(err, response) { 234 | builder.db.busy = false; 235 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 236 | 237 | var rows = response || EMPTYARRAY; 238 | 239 | if (!cmd.field && cmd.scalar !== 'group') 240 | rows = rows.length ? (rows[0].dbmsvalue || 0) : 0; 241 | 242 | builder.$callback(err, rows); 243 | }); 244 | } 245 | 246 | function abortcommands(client, builder) { 247 | while (builder.db.$commands.length) { 248 | var c = builder.db.$commands.shift(); 249 | if (c && c.type === 'commit') { 250 | c.type = 'rollback'; 251 | builder.db.$commands.unshift(c); 252 | break; 253 | } 254 | } 255 | } 256 | 257 | function insert(client, cmd) { 258 | 259 | var builder = cmd.builder; 260 | 261 | cmd.builder.options.transform && cmd.builder.options.transform(cmd.builder.value, cmd.builder.db.$output, cmd.builder.db.$lastoutput); 262 | 263 | var keys = Object.keys(builder.value); 264 | var params = []; 265 | var fields = []; 266 | var values = []; 267 | var opt = builder.options; 268 | 269 | for (var i = 0; i < keys.length; i++) { 270 | var key = keys[i]; 271 | var val = builder.value[key]; 272 | if (val === undefined || BLACKLIST[key]) 273 | continue; 274 | 275 | if (builder.options.fields && builder.options.fields.length) { 276 | var skip = true; 277 | for (var j = 0; j < builder.options.fields.length; j++) { 278 | var field = builder.options.fields[j]; 279 | if (field[0] === '-') { 280 | field = field.substring(1); 281 | if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 282 | skip = true; 283 | break; 284 | } 285 | skip = false; 286 | } else if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 287 | skip = false; 288 | break; 289 | } else 290 | skip = false; 291 | } 292 | 293 | if (skip) 294 | continue; 295 | } 296 | 297 | var raw = false; 298 | 299 | switch (key[0]) { 300 | case '-': 301 | case '+': 302 | case '*': 303 | case '/': 304 | case '>': 305 | case '<': 306 | key = key.substring(1); 307 | break; 308 | case '=': 309 | key = key.substring(1); 310 | raw = true; 311 | break; 312 | case '#': 313 | continue; 314 | case '!': 315 | // toggle 316 | key = key.substring(1); 317 | if (val) 318 | val = true; 319 | else 320 | val = false; 321 | break; 322 | } 323 | 324 | fields.push('`' + key + '`'); 325 | 326 | if (raw) { 327 | values.push(val); 328 | } else { 329 | values.push('?'); 330 | params.push(val == null ? null : typeof(val) === 'function' ? val(builder.value) : val); 331 | } 332 | } 333 | 334 | var q = 'INSERT INTO ' + opt.table + ' (' + fields.join(',') + ') VALUES(' + values.join(',') + ')';// + (builder.$primarykey ? (' RETURNING ' + builder.$primarykey) : ''); 335 | 336 | builder.db.$debug && builder.db.$debug(q); 337 | F.$events.dbms && EMIT('dbms', 'insert', opt.table, opt.db, builder); 338 | 339 | if (CANSTATS) 340 | F.stats.performance.dbwm++; 341 | 342 | client.query(q, params, function(err, response) { 343 | 344 | // Transaction is aborted 345 | if (err && client.$dbmstransaction) { 346 | client.$dbmstransaction = true; 347 | abortcommands(client, builder); 348 | } 349 | 350 | builder.db.busy = false; 351 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 352 | builder.$callback(err, err == null ? (response && response.length ? response[0][builder.$primarykey] : 1) : 0); 353 | }); 354 | } 355 | 356 | function insertexists(client, cmd) { 357 | var builder = cmd.builder; 358 | var opt = builder.options; 359 | var q = 'SELECT 1 as dbmsvalue FROM ' + opt.table + WHERE(builder); 360 | builder.db.$debug && builder.db.$debug(q); 361 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 362 | 363 | if (CANSTATS) 364 | F.stats.performance.dbrm++; 365 | 366 | client.query(q, function(err, response) { 367 | builder.db.busy = false; 368 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 369 | var rows = response || EMPTYARRAY; 370 | if (rows.length) 371 | builder.$callback(err, 0); 372 | else 373 | insert(client, cmd); 374 | }); 375 | } 376 | 377 | function modify(client, cmd) { 378 | 379 | cmd.builder.options.transform && cmd.builder.options.transform(cmd.builder.value, cmd.builder.db.$output, cmd.builder.db.$lastoutput); 380 | 381 | var keys = Object.keys(cmd.builder.value); 382 | var fields = []; 383 | var params = []; 384 | 385 | for (var i = 0; i < keys.length; i++) { 386 | var key = keys[i]; 387 | var val = cmd.builder.value[key]; 388 | 389 | if (val === undefined || BLACKLIST[key]) 390 | continue; 391 | 392 | if (cmd.builder.options.equal && cmd.builder.options.equal.indexOf(key) !== -1) 393 | continue; 394 | 395 | if (cmd.builder.options.fields && cmd.builder.options.fields.length) { 396 | var skip = true; 397 | for (var j = 0; j < cmd.builder.options.fields.length; j++) { 398 | var field = cmd.builder.options.fields[j]; 399 | if (field[0] === '-') { 400 | field = field.substring(1); 401 | if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 402 | skip = true; 403 | break; 404 | } 405 | skip = false; 406 | } else if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 407 | skip = false; 408 | break; 409 | } else 410 | skip = false; 411 | } 412 | 413 | if (skip) 414 | continue; 415 | } 416 | 417 | var c = key[0]; 418 | var type; 419 | 420 | if (typeof(val) === 'function') 421 | val = val(cmd.builder.value); 422 | 423 | switch (c) { 424 | case '-': 425 | case '+': 426 | case '*': 427 | case '/': 428 | params.push(val ? val : 0); 429 | key = key.substring(1); 430 | type = '`' + key + '`=COALESCE(' + key + ',0)' + c + '?'; 431 | break; 432 | case '>': 433 | case '<': 434 | params.push(val ? val : 0); 435 | key = key.substring(1); 436 | type = '`' + key + '`=' + (c === '>' ? 'GREATEST' : 'LEAST') + '(COALESCE(' + key + ',0),?)'; 437 | break; 438 | case '!': 439 | // toggle 440 | key = key.substring(1); 441 | type = '`' + key + '`=NOT ' + key; 442 | break; 443 | case '=': 444 | case '#': 445 | // raw 446 | type = '`' + key.substring(1) + '`=' + val; 447 | break; 448 | default: 449 | params.push(val); 450 | type = '`' + key + '`=?'; 451 | break; 452 | } 453 | type && fields.push(type); 454 | } 455 | 456 | var builder = cmd.builder; 457 | var opt = builder.options; 458 | 459 | if (opt.equal) { 460 | for (var i = 0; i < opt.equal.length; i++) 461 | cmd.builder.where(opt.equal[i], builder.value[opt.equal[i]]); 462 | } 463 | 464 | var q = 'UPDATE ' + opt.table + ' SET ' + fields + WHERE(builder, true, null, params); 465 | builder.db.$debug && builder.db.$debug(q); 466 | F.$events.dbms && EMIT('dbms', 'update', opt.table, opt.db, cmd.builder); 467 | 468 | if (CANSTATS) 469 | F.stats.performance.dbwm++; 470 | 471 | client.query(q, params, function(err, response) { 472 | 473 | // Transaction is aborted 474 | if (err && client.$dbmstransaction) { 475 | client.$dbmstransaction = true; 476 | abortcommands(client, builder); 477 | } 478 | 479 | builder.db.busy = false; 480 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 481 | var rows = response || EMPTYOBJECT; 482 | rows = rows.affectedRows || 0; 483 | if (!rows && cmd.insert) { 484 | if (cmd.insert !== true) 485 | cmd.builder.value = cmd.insert; 486 | cmd.builder.options.insert && cmd.builder.options.insert(cmd.builder.value, cmd.builder.options.insertparams); 487 | insert(client, cmd); 488 | } else 489 | builder.$callback(err, rows); 490 | }); 491 | } 492 | 493 | function remove(client, cmd) { 494 | var builder = cmd.builder; 495 | var opt = builder.options; 496 | var params = []; 497 | var q = 'DELETE FROM ' + opt.table + WHERE(builder, true, null, params); 498 | builder.db.$debug && builder.db.$debug(q); 499 | F.$events.dbms && EMIT('dbms', 'delete', opt.table, opt.db, builder); 500 | 501 | if (CANSTATS) 502 | F.stats.performance.dbwm++; 503 | 504 | client.query(q, params, function(err, response) { 505 | 506 | // Transaction is aborted 507 | if (err && client.$dbmstransaction) { 508 | client.$dbmstransaction = true; 509 | abortcommands(client, builder); 510 | } 511 | 512 | builder.db.busy = false; 513 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 514 | var rows = response || EMPTYOBJECT; 515 | rows = rows.affectedRows || 0; 516 | builder.$callback(err, rows); 517 | }); 518 | } 519 | 520 | function destroy(conn) { 521 | if (conn) { 522 | if (conn.$pool) { 523 | if (conn.$dbmstransaction) 524 | conn.$dbmstransaction = false; 525 | conn.$pool && conn.$pool.releaseConnection(conn); 526 | conn.$pool = null; 527 | } else 528 | conn.destroy(); 529 | } 530 | } 531 | 532 | function clientcommand(cmd, client, self) { 533 | switch (cmd.type) { 534 | case 'transaction': 535 | client.$dbmstransaction = true; 536 | command(client, 'BEGIN', cmd); 537 | break; 538 | case 'end': 539 | cmd.type = self.$eb ? self.$errors.items.length ? 'ROLLBACK' : self.$errors.length ? 'ROLLBACK' : 'COMMIT' : 'COMMIT'; 540 | command(client, cmd.type, cmd); 541 | break; 542 | case 'commit': 543 | case 'rollback': 544 | client.$dbmstransaction = false; 545 | command(client, cmd.type.toUpperCase(), cmd); 546 | break; 547 | case 'find': 548 | case 'read': 549 | select(client, cmd); 550 | break; 551 | case 'diff': 552 | var cb = cmd.builder.$callback; 553 | cmd.builder.$callback = function(err, response) { 554 | cb.call(cmd.builder, err, err ? EMPTYOBJECT : DIFFARR(cmd.key, response, cmd.form)); 555 | }; 556 | select(client, cmd); 557 | break; 558 | 559 | case 'modify2': 560 | var cb = cmd.builder.$callback; 561 | cmd.builder.$callback = function(err, response) { 562 | cmd.builder.options.fields = null; 563 | if (err) { 564 | cb.call(cmd.builder, err, 0); 565 | } else if (response) { 566 | cmd.builder.db.busy = true; 567 | var mod = cmd.fn(response, cmd.builder.db.$output, cmd.builder.db.$outputall); 568 | cmd.builder.db.busy = false; 569 | if (mod) { 570 | cmd.builder.value = mod; 571 | cmd.builder.$callback = cb; 572 | if (cmd.builder.value.$clean) 573 | cmd.builder.value = cmd.builder.value.$clean(); 574 | modify(client, cmd); 575 | } else 576 | cb.call(cmd.builder, err, 0); 577 | } else { 578 | if (cmd.insert) { 579 | cmd.builder.db.busy = true; 580 | mod = cmd.fn(null, cmd.builder.db.$output, cmd.builder.db.$outputall); 581 | cmd.builder.db.busy = false; 582 | if (mod) { 583 | cmd.builder.value = mod; 584 | cmd.builder.$callback = cb; 585 | insert(client, cmd); 586 | } else 587 | cb.call(cmd.builder, err, 0); 588 | } else { 589 | cb.call(cmd.builder, err, 0); 590 | } 591 | } 592 | }; 593 | select(client, cmd); 594 | break; 595 | case 'check': 596 | check(client, cmd); 597 | break; 598 | case 'list': 599 | list(client, cmd); 600 | break; 601 | case 'scalar': 602 | scalar(client, cmd); 603 | break; 604 | case 'insert': 605 | if (cmd.unique) 606 | insertexists(client, cmd); 607 | else 608 | insert(client, cmd); 609 | break; 610 | case 'update': 611 | case 'modify': 612 | modify(client, cmd); 613 | break; 614 | case 'remove': 615 | remove(client, cmd); 616 | break; 617 | case 'query': 618 | query(client, cmd); 619 | break; 620 | default: 621 | cmd.builder.$callback(new Error('Operation "' + cmd.type + '" not found')); 622 | break; 623 | } 624 | } 625 | 626 | exports.run = function(opt, self, cmd, repeated) { 627 | 628 | var conn = self.$conn[opt.id]; 629 | if (conn) { 630 | clientcommand(cmd, conn, self); 631 | return; 632 | } 633 | 634 | if (opt.options.pooling) { 635 | conn = POOLS[opt.id]; 636 | if (!conn) { 637 | var optconn = CLONE(opt.options); 638 | if (optconn.max) { 639 | optconn.connectionLimit = optconn.max; 640 | delete optconn.max; 641 | } 642 | delete optconn.min; 643 | delete optconn.pooling; 644 | conn = POOLS[opt.id] = Database.createPool(optconn); 645 | } 646 | } else { 647 | 648 | var optconn = CACHEOPT[opt.id]; 649 | if (!optconn) { 650 | optconn = CACHEOPT[opt.id] = CLONE(opt.options); 651 | if (optconn.max) { 652 | optconn.connectionLimit = optconn.max; 653 | delete optconn.max; 654 | } 655 | delete optconn.min; 656 | delete optconn.pooling; 657 | } 658 | 659 | conn = self.$conn[opt.id] = Database.createConnection(optconn); 660 | conn.$dbms = self; 661 | conn.$$destroy = destroy; 662 | } 663 | 664 | self.$op = null; 665 | self.busy = true; 666 | 667 | if (opt.options.pooling) { 668 | conn.getConnection(function(err, connection) { 669 | 670 | if (err) { 671 | self.busy = false; 672 | opt.onerror && opt.onerror(err); 673 | 674 | if ((!repeated || repeated < 3) && err.toString().indexOf('Too many connections') !== -1) { 675 | // try again 676 | setTimeout(exports.run, 200, opt, self, cmd, (repeated || 0) + 1); 677 | return; 678 | } 679 | 680 | if (cmd.builder) 681 | cmd.builder.$callback(err); 682 | else 683 | cmd.db.$next(err); 684 | 685 | } else { 686 | self.$conn[opt.id] = connection; 687 | connection.$dbms = self; 688 | connection.$opt = opt; 689 | connection.$pool = conn; 690 | connection.$$destroy = destroy; 691 | clientcommand(cmd, connection, self); 692 | } 693 | }); 694 | } else 695 | clientcommand(cmd, conn, self); 696 | 697 | }; 698 | 699 | function prepare_owner(cmd, condition) { 700 | 701 | var tmp = []; 702 | 703 | if (cmd.member) { 704 | for (var i = 0; i < cmd.member.length; i++) 705 | tmp.push(ESCAPE(cmd.member[i])); 706 | } 707 | 708 | var addcondition = []; 709 | if (cmd.condition) { 710 | addcondition.push(''); 711 | if (typeof(cmd.condition) === 'string') { 712 | addcondition.push(val); 713 | } else { 714 | for (var key in cmd.condition) { 715 | var val = cmd.condition[key]; 716 | addcondition.push(key + (val == null ? ' IS ' : '=') + ESCAPE(val)); 717 | } 718 | } 719 | } 720 | 721 | // e.g. userid=ID OR (userid IN (ARR) (AND condition)) 722 | condition.push('(' + cmd.name + '=' + ESCAPE(cmd.value) + (tmp.length ? (' OR (' + cmd.name + ' IN (' + tmp.join(',') + ')' + (addcondition.length ? addcondition.join(' AND ') : '') + ')') : '') + ')'); 723 | } 724 | 725 | function WHERE(builder, scalar, group, params) { 726 | 727 | var condition = []; 728 | var sort = []; 729 | var tmp; 730 | var op = 'AND'; 731 | var opuse = false; 732 | var current; // temporary for "query" + "cmd.value" and "replace" method because of performance 733 | 734 | var replace = builder.options.params ? function(text) { 735 | var indexer = (+text.substring(1)) - 1; 736 | params.push(current[indexer]); 737 | return '?'; 738 | } : null; 739 | 740 | for (var i = 0; i < builder.$commands.length; i++) { 741 | var cmd = builder.$commands[i]; 742 | 743 | if (builder.options.islanguage && cmd.name && cmd.name[cmd.name.length - 1] === '§') 744 | cmd.name = cmd.name.substring(0, cmd.name.length - 1) + (builder.options.language || ''); 745 | 746 | switch (cmd.type) { 747 | case 'where': 748 | if (cmd.value === undefined) { 749 | opuse && condition.length && condition.push(op); 750 | condition.push(cmd.name); 751 | } else { 752 | tmp = ESCAPE(cmd.value); 753 | opuse && condition.length && condition.push(op); 754 | condition.push(cmd.name + ((cmd.value == null || tmp == 'null') && cmd.compare === '=' ? ' IS ' : cmd.compare) + tmp); 755 | } 756 | break; 757 | 758 | case 'owner': 759 | opuse && condition.length && condition.push(op); 760 | prepare_owner(cmd, condition); 761 | break; 762 | 763 | case 'custom': 764 | cmd.fn.call(builder, builder, builder.db.$output, builder.db.$lastoutput); 765 | break; 766 | 767 | case 'in': 768 | if (typeof(cmd.value) === 'function') 769 | cmd.value = cmd.value(); 770 | if (cmd.value instanceof Array) { 771 | tmp = []; 772 | for (var j = 0; j < cmd.value.length; j++) { 773 | var val = cmd.value[j]; 774 | if (val && cmd.field) 775 | val = val[cmd.field]; 776 | tmp.push(ESCAPE(val)); 777 | } 778 | opuse && condition.length && condition.push(op); 779 | condition.push(cmd.name + ' IN (' + (tmp.length ? tmp.join(',') : 'NULL') + ')'); 780 | } else { 781 | opuse && condition.length && condition.push(op); 782 | condition.push(cmd.name + '=' + ESCAPE(cmd.field ? cmd.value[cmd.field] : cmd.value)); 783 | } 784 | break; 785 | 786 | case 'notin': 787 | if (typeof(cmd.value) === 'function') 788 | cmd.value = cmd.value(); 789 | if (cmd.value instanceof Array) { 790 | tmp = []; 791 | for (var j = 0; j < cmd.value.length; j++) { 792 | var val = cmd.value[j]; 793 | if (val && cmd.field) 794 | val = val[cmd.field]; 795 | tmp.push(ESCAPE(val)); 796 | } 797 | opuse && condition.length && condition.push(op); 798 | condition.push(cmd.name + ' NOT IN (' + (tmp.length ? tmp.join(',') : 'NULL') + ')'); 799 | } else { 800 | opuse && condition.length && condition.push(op); 801 | condition.push(cmd.name + '<>' + ESCAPE(cmd.field ? cmd.value[cmd.field] : cmd.value)); 802 | } 803 | break; 804 | 805 | case 'between': 806 | opuse && condition.length && condition.push(op); 807 | condition.push('(' + cmd.name + '>=' + ESCAPE(cmd.a) + ' AND ' + cmd.name + '<=' + ESCAPE(cmd.b) + ')'); 808 | break; 809 | 810 | case 'search': 811 | tmp = ESCAPE((!cmd.compare || cmd.compare === '*' ? ('%' + cmd.value + '%') : (cmd.compare === 'beg' ? ('%' + cmd.value) : (cmd.value + '%')))); 812 | opuse && condition.length && condition.push(op); 813 | condition.push(cmd.name + ' LIKE ' + tmp); 814 | break; 815 | 816 | case 'searchfull': 817 | // tmp = ESCAPE('%' + cmd.value.toLowerCase().replace(/y/g, 'i') + '%'); 818 | // opuse && condition.length && condition.push(op); 819 | // condition.push('REPLACE(LOWER(to_tsvector(' + builder.options.table + '::text)::text), \'y\', \'i\') LIKE ' + tmp); 820 | break; 821 | 822 | case 'searchall': 823 | tmp = ''; 824 | for (var j = 0; j < cmd.value.length; j++) 825 | tmp += (tmp ? ' AND ' : '') + cmd.name + ' LIKE ' + ESCAPE('%' + cmd.value[j] + '%'); 826 | opuse && condition.length && condition.push(op); 827 | condition.push('(' + (tmp || '0=1') + ')'); 828 | break; 829 | 830 | case 'fulltext': 831 | tmp = ESCAPE('%' + cmd.value.toLowerCase() + '%'); 832 | opuse && condition.length && condition.push(op); 833 | condition.push('LOWER(' + cmd.name + ') LIKE ' + tmp); 834 | break; 835 | case 'contains': 836 | opuse && condition.length && condition.push(op); 837 | condition.push('LENGTH(' + cmd.name +')>0'); 838 | break; 839 | case 'query': 840 | opuse && condition.length && condition.push(op); 841 | if (cmd.value) 842 | current = cmd.value; 843 | condition.push('(' + (current == undefined ? cmd.query : cmd.query.replace(REG_PARAMS, replace)) + ')'); 844 | break; 845 | case 'permit': 846 | opuse && condition.length && condition.push(op); 847 | if (cmd.must) 848 | condition.push('(' + ((cmd.useridfield ? ('`' + cmd.useridfield + '`=' + Database.escape(cmd.userid) + ' OR ') : '') + '`' + cmd.name + '` && $' + params.push([cmd.value])) + ')'); 849 | else 850 | condition.push('(' + ((cmd.useridfield ? ('`' + cmd.useridfield + '`=' + Database.escape(cmd.userid) + ' OR ') : '') + 'array_length(`' + cmd.name + '`,1) IS NULL OR `' + cmd.name + '` && $' + params.push([cmd.value])) + ')'); 851 | break; 852 | case 'empty': 853 | opuse && condition.length && condition.push(op); 854 | condition.push('(' + cmd.name + ' IS NULL OR LENGTH(' + cmd.name + ')=0)'); 855 | break; 856 | case 'month': 857 | case 'year': 858 | case 'day': 859 | case 'hour': 860 | case 'minute': 861 | opuse && condition.length && condition.push(op); 862 | condition.push(cmd.type + '(`' + cmd.name + '`)' + cmd.compare + ESCAPE(cmd.value)); 863 | break; 864 | case 'date': 865 | opuse && condition.length && condition.push(op); 866 | condition.push('CONVERT(`' + cmd.name + '`,date)' + cmd.compare + (cmd.value instanceof Date ? ('CONVERT(\'' + cmd.value.format('yyyy-MM-dd') + '\',date)') : 'null')); 867 | break; 868 | case 'or': 869 | opuse && condition.length && condition.push(op); 870 | op = 'OR'; 871 | opuse = false; 872 | condition.push('('); 873 | continue; 874 | case 'end': 875 | condition.push(')'); 876 | op = 'AND'; 877 | break; 878 | case 'and': 879 | opuse && condition.length && condition.push(op); 880 | op = 'AND'; 881 | break; 882 | case 'sort': 883 | sort.push(cmd.name + ' ' + (cmd.desc ? 'DESC' : 'ASC')); 884 | break; 885 | case 'regexp': 886 | tmp = cmd.value.toString().substring(1); 887 | var g = '~'; 888 | if (tmp[tmp.length - 1] === 'i') { 889 | tmp = tmp.substring(0, tmp.length - 2); 890 | g = '~*'; 891 | } else 892 | tmp = tmp.substring(0, tmp.length - 1); 893 | opuse && condition.length && condition.push(op); 894 | condition.push(cmd.name + g + '\'' + tmp + '\''); 895 | break; 896 | } 897 | opuse = true; 898 | } 899 | 900 | var query = (condition.length ? (' WHERE ' + condition.join(' ')) : '') + (group ? (' GROUP BY ' + group) : ''); 901 | 902 | if (scalar) { 903 | builder.options.sort = sort; 904 | } else { 905 | if (sort.length) 906 | query += ' ORDER BY ' + sort.join(','); 907 | if (builder.options.skip && builder.options.take) 908 | query += ' LIMIT ' + builder.options.take + ' OFFSET ' + builder.options.skip; 909 | else if (builder.options.take) 910 | query += ' LIMIT ' + builder.options.take; 911 | else if (builder.options.skip) 912 | query += ' OFFSET ' + builder.options.skip; 913 | } 914 | 915 | return query; 916 | } 917 | 918 | function OFFSET(builder) { 919 | var query = ''; 920 | var sort = builder.options.sort || EMPTYARRAY; 921 | if (sort.length) 922 | query += ' ORDER BY ' + sort.join(','); 923 | if (builder.options.skip && builder.options.take) 924 | query += ' LIMIT ' + builder.options.take + ' OFFSET ' + builder.options.skip; 925 | else if (builder.options.take) 926 | query += ' LIMIT ' + builder.options.take; 927 | else if (builder.options.skip) 928 | query += ' OFFSET ' + builder.options.skip; 929 | return query; 930 | } 931 | 932 | function FIELDS(builder) { 933 | 934 | var output = ''; 935 | var plus = ''; 936 | var fields = builder.options.fields; 937 | 938 | if (fields && fields.length) { 939 | for (var i = 0; i < fields.length; i++) { 940 | var field = fields[i]; 941 | if (field[0] === '-') { 942 | 943 | if (builder.options.fieldsrem) 944 | builder.options.fieldsrem.push(field.substring(1)); 945 | else 946 | builder.options.fieldsrem = [field.substring(1)]; 947 | 948 | continue; 949 | } 950 | 951 | if (builder.options.islanguage) { 952 | if (field[field.length - 1] === '§') { 953 | field = field.substring(0, field.length - 1); 954 | field = (field + builder.options.language) + ' AS ' + field; 955 | } 956 | } 957 | 958 | output += (output ? ',' : '') + field; 959 | } 960 | if (output && builder.$joinmeta) 961 | output += ',' + builder.$joinmeta.a; 962 | } 963 | 964 | fields = builder.options.subquery; 965 | if (fields && fields.length) { 966 | for (var i = 0; i < fields.length; i++) 967 | plus += ',' + (fields[i].name ? ('(' + fields[i].query + ') AS ' + fields[i].name) : fields[i].query); 968 | } 969 | 970 | return (output ? output : '*') + plus; 971 | } 972 | 973 | function dateToString(dt) { 974 | 975 | var arr = []; 976 | 977 | arr.push(dt.getFullYear().toString()); 978 | arr.push((dt.getMonth() + 1).toString()); 979 | arr.push(dt.getDate().toString()); 980 | arr.push(dt.getHours().toString()); 981 | arr.push(dt.getMinutes().toString()); 982 | arr.push(dt.getSeconds().toString()); 983 | 984 | for (var i = 1, length = arr.length; i < length; i++) { 985 | if (arr[i].length === 1) 986 | arr[i] = '0' + arr[i]; 987 | } 988 | 989 | return arr[0] + '-' + arr[1] + '-' + arr[2] + ' ' + arr[3] + ':' + arr[4] + ':' + arr[5]; 990 | } 991 | -------------------------------------------------------------------------------- /opendb.js: -------------------------------------------------------------------------------- 1 | const BLACKLIST = { dbms: 1 }; 2 | const ISOP = { '+': 1, '-': 1, '*': 1, '/': 1, '=': 1, '!': 1, '#': 1 }; 3 | 4 | var INSTANCES = {}; 5 | 6 | function select(client, cmd) { 7 | 8 | var builder = cmd.builder; 9 | var opt = builder.options; 10 | var filter = WHERE(builder, null, null); 11 | var fields = FIELDS(builder); 12 | 13 | // opt.table 14 | var data = {}; 15 | 16 | data.type = 'find'; 17 | data.db = builder.options.table; 18 | 19 | if (fields) 20 | data.fields = fields; 21 | 22 | data.filter = filter.filter; 23 | data.filterarg = filter.arg; 24 | 25 | if (filter.sort) 26 | data.sort = filter.sort; 27 | 28 | if (filter.take) 29 | data.take = filter.take; 30 | 31 | if (filter.skip) 32 | data.skip = filter.skip; 33 | 34 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 35 | 36 | client.socket.rpc(data, function(err, response) { 37 | 38 | builder.db.busy = false; 39 | 40 | var rows = response; 41 | err && client.$opt.onerror && client.$opt.onerror(err, data); 42 | 43 | if (opt.first) 44 | rows = rows[0] || null; 45 | 46 | // checks joins 47 | if (!err && builder.$joins) { 48 | client.$dbms._joins(rows, builder); 49 | setImmediate(builder.db.$next); 50 | } else 51 | builder.$callback(err, rows); 52 | }); 53 | } 54 | 55 | function check(client, cmd) { 56 | var builder = cmd.builder; 57 | var opt = builder.options; 58 | var filter = WHERE(builder); 59 | var data = {}; 60 | 61 | data.type = 'read'; 62 | data.db = builder.options.table; 63 | data.filter = filter.filter; 64 | data.filterarg = filter.arg; 65 | 66 | if (!cmd.value && builder.options.params) 67 | cmd.value = []; 68 | 69 | builder.db.$debug && builder.db.$debug(data); 70 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 71 | 72 | client.socket.rpc(data, function(err, response) { 73 | builder.db.busy = false; 74 | var is = !err && !!response; 75 | err && client.$opt.onerror && client.$opt.onerror(err, data); 76 | builder.$callback(err, is); 77 | }); 78 | } 79 | 80 | function query(client, cmd) { 81 | 82 | var builder = cmd.builder; 83 | var opt = builder.options; 84 | var filter = WHERE(builder); 85 | var data = {}; 86 | 87 | data.type = 'find'; 88 | data.db = builder.options.table; 89 | data.filter = filter.filter; 90 | data.filterparams = filter.arg; 91 | data.scalar = cmd.query; 92 | data.scalararg = cmd.value || EMPTYOBJECT; 93 | 94 | builder.db.$debug && builder.db.$debug(data); 95 | F.$events.dbms && EMIT('dbms', 'query', opt.table, opt.db, builder); 96 | 97 | client.socket.rpc(data, function(err, response) { 98 | builder.db.busy = false; 99 | err && client.$opt.onerror && client.$opt.onerror(err, data); 100 | builder.$callback(err, response); 101 | }); 102 | } 103 | 104 | function scalar(client, cmd) { 105 | 106 | var builder = cmd.builder; 107 | var opt = builder.options; 108 | var scalar = ''; 109 | var data = {}; 110 | var filter = WHERE(builder); 111 | 112 | data.type = 'scalar'; 113 | data.db = builder.options.table; 114 | data.filter = filter.filter; 115 | data.filterparams =filter.arg; 116 | data.scalar = scalar; 117 | data.scalararg = EMPTYOBJECT; 118 | 119 | var name = cmd.name; 120 | if (name) { 121 | var index = name.indexOf(' as '); 122 | if (index !== -1) { 123 | name = name.substring(index + 4); 124 | cmd.name = cmd.name.substring(0, index); 125 | } 126 | } 127 | 128 | switch (cmd.scalar) { 129 | case 'group': 130 | data.scalar = cmd.field ? ('var k=doc.' + cmd.name + '+\'\';if (arg[k]){arg[k]+=doc.' + cmd.field + '||0}else{arg[k]=doc.' + cmd.field + '||0}') : ('var k=doc.' + cmd.name + '+\'\';if (arg[k]){arg[k]++}else{arg[k]=1}'); 131 | break; 132 | default: 133 | if (cmd.field) { 134 | data.scalar = 'var k=doc.' + cmd.name + '+\'\';if (arg[k]){tmp.bk=doc.' + cmd.field + '||0;' + (cmd.scalar === 'max' ? 'if(tmp.bk>arg[k])arg[k]=tmp.bk' : cmd.scalar === 'min' ? 'if(tmp.bktmp.val?tmp.val:arg.min;arg.max=arg.max==null?tmp.val:arg.max': 213 | case '<': 214 | key = key.substring(1); 215 | break; 216 | case '=': 217 | key = key.substring(1); 218 | break; 219 | case '#': 220 | break; 221 | case '!': 222 | // toggle 223 | key = key.substring(1); 224 | if (val) 225 | val = true; 226 | else 227 | val = false; 228 | break; 229 | } 230 | 231 | doc[key] = val == null ? null : typeof(val) === 'function' ? val(builder.value) : val; 232 | } 233 | 234 | // builder.db.$debug && builder.db.$debug(q); 235 | F.$events.dbms && EMIT('dbms', 'insert', opt.table, opt.db, builder); 236 | 237 | var data = {}; 238 | data.type = 'insert'; 239 | data.db = builder.options.table; 240 | data.data = doc; 241 | client.socket.rpc(data, function(err, response) { 242 | builder.db.busy = false; 243 | err && client.$opt.onerror && client.$opt.onerror(err, data); 244 | builder.$callback(err, err == null ? response : 0); 245 | }); 246 | } 247 | 248 | function insertexists(client, cmd) { 249 | 250 | var builder = cmd.builder; 251 | var opt = builder.options; 252 | var filter = WHERE(cmd.builder); 253 | var data = {}; 254 | 255 | data.type = 'read'; 256 | data.db = builder.options.table; 257 | data.filter = filter.filter; 258 | data.filterarg = filter.arg; 259 | 260 | F.$events.dbms && EMIT('dbms', 'select', opt.table, data, builder); 261 | 262 | client.socket.rpc(data, function(err, response) { 263 | builder.db.busy = false; 264 | err && client.$opt.onerror && client.$opt.onerror(err, data); 265 | if (response) 266 | builder.$callback(err, 0); 267 | else 268 | insert(client, cmd); 269 | }); 270 | } 271 | 272 | function modify(client, cmd) { 273 | 274 | cmd.builder.options.transform && cmd.builder.options.transform(cmd.builder.value, cmd.builder.db.$output, cmd.builder.db.$lastoutput); 275 | 276 | var keys = Object.keys(cmd.builder.value); 277 | var params = []; 278 | var arg = {}; 279 | var builder = []; 280 | var tmp; 281 | for (var i = 0; i < keys.length; i++) { 282 | var key = keys[i]; 283 | var val = cmd.builder.value[key]; 284 | 285 | if (val === undefined || BLACKLIST[key]) 286 | continue; 287 | 288 | if (cmd.builder.options.equal && cmd.builder.options.equal.indexOf(key) !== -1) 289 | continue; 290 | 291 | if (cmd.builder.options.fields && cmd.builder.options.fields.length) { 292 | var skip = true; 293 | for (var j = 0; j < cmd.builder.options.fields.length; j++) { 294 | var field = cmd.builder.options.fields[j]; 295 | if (field[0] === '-') { 296 | field = field.substring(1); 297 | if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 298 | skip = true; 299 | break; 300 | } 301 | skip = false; 302 | } else if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 303 | skip = false; 304 | break; 305 | } else 306 | skip = false; 307 | } 308 | 309 | if (skip) 310 | continue; 311 | } 312 | 313 | var c = key[0]; 314 | 315 | if (typeof(val) === 'function') 316 | val = val(cmd.builder.value); 317 | 318 | switch (c) { 319 | case '-': 320 | case '+': 321 | case '*': 322 | case '/': 323 | key = key.substring(1); 324 | params.push(val ? val : 0); 325 | builder.push('doc.' + key + '=(doc.' + key + '||0)' + c + push(arg, val ? val : 0, cmd.builder)); 326 | break; 327 | case '>': 328 | case '<': 329 | tmp = push(arg, val ? val : 0, cmd.builder); 330 | key = key.substring(1); 331 | builder.push('doc.' + key + '=(doc.' + key + '||0)' + c + tmp + '?(doc.' + key + '||0):' + tmp); 332 | break; 333 | case '!': 334 | // toggle 335 | key = key.substring(1); 336 | builder.push('doc.' + key + '=!doc.' + key); 337 | break; 338 | case '=': 339 | case '#': 340 | // raw 341 | builder.push('doc.' + key + '=' + val); 342 | break; 343 | default: 344 | builder.push('doc.' + key + '=' + push(arg, val, cmd.builder)); 345 | break; 346 | } 347 | } 348 | 349 | var opt = cmd.builder.options; 350 | 351 | if (opt.equal) { 352 | for (var i = 0; i < opt.equal.length; i++) 353 | cmd.builder.where(opt.equal[i], builder.value[opt.equal[i]]); 354 | } 355 | 356 | var filter = WHERE(cmd.builder); 357 | var data = {}; 358 | 359 | data.type = 'update'; 360 | data.db = cmd.builder.options.table; 361 | data.filter = filter.filter; 362 | data.filterparams = filter.arg; 363 | data.modify = builder.join(';'); 364 | data.modifyarg = arg; 365 | 366 | if (filter.take) 367 | data.take = filter.take; 368 | 369 | if (filter.skip) 370 | data.skip = filter.skip; 371 | 372 | cmd.builder.db.$debug && cmd.builder.db.$debug(data); 373 | F.$events.dbms && EMIT('dbms', 'update', data, builder); 374 | 375 | client.socket.rpc(data, function(err, response) { 376 | cmd.builder.db.busy = false; 377 | err && client.$opt.onerror && client.$opt.onerror(err, data); 378 | if (!response && cmd.insert) { 379 | if (cmd.insert !== true) 380 | cmd.builder.value = cmd.insert; 381 | cmd.builder.options.insert && cmd.builder.options.insert(cmd.builder.value, cmd.builder.options.insertparams); 382 | insert(client, cmd); 383 | } else 384 | cmd.builder.$callback(err, response); 385 | }); 386 | } 387 | 388 | function remove(client, cmd) { 389 | var builder = cmd.builder; 390 | var opt = cmd.builder.options; 391 | var filter = WHERE(builder); 392 | var data = {}; 393 | data.type = 'remove'; 394 | data.db = builder.options.table; 395 | data.filter = filter.filter; 396 | data.filterarg = filter.arg; 397 | 398 | if (filter.take) 399 | data.take = filter.take; 400 | 401 | if (filter.skip) 402 | data.skip = filter.skip; 403 | 404 | builder.db.$debug && builder.db.$debug(data); 405 | F.$events.dbms && EMIT('dbms', 'delete', opt.table, opt.db, builder); 406 | 407 | client.socket.rpc(data, function(err, response) { 408 | cmd.builder.db.busy = false; 409 | err && client.$opt.onerror && client.$opt.onerror(err, data); 410 | cmd.builder.$callback(err, response); 411 | }); 412 | } 413 | 414 | function clientcommand(cmd, client) { 415 | cmd.builder.pcounter = 0; 416 | switch (cmd.type) { 417 | case 'transaction': 418 | case 'end': 419 | case 'commit': 420 | case 'rollback': 421 | break; 422 | case 'find': 423 | case 'read': 424 | case 'list': 425 | select(client, cmd); 426 | break; 427 | case 'check': 428 | check(client, cmd); 429 | break; 430 | case 'scalar': 431 | scalar(client, cmd); 432 | break; 433 | case 'insert': 434 | if (cmd.unique) 435 | insertexists(client, cmd); 436 | else 437 | insert(client, cmd); 438 | break; 439 | 440 | case 'modify2': 441 | var cb = cmd.builder.$callback; 442 | cmd.builder.$callback = function(err, response) { 443 | cmd.builder.options.fields = null; 444 | if (err) { 445 | cb.call(cmd.builder, err, 0); 446 | } else if (response) { 447 | var mod = cmd.fn(response); 448 | if (mod) { 449 | cmd.builder.value = mod; 450 | cmd.builder.$callback = cb; 451 | if (cmd.builder.value.$clean) 452 | cmd.builder.value = cmd.builder.value.$clean(); 453 | modify(client, cmd); 454 | } else 455 | cb.call(cmd.builder, err, 0); 456 | } else { 457 | if (cmd.insert) { 458 | mod = cmd.fn(null); 459 | if (mod) { 460 | cmd.builder.value = mod; 461 | cmd.builder.$callback = cb; 462 | insert(client, cmd); 463 | } else 464 | cb.call(cmd.builder, err, 0); 465 | } else { 466 | cb.call(cmd.builder, err, 0); 467 | } 468 | } 469 | }; 470 | select(client, cmd); 471 | break; 472 | 473 | case 'update': 474 | case 'modify': 475 | modify(client, cmd); 476 | break; 477 | case 'remove': 478 | remove(client, cmd); 479 | break; 480 | case 'query': 481 | query(client, cmd); 482 | break; 483 | default: 484 | cmd.builder.$callback(new Error('Operation "' + cmd.type + '" not found')); 485 | break; 486 | } 487 | } 488 | 489 | function timeout(client, callbackid) { 490 | var obj = client.callbacks[callbackid]; 491 | if (obj) { 492 | delete client.callbacks[callbackid]; 493 | obj.callback('Timeout'); 494 | } 495 | } 496 | 497 | function connect(opt, self, cmd) { 498 | 499 | INSTANCES[opt.database] = { ready: 0 }; 500 | 501 | WEBSOCKETCLIENT(function(client) { 502 | 503 | client.callbackid = 1; 504 | client.callbacks = {}; 505 | client.rpc = function(query, callback) { 506 | if (callback) { 507 | query.callbackid = client.callbackid++; 508 | var obj = { callback: callback }; 509 | obj.timeout = setTimeout(timeout, 10000, client, query.callbackid); 510 | client.callbacks[query.callbackid] = obj; 511 | } 512 | client.send(query); 513 | }; 514 | 515 | client.on('error', function(err) { 516 | delete INSTANCES[opt.database]; 517 | delete opt.socket; 518 | cmd.builder.$callback(err); 519 | }); 520 | 521 | client.on('message', function(msg) { 522 | 523 | if (msg.type === 'init') { 524 | INSTANCES[opt.database].socket = client; 525 | INSTANCES[opt.database].ready = 1; 526 | opt.socket = client; 527 | clientcommand(cmd, opt); 528 | } 529 | 530 | if (msg && msg.callbackid) { 531 | var obj = client.callbacks[msg.callbackid]; 532 | if (obj) { 533 | clearTimeout(obj.timeout); 534 | obj.callback(msg.error, msg.response); 535 | } 536 | } 537 | 538 | }); 539 | 540 | client.connect(opt.url); 541 | }); 542 | } 543 | 544 | exports.run = function(opt, self, cmd) { 545 | self.$op = null; 546 | self.busy = true; 547 | self.$opt = opt; 548 | var conn = INSTANCES[opt.database]; 549 | if (conn) { 550 | if (conn.ready) 551 | clientcommand(cmd, opt); 552 | else 553 | setTimeout(exports.run, 300, opt, self, cmd); 554 | } else 555 | connect(opt, self, cmd); 556 | }; 557 | 558 | function push(arg, value, builder) { 559 | var p = 'k' + (builder.pcounter++); 560 | arg[p] = value; 561 | return 'arg.' + p; 562 | } 563 | 564 | function WHERE(builder) { 565 | 566 | var condition = []; 567 | var sort = ''; 568 | var op = '&&'; 569 | var opuse = false; 570 | var arg = {}; 571 | 572 | for (var i = 0; i < builder.$commands.length; i++) { 573 | var cmd = builder.$commands[i]; 574 | 575 | if (builder.options.islanguage && cmd.name && cmd.name[cmd.name.length - 1] === '§') 576 | cmd.name = cmd.name.substring(0, cmd.name.length - 1) + (builder.options.language || ''); 577 | 578 | switch (cmd.type) { 579 | case 'where': 580 | opuse && condition.length && condition.push(op); 581 | if (cmd.compare === '<>') 582 | cmd.compare = '!='; 583 | else if (cmd.compare === '=') 584 | cmd.compare = '=='; 585 | 586 | if (cmd.value === undefined) 587 | condition.push(cmd.name); 588 | else { 589 | var tmp = push(arg, cmd.value, builder); 590 | condition.push('doc.' + cmd.name + ' instanceof Array?(doc.' + cmd.name + '.indexOf(' + tmp + ')' + (cmd.compare === '==' ? '!=' : '==') + '-1):(doc.' + cmd.name + cmd.compare + tmp + ')'); 591 | } 592 | 593 | break; 594 | case 'custom': 595 | cmd.fn.call(builder, builder, builder.db.$output, builder.db.$lastoutput); 596 | break; 597 | case 'in': 598 | 599 | if (typeof(cmd.value) === 'function') 600 | cmd.value = cmd.value(); 601 | 602 | if (cmd.value instanceof Array) { 603 | 604 | if (cmd.field) { 605 | var tmp = []; 606 | for (var j = 0; j < cmd.value.length; j++) { 607 | if (cmd.value[j]) 608 | tmp.push(cmd.value[j][cmd.field]); 609 | } 610 | cmd.value = tmp; 611 | } 612 | 613 | opuse && condition.length && condition.push(op); 614 | condition.push(push(arg, cmd.value, builder) + '.indexOf(doc.' + cmd.name + ')!==-1'); 615 | } else { 616 | opuse && condition.length && condition.push(op); 617 | condition.push('doc.' + cmd.name + '==' + push(arg, cmd.field ? cmd.value[cmd.field] : cmd.value, builder)); 618 | } 619 | break; 620 | case 'notin': 621 | 622 | if (typeof(cmd.value) === 'function') 623 | cmd.value = cmd.value(); 624 | 625 | if (cmd.value instanceof Array) { 626 | 627 | if (cmd.field) { 628 | var tmp = []; 629 | for (var j = 0; j < cmd.value.length; j++) { 630 | if (cmd.value[j]) 631 | tmp.push(cmd.value[j][cmd.field]); 632 | } 633 | cmd.value = tmp; 634 | } 635 | 636 | opuse && condition.length && condition.push(op); 637 | condition.push(push(arg, cmd.value, builder) + '.indexOf(doc.' + cmd.name + ')===-1'); 638 | } else { 639 | opuse && condition.length && condition.push(op); 640 | condition.push('doc.' + cmd.name + '!=' + push(arg, cmd.field ? cmd.value[cmd.field] : cmd.value, cmd)); 641 | } 642 | 643 | break; 644 | case 'between': 645 | opuse && condition.length && condition.push(op); 646 | condition.push('(doc.' + cmd.name + '>=' + push(arg, cmd.a, builder) + '&&doc.' + cmd.name + '<=' + push(arg, cmd.b, builder) + ')'); 647 | break; 648 | case 'search': 649 | opuse && condition.length && condition.push(op); 650 | condition.push('doc.' + cmd.name + '.indexOf(' + push(arg, cmd.value, builder) + ')!==-1'); 651 | break; 652 | 653 | case 'searchfull': 654 | // tmp = ESCAPE('%' + cmd.value.toLowerCase().replace(/y/g, 'i') + '%'); 655 | // opuse && condition.length && condition.push(op); 656 | // condition.push('REPLACE(LOWER(to_tsvector(' + builder.options.table + '::text)::text), \'y\', \'i\') ILIKE ' + tmp); 657 | break; 658 | 659 | case 'searchall': 660 | // tmp = ''; 661 | // for (var j = 0; j < cmd.value.length; j++) 662 | // tmp += (tmp ? ' AND ' : '') + cmd.name + ' ILIKE ' + ESCAPE('%' + cmd.value[j] + '%'); 663 | // opuse && condition.length && condition.push(op); 664 | // condition.push('(' + (tmp || '0=1') + ')'); 665 | break; 666 | 667 | case 'fulltext': 668 | // tmp = ESCAPE('%' + cmd.value.toLowerCase() + '%'); 669 | // opuse && condition.length && condition.push(op); 670 | // condition.push('LOWER(' + cmd.name + ') ILIKE ' + tmp); 671 | break; 672 | case 'contains': 673 | opuse && condition.length && condition.push(op); 674 | condition.push('!!doc.' + cmd.name); 675 | break; 676 | case 'query': 677 | opuse && condition.length && condition.push(op); 678 | condition.push(cmd.query); 679 | break; 680 | case 'permit': 681 | break; 682 | case 'empty': 683 | opuse && condition.length && condition.push(op); 684 | condition.push('!doc.' + cmd.name); 685 | break; 686 | case 'month': 687 | case 'year': 688 | case 'day': 689 | case 'hour': 690 | case 'minute': 691 | opuse && condition.length && condition.push(op); 692 | var type = cmd.type === 'month' ? 'Month' : cmd.type === 'year' ? 'FullYear' : cmd.type === 'day' ? 'Date' : cmd.type === 'minute' ? 'Minutes' : cmd.type === 'hours' ? 'Hours' : 'Seconds'; 693 | condition.push('doc.' + cmd.name + ' instanceof Date?(doc.' + cmd.name + '.get' + type + '()===' + cmd.value + '):false'); 694 | break; 695 | case 'date': 696 | opuse && condition.length && condition.push(op); 697 | condition.push(cmd.value instanceof Date ? ('doc.' + cmd.name + ' instanceof Date?(doc.' + cmd.name + '.getDate()===' + cmd.value.getDate() + '&&doc.' + cmd.name + '.getMonth()===' + cmd.value.getMonth() + '&&doc.' + cmd.name + '.getFullYear()===' + cmd.value.getFullYear() + '):false') : ('!doc.' + cmd.name)); 698 | break; 699 | case 'or': 700 | opuse && condition.length && condition.push(op); 701 | op = '||'; 702 | opuse = false; 703 | condition.push('('); 704 | continue; 705 | case 'end': 706 | condition.push(')'); 707 | op = '&&'; 708 | break; 709 | case 'and': 710 | opuse && condition.length && condition.push(op); 711 | op = '&&'; 712 | break; 713 | case 'sort': 714 | sort = cmd.name + '_' + (cmd.desc ? 'desc' : 'asc'); 715 | break; 716 | case 'regexp': 717 | // tmp = cmd.value.toString().substring(1); 718 | // var g = '~'; 719 | // if (tmp[tmp.length - 1] === 'i') { 720 | // tmp = tmp.substring(0, tmp.length - 2); 721 | // g = '~*'; 722 | // } else 723 | // tmp = tmp.substring(0, tmp.length - 1); 724 | // opuse && condition.length && condition.push(op); 725 | // condition.push(cmd.name + g + '\'' + tmp + '\''); 726 | break; 727 | } 728 | opuse = true; 729 | } 730 | 731 | // var query = (condition.length ? (' WHERE ' + condition.join(' ')) : '') + (group ? (' GROUP BY ' + group) : ''); 732 | return { filter: condition.length ? condition.join('') : 'true', arg: arg, sort: sort, take: builder.options.take, skip: builder.options.skip }; 733 | } 734 | 735 | function FIELDS(builder) { 736 | var fields = builder.options.fields || ''; 737 | return fields + (fields && fields.length && builder.$joinmeta ? (',' + builder.$joinmeta.a) : ''); 738 | } 739 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dbms", 3 | "version": "1.0.9", 4 | "description": "Database Management System", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/totaljs/dbms.git" 12 | }, 13 | "keywords": [ 14 | "orm", 15 | "database", 16 | "postgresql", 17 | "mysql", 18 | "mariadb", 19 | "mongodb", 20 | "sqlserver", 21 | "nosql" 22 | ], 23 | "author": "Peter Sirka", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/totaljs/dbms/issues" 27 | }, 28 | "homepage": "https://github.com/totaljs/dbms#readme" 29 | } 30 | -------------------------------------------------------------------------------- /pg-lo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A custom implementation of pg-large-object 3 | * @author Joris van der Wel 4 | * @contributor Peter Sirka 5 | */ 6 | 7 | const NOOP = function(){}; 8 | const Stream = require('stream'); 9 | const BUFFERSIZE = 16384; 10 | const WRITESTREAM = { highWaterMark: BUFFERSIZE, decodeStrings: true, objectMode: false }; 11 | const READSTREAM = { highWaterMark: BUFFERSIZE, encoding: null, objectMode: false }; 12 | 13 | var LargeObject = function(client, oid, fd) { 14 | this._client = client; 15 | this.oid = oid; 16 | this._fd = fd; 17 | }; 18 | 19 | LargeObject.SEEK_SET = 0; 20 | LargeObject.SEEK_CUR = 1; 21 | LargeObject.SEEK_END = 2; 22 | 23 | LargeObject.prototype.close = function(callback) { 24 | var self = this; 25 | self._client.query({ name: 'npg_lo_close', text: 'SELECT lo_close($1) as ok', values: [self._fd] }, callback); 26 | return self; 27 | }; 28 | 29 | LargeObject.prototype.read = function(length, callback) { 30 | var self = this; 31 | self._client.query({ name: 'npg_loread', text:'SELECT loread($1, $2) as data', values: [self._fd, length] }, function(err, response) { 32 | if (err) 33 | return callback(err); 34 | var data = response.rows[0].data; 35 | callback(null, data); 36 | }); 37 | return self; 38 | }; 39 | 40 | LargeObject.prototype.write = function(buffer, callback) { 41 | var self = this; 42 | self._client.query({ name: 'npg_lowrite', text:'SELECT lowrite($1, $2)', values: [self._fd, buffer] }, callback); 43 | return self; 44 | }; 45 | 46 | LargeObject.prototype.seek = function(position, ref, callback) { 47 | var self = this; 48 | self._client.query({ name: 'npg_lo_lseek64', text: 'SELECT lo_lseek' + self.plusql + '($1, $2, $3) as location', values: [self._fd, position, ref] }, function(err, response) { 49 | if (err) 50 | return callback(err); 51 | var location = response.rows[0].location; 52 | callback(null, location); 53 | }); 54 | return self; 55 | }; 56 | 57 | LargeObject.prototype.tell = function(callback) { 58 | var self = this; 59 | self._client.query({ name: 'npg_lo_tell64', text: 'SELECT lo_tell' + self.plusql + '($1) as location', values: [self._fd] }, function(err, response) { 60 | if (err) 61 | return callback(err); 62 | var location = response.rows[0].location; 63 | callback(null, location); 64 | }); 65 | return self; 66 | }; 67 | 68 | LargeObject.prototype.size = function(callback) { 69 | var self = this; 70 | self._client.query({ name: 'npg_size', text: 'SELECT lo_lseek' + self.plusql + '($1, location, 0), seek.size FROM (SELECT lo_lseek' + self.plusql + '($1, 0, 2) AS SIZE, tell.location FROM (SELECT lo_tell' + self.plusql + '($1) AS location) tell) seek', values: [self._fd] }, function(err, response) { 71 | if (err) 72 | return callback(err); 73 | var size = response.rows[0].size; 74 | callback(null, size); 75 | }); 76 | return self; 77 | }; 78 | 79 | LargeObject.prototype.truncate = function(length, callback) { 80 | var self = this; 81 | self._client.query({ name: 'npg_lo_truncate' + self.plusql, text:'SELECT lo_truncate' + self.plusql + '($1, $2)', values: [self._fd, length]}, callback); 82 | return self; 83 | }; 84 | 85 | LargeObject.prototype.getReadableStream = function(bufferSize) { 86 | return new ReadStream(this, bufferSize); 87 | }; 88 | 89 | LargeObject.prototype.getWritableStream = function(bufferSize) { 90 | return new WriteStream(this, bufferSize); 91 | }; 92 | 93 | var LargeObjectManager = function(client) { 94 | this._client = client; 95 | }; 96 | 97 | LargeObjectManager.WRITE = 0x00020000; 98 | LargeObjectManager.READ = 0x00040000; 99 | LargeObjectManager.READWRITE = 0x00020000 | 0x00040000; 100 | 101 | LargeObjectManager.prototype.open = function(oid, mode, callback) { 102 | 103 | if (!oid) 104 | throw 'Illegal Argument'; 105 | 106 | var self = this; 107 | self._client.query({ name: 'npg_lo_open', text:'SELECT lo_open($1, $2) AS fd, current_setting(\'server_version_num\') as version', values: [oid, mode]}, function(err, response) { 108 | if (err) 109 | return callback(err); 110 | var lo = new LargeObject(self._client, oid, response.rows[0].fd); 111 | lo.oldversion = response.rows[0].version < 90300; 112 | lo.plusql = lo.oldversion ? '' : '64'; 113 | callback(null, lo); 114 | }); 115 | 116 | return self; 117 | }; 118 | 119 | LargeObjectManager.prototype.create = function(callback) { 120 | this._client.query({ name: 'npg_lo_creat', text:'SELECT lo_creat($1) AS oid', values: [LargeObjectManager.READWRITE]}, function(err, response) { 121 | if (err) 122 | return callback(err); 123 | var oid = response.rows[0].oid; 124 | callback(null, oid); 125 | }); 126 | return this; 127 | }; 128 | 129 | LargeObjectManager.prototype.unlink = function(oid, callback) { 130 | if (!oid) 131 | throw 'Illegal Argument'; 132 | this._client.query({ name: 'npg_lo_unlink', text:'SELECT lo_unlink($1) as ok', values: [oid]}, callback); 133 | return this; 134 | }; 135 | 136 | 137 | LargeObjectManager.prototype.readStream = function(oid, bufferSize, callback) { 138 | 139 | if (typeof(bufferSize) === 'function') { 140 | callback = bufferSize; 141 | bufferSize = undefined; 142 | } 143 | 144 | var self = this; 145 | 146 | self.open(oid, LargeObjectManager.READ, function(err, obj) { 147 | 148 | if (err) 149 | return callback(err); 150 | 151 | obj.size(function(err, size) { 152 | 153 | if (err) 154 | return callback(err); 155 | 156 | if (size === '0') 157 | return callback(new Error('Stream is empty.'), size, null); 158 | 159 | var stream = obj.getReadableStream(bufferSize); 160 | 161 | stream.on('error', function() { 162 | obj.close(NOOP); 163 | }); 164 | 165 | stream.on('end', function() { 166 | obj.close(NOOP); 167 | }); 168 | 169 | callback(null, size, stream); 170 | }); 171 | }); 172 | 173 | return self; 174 | }; 175 | 176 | LargeObjectManager.prototype.writeStream = function(bufferSize, callback) { 177 | 178 | if (typeof(bufferSize) === 'function') { 179 | callback = bufferSize; 180 | bufferSize = undefined; 181 | } 182 | 183 | var self = this; 184 | self.create(function(err, oid) { 185 | if (err) 186 | return callback(err); 187 | 188 | self.open(oid, LargeObjectManager.WRITE, function(err, obj) { 189 | if (err) 190 | return callback(err); 191 | 192 | var stream = obj.getWritableStream(bufferSize); 193 | 194 | stream.on('error', function() { 195 | obj.close(NOOP); 196 | }); 197 | 198 | stream.on('finish', function() { 199 | obj.close(NOOP); 200 | }); 201 | 202 | callback(null, oid, stream); 203 | }); 204 | 205 | }); 206 | return self; 207 | }; 208 | 209 | 210 | var WriteStream = function(largeObject, bufferSize) { 211 | 212 | if (bufferSize && bufferSize !== BUFFERSIZE) 213 | WRITESTREAM.bufferSize = bufferSize; 214 | else if (WRITESTREAM.bufferSize !== BUFFERSIZE) 215 | WRITESTREAM.bufferSize = BUFFERSIZE; 216 | 217 | Stream.Writable.call(this, WRITESTREAM); 218 | this._largeObject = largeObject; 219 | }; 220 | 221 | WriteStream.prototype = Object.create(Stream.Writable.prototype); 222 | WriteStream.prototype._write = function(chunk, encoding, callback) { 223 | if (!Buffer.isBuffer(chunk)) 224 | throw 'Illegal Argument'; 225 | this._largeObject.write(chunk, callback); 226 | }; 227 | 228 | var ReadStream = function(largeObject, bufferSize) { 229 | if (bufferSize && bufferSize !== BUFFERSIZE) 230 | READSTREAM.bufferSize = bufferSize; 231 | else if (READSTREAM.bufferSize !== BUFFERSIZE) 232 | READSTREAM.bufferSize = BUFFERSIZE; 233 | Stream.Readable.call(this, READSTREAM); 234 | this._largeObject = largeObject; 235 | }; 236 | 237 | ReadStream.prototype = Object.create(Stream.Readable.prototype); 238 | ReadStream.prototype._read = function(length) { 239 | 240 | if (length <= 0) 241 | throw 'Illegal Argument'; 242 | 243 | var self = this; 244 | self._largeObject.read(length, function(error, data) { 245 | 246 | if (error) 247 | return self.emit('error', error); 248 | 249 | self.push(data); 250 | 251 | if (data.length < length) 252 | self.push(null); // the large object has no more data left 253 | }); 254 | }; 255 | 256 | exports.create = function(client) { 257 | return new LargeObjectManager(client); 258 | }; -------------------------------------------------------------------------------- /pg.js: -------------------------------------------------------------------------------- 1 | const Database = require('pg'); 2 | const Lo = require('./pg-lo'); 3 | const POOLS = {}; 4 | const REG_ESCAPE_1 = /'/g; 5 | const REG_ESCAPE_2 = /\\/g; 6 | const REG_PARAMS = /\$\d/g; 7 | const EMPTYARRAY = []; 8 | const BLACKLIST = { dbms: 1 }; 9 | const ISOP = { '+': 1, '-': 1, '*': 1, '/': 1, '=': 1, '!': 1, '#': 1 }; 10 | const CANSTATS = global.F ? (global.F.stats && global.F.stats.performance && global.F.stats.performance.dbrm != null) : false; 11 | 12 | // Convertor: Numeric to number 13 | Database.types.setTypeParser(1700, val => val == null ? null : +val); 14 | 15 | var ESCAPE = global.PG_ESCAPE = function(value) { 16 | 17 | if (value == null) 18 | return 'null'; 19 | 20 | var type = typeof(value); 21 | 22 | if (type === 'function') { 23 | value = value(); 24 | if (value == null) 25 | return 'null'; 26 | type = typeof(value); 27 | } 28 | 29 | if (type === 'boolean') 30 | return value === true ? 'true' : 'false'; 31 | 32 | if (type === 'number') 33 | return value.toString(); 34 | 35 | if (type === 'string') 36 | return pg_escape(value); 37 | 38 | if (value instanceof Array) 39 | return pg_escape(value.join(',')); 40 | 41 | if (value instanceof Date) 42 | return pg_escape(dateToString(value)); 43 | 44 | return pg_escape(value.toString()); 45 | }; 46 | 47 | function createpool(opt) { 48 | return POOLS[opt.id] ? POOLS[opt.id] : (POOLS[opt.id] = opt.options.native ? new Database.native.Pool(opt.options) : new Database.Pool(opt.options)); 49 | } 50 | 51 | function createclient(opt) { 52 | return opt.options.native ? new Database.native.Client(opt.options) : new Database.Client(opt.options); 53 | } 54 | 55 | function select(client, cmd) { 56 | 57 | var builder = cmd.builder; 58 | var opt = builder.options; 59 | var params = []; 60 | var q = 'SELECT ' + FIELDS(builder) + ' FROM ' + opt.table + WHERE(builder, null, null, params); 61 | 62 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 63 | builder.db.$debug && builder.db.$debug(q); 64 | 65 | if (CANSTATS) 66 | F.stats.performance.dbrm++; 67 | 68 | client.query(q, params, function(err, response) { 69 | builder.db.busy = false; 70 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 71 | 72 | var rows = response ? response.rows : EMPTYARRAY; 73 | if (opt.first) 74 | rows = rows.length ? rows[0] : null; 75 | 76 | // checks joins 77 | if (!err && builder.$joins) { 78 | client.$dbms._joins(rows, builder); 79 | setImmediate(builder.db.$next); 80 | } else 81 | builder.$callback(err, rows); 82 | 83 | }); 84 | } 85 | 86 | function check(client, cmd) { 87 | 88 | var builder = cmd.builder; 89 | var opt = builder.options; 90 | var params = []; 91 | var q = 'SELECT 1 FROM ' + opt.table + WHERE(builder, null, null, params); 92 | 93 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 94 | 95 | if (CANSTATS) 96 | F.stats.performance.dbrm++; 97 | 98 | builder.db.$debug && builder.db.$debug(q); 99 | client.query(q, params, function(err, response) { 100 | builder.db.busy = false; 101 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 102 | var is = response && response.rows ? response.rows[0] != null : false; 103 | builder.$callback(err, is); 104 | }); 105 | } 106 | 107 | function query(client, cmd) { 108 | var builder = cmd.builder; 109 | var opt = builder.options; 110 | 111 | if (!cmd.value && builder.options.params) 112 | cmd.value = []; 113 | 114 | var q = cmd.query + WHERE(builder, null, null, cmd.value); 115 | builder.db.$debug && builder.db.$debug(q); 116 | 117 | if (CANSTATS) 118 | F.stats.performance.dbrm++; 119 | 120 | F.$events.dbms && EMIT('dbms', 'query', cmd.query, opt.db, builder); 121 | client.query(q, cmd.value, function(err, response) { 122 | builder.db.busy = false; 123 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 124 | var rows = response ? response.rows : EMPTYARRAY; 125 | if (opt.first) 126 | rows = rows.length ? rows[0] : null; 127 | builder.$callback(err, rows); 128 | }); 129 | } 130 | 131 | function command(client, sql, cmd) { 132 | cmd.db.$debug && cmd.db.$debug(sql); 133 | F.$events.dbms && EMIT('dbms', 'query', sql, cmd.db); 134 | 135 | if (CANSTATS) 136 | F.stats.performance.dbrm++; 137 | 138 | client.query(sql, function(err) { 139 | cmd.db.busy = false; 140 | err && client.$opt.onerror && client.$opt.onerror(err, sql); 141 | cmd.db.$next(err); 142 | }); 143 | } 144 | 145 | function list(client, cmd) { 146 | 147 | var builder = cmd.builder; 148 | var opt = builder.options; 149 | var params = []; 150 | var query = WHERE(builder, true, null, params); 151 | var q; 152 | 153 | if (cmd.improved && builder.skip) { 154 | 155 | q = 'SELECT ' + FIELDS(builder) + ' FROM ' + opt.table + query + OFFSET(builder); 156 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 157 | builder.db.$debug && builder.db.$debug(q); 158 | builder.db.busy = true; 159 | 160 | if (CANSTATS) 161 | F.stats.performance.dbrm++; 162 | 163 | client.query(q, params, function(err, response) { 164 | builder.db.busy = false; 165 | var rows = response ? response.rows : []; 166 | if (!err && builder.$joins) { 167 | client.$dbms._joins(rows, builder, rows.length); 168 | setImmediate(builder.db.$next); 169 | } else 170 | builder.$callback(err, rows, rows.length); 171 | }); 172 | 173 | } else { 174 | q = 'SELECT COUNT(1)::int as dbmsvalue FROM ' + opt.table + query; 175 | builder.db.$debug && builder.db.$debug(q); 176 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 177 | 178 | if (CANSTATS) 179 | F.stats.performance.dbrm++; 180 | 181 | client.query(q, params, function(err, response) { 182 | builder.db.busy = false; 183 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 184 | 185 | var count = err ? 0 : response.rows && response.rows.length ? response.rows[0].dbmsvalue : 0; 186 | var fn = function(err, response) { 187 | builder.db.busy = false; 188 | 189 | var rows = response ? response.rows : []; 190 | 191 | // checks joins 192 | // client.$dbms._joins(rows, builder); 193 | if (!err && builder.$joins) { 194 | client.$dbms._joins(rows, builder, count); 195 | setImmediate(builder.db.$next); 196 | } else 197 | builder.$callback(err, rows, count); 198 | }; 199 | 200 | if (count) { 201 | builder.db.busy = true; 202 | q = 'SELECT ' + FIELDS(builder) + ' FROM ' + opt.table + query + OFFSET(builder); 203 | builder.db.$debug && builder.db.$debug(q); 204 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 205 | 206 | if (CANSTATS) 207 | F.stats.performance.dbrm++; 208 | 209 | client.query(q, params, fn); 210 | } else 211 | fn(err, null); 212 | }); 213 | } 214 | } 215 | 216 | function scalar(client, cmd) { 217 | 218 | var builder = cmd.builder; 219 | var opt = builder.options; 220 | var params = []; 221 | var q; 222 | 223 | switch (cmd.scalar) { 224 | case 'avg': 225 | case 'min': 226 | case 'sum': 227 | case 'max': 228 | case 'count': 229 | q = 'SELECT ' + cmd.scalar.toUpperCase() + (cmd.scalar !== 'count' ? ('(' + (cmd.field || cmd.name) + ')') : '(1)') + '::int as dbmsvalue FROM ' + opt.table; 230 | break; 231 | case 'group': 232 | q = 'SELECT ' + cmd.name + ', ' + (cmd.field ? ('SUM(' + cmd.field + ')::numeric') : 'COUNT(1)::int') + ' as count FROM ' + opt.table; 233 | break; 234 | } 235 | 236 | q = q + WHERE(builder, false, cmd.scalar === 'group' ? cmd.name : null, params); 237 | builder.db.$debug && builder.db.$debug(q); 238 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 239 | 240 | if (CANSTATS) 241 | F.stats.performance.dbrm++; 242 | 243 | client.query(q, params, function(err, response) { 244 | builder.db.busy = false; 245 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 246 | 247 | var rows = response ? response.rows || EMPTYARRAY : EMPTYARRAY; 248 | if (!cmd.field && cmd.scalar !== 'group') 249 | rows = rows.length ? (rows[0].dbmsvalue || 0) : 0; 250 | 251 | builder.$callback(err, rows); 252 | }); 253 | } 254 | 255 | function abortcommands(client, builder) { 256 | while (builder.db.$commands.length) { 257 | var c = builder.db.$commands.shift(); 258 | if (c && c.type === 'commit') { 259 | c.type = 'rollback'; 260 | builder.db.$commands.unshift(c); 261 | break; 262 | } 263 | } 264 | } 265 | 266 | function insert(client, cmd) { 267 | 268 | var builder = cmd.builder; 269 | 270 | cmd.builder.options.transform && cmd.builder.options.transform(cmd.builder.value, cmd.builder.db.$output, cmd.builder.db.$lastoutput); 271 | 272 | var keys = Object.keys(builder.value); 273 | var params = []; 274 | var fields = []; 275 | var values = []; 276 | var index = 1; 277 | var opt = builder.options; 278 | 279 | for (var i = 0; i < keys.length; i++) { 280 | var key = keys[i]; 281 | var val = builder.value[key]; 282 | if (val === undefined || BLACKLIST[key]) 283 | continue; 284 | 285 | if (builder.options.fields && builder.options.fields.length) { 286 | var skip = true; 287 | for (var j = 0; j < builder.options.fields.length; j++) { 288 | var field = builder.options.fields[j]; 289 | if (field[0] === '-') { 290 | field = field.substring(1); 291 | if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 292 | skip = true; 293 | break; 294 | } 295 | skip = false; 296 | } else if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 297 | skip = false; 298 | break; 299 | } else 300 | skip = false; 301 | } 302 | 303 | if (skip) 304 | continue; 305 | } 306 | 307 | var raw = false; 308 | 309 | switch (key[0]) { 310 | case '-': 311 | case '+': 312 | case '*': 313 | case '/': 314 | case '>': 315 | case '<': 316 | key = key.substring(1); 317 | break; 318 | case '=': 319 | key = key.substring(1); 320 | raw = true; 321 | break; 322 | case '#': 323 | continue; 324 | case '!': 325 | // toggle 326 | key = key.substring(1); 327 | if (val) 328 | val = true; 329 | else 330 | val = false; 331 | break; 332 | } 333 | 334 | fields.push('"' + key + '"'); 335 | 336 | if (raw) { 337 | values.push(val); 338 | } else { 339 | values.push('$' + index++); 340 | params.push(val == null ? null : typeof(val) === 'function' ? val(builder.value) : val); 341 | } 342 | } 343 | 344 | var q = 'INSERT INTO ' + opt.table + ' (' + fields.join(',') + ') VALUES(' + values.join(',') + ')' + (builder.$primarykey ? (' RETURNING ' + builder.$primarykey) : ''); 345 | 346 | builder.db.$debug && builder.db.$debug(q); 347 | F.$events.dbms && EMIT('dbms', 'insert', opt.table, opt.db, builder); 348 | 349 | if (CANSTATS) 350 | F.stats.performance.dbwm++; 351 | 352 | client.query(q, params, function(err, response) { 353 | 354 | // Transaction is aborted 355 | if (err && client.$dbmstransaction) { 356 | client.$dbmstransaction = true; 357 | abortcommands(client, builder); 358 | } 359 | 360 | builder.db.busy = false; 361 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 362 | builder.$callback(err, err == null ? (response.rows && response.rows.length ? response.rows[0][builder.$primarykey] : 1) : 0); 363 | }); 364 | } 365 | 366 | function insertexists(client, cmd) { 367 | var builder = cmd.builder; 368 | var opt = builder.options; 369 | var q = 'SELECT 1 as dbmsvalue FROM ' + opt.table + WHERE(builder); 370 | builder.db.$debug && builder.db.$debug(q); 371 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 372 | 373 | if (CANSTATS) 374 | F.stats.performance.dbrm++; 375 | 376 | client.query(q, function(err, response) { 377 | builder.db.busy = false; 378 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 379 | var rows = response ? response.rows : EMPTYARRAY; 380 | if (rows.length) 381 | builder.$callback(err, 0); 382 | else 383 | insert(client, cmd); 384 | }); 385 | } 386 | 387 | function modify(client, cmd) { 388 | 389 | cmd.builder.options.transform && cmd.builder.options.transform(cmd.builder.value, cmd.builder.db.$output, cmd.builder.db.$lastoutput); 390 | 391 | var keys = Object.keys(cmd.builder.value); 392 | var fields = []; 393 | var params = []; 394 | var index = 1; 395 | 396 | for (var i = 0; i < keys.length; i++) { 397 | var key = keys[i]; 398 | var val = cmd.builder.value[key]; 399 | 400 | if (val === undefined || BLACKLIST[key]) 401 | continue; 402 | 403 | if (cmd.builder.options.equal && cmd.builder.options.equal.indexOf(key) !== -1) 404 | continue; 405 | 406 | if (cmd.builder.options.fields && cmd.builder.options.fields.length) { 407 | var skip = true; 408 | for (var j = 0; j < cmd.builder.options.fields.length; j++) { 409 | var field = cmd.builder.options.fields[j]; 410 | if (field[0] === '-') { 411 | field = field.substring(1); 412 | if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 413 | skip = true; 414 | break; 415 | } 416 | skip = false; 417 | } else if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 418 | skip = false; 419 | break; 420 | } else 421 | skip = false; 422 | } 423 | 424 | if (skip) 425 | continue; 426 | } 427 | 428 | var c = key[0]; 429 | var type; 430 | 431 | if (typeof(val) === 'function') 432 | val = val(cmd.builder.value); 433 | 434 | switch (c) { 435 | case '-': 436 | case '+': 437 | case '*': 438 | case '/': 439 | params.push(val ? val : 0); 440 | key = key.substring(1); 441 | type = '"' + key + '"=COALESCE(' + key + ',0)' + c + '$' + (index++); 442 | break; 443 | case '>': 444 | case '<': 445 | params.push(val ? val : 0); 446 | key = key.substring(1); 447 | type = '"' + key + '"=' + (c === '>' ? 'GREATEST' : 'LEAST') + '(' + key + ',$' + (index++) + ')'; 448 | break; 449 | case '!': 450 | // toggle 451 | key = key.substring(1); 452 | type = '"' + key + '"=NOT ' + key; 453 | break; 454 | case '=': 455 | case '#': 456 | // raw 457 | type = '"' + key.substring(1) + '"=' + val; 458 | break; 459 | default: 460 | params.push(val); 461 | type = '"' + key + '"=$' + (index++); 462 | break; 463 | } 464 | type && fields.push(type); 465 | } 466 | 467 | var builder = cmd.builder; 468 | var opt = builder.options; 469 | 470 | if (opt.equal) { 471 | for (var i = 0; i < opt.equal.length; i++) 472 | cmd.builder.where(opt.equal[i], builder.value[opt.equal[i]]); 473 | } 474 | 475 | var q = 'WITH rows AS (UPDATE ' + opt.table + ' SET ' + fields + WHERE(builder, true, null, params) + ' RETURNING 1) SELECT count(1)::int as dbmsvalue FROM rows'; 476 | builder.db.$debug && builder.db.$debug(q); 477 | F.$events.dbms && EMIT('dbms', 'update', opt.table, opt.db, cmd.builder); 478 | 479 | if (CANSTATS) 480 | F.stats.performance.dbwm++; 481 | 482 | client.query(q, params, function(err, response) { 483 | 484 | // Transaction is aborted 485 | if (err && client.$dbmstransaction) { 486 | client.$dbmstransaction = true; 487 | abortcommands(client, builder); 488 | } 489 | 490 | builder.db.busy = false; 491 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 492 | var rows = response ? response.rows || EMPTYARRAY : EMPTYARRAY; 493 | rows = rows.length ? rows[0].dbmsvalue : 0; 494 | if (!rows && cmd.insert) { 495 | if (cmd.insert !== true) 496 | cmd.builder.value = cmd.insert; 497 | cmd.builder.options.insert && cmd.builder.options.insert(cmd.builder.value, cmd.builder.options.insertparams); 498 | insert(client, cmd); 499 | } else 500 | builder.$callback(err, rows); 501 | }); 502 | } 503 | 504 | function remove(client, cmd) { 505 | var builder = cmd.builder; 506 | var opt = builder.options; 507 | var params = []; 508 | var q = 'WITH rows AS (DELETE FROM ' + opt.table + WHERE(builder, true, null, params) + ' RETURNING 1) SELECT count(1)::int as dbmsvalue FROM rows'; 509 | builder.db.$debug && builder.db.$debug(q); 510 | F.$events.dbms && EMIT('dbms', 'delete', opt.table, opt.db, builder); 511 | 512 | if (CANSTATS) 513 | F.stats.performance.dbwm++; 514 | 515 | client.query(q, params, function(err, response) { 516 | 517 | // Transaction is aborted 518 | if (err && client.$dbmstransaction) { 519 | client.$dbmstransaction = true; 520 | abortcommands(client, builder); 521 | } 522 | 523 | builder.db.busy = false; 524 | err && client.$opt.onerror && client.$opt.onerror(err, q, builder); 525 | var rows = response ? response.rows || EMPTYARRAY : EMPTYARRAY; 526 | rows = rows.length ? rows[0].dbmsvalue : 0; 527 | builder.$callback(err, rows); 528 | }); 529 | } 530 | 531 | function destroy(conn) { 532 | 533 | var client = conn.client; 534 | if (client) { 535 | 536 | if (client.$dbmstransaction) 537 | client.$dbmstransaction = false; 538 | 539 | if (client.release) 540 | client.release(); 541 | else 542 | client.end(); 543 | } 544 | } 545 | 546 | function clientcommand(cmd, client, self) { 547 | switch (cmd.type) { 548 | case 'transaction': 549 | client.$dbmstransaction = true; 550 | command(client, 'BEGIN', cmd); 551 | break; 552 | case 'end': 553 | cmd.type = self.$eb ? self.$errors.items.length ? 'ROLLBACK' : self.$errors.length ? 'ROLLBACK' : 'COMMIT' : 'COMMIT'; 554 | command(client, cmd.type, cmd); 555 | break; 556 | case 'commit': 557 | case 'rollback': 558 | client.$dbmstransaction = false; 559 | command(client, cmd.type.toUpperCase(), cmd); 560 | break; 561 | case 'find': 562 | case 'read': 563 | select(client, cmd); 564 | break; 565 | case 'diff': 566 | var cb = cmd.builder.$callback; 567 | cmd.builder.$callback = function(err, response) { 568 | cb.call(cmd.builder, err, err ? EMPTYOBJECT : DIFFARR(cmd.key, response, cmd.form)); 569 | }; 570 | select(client, cmd); 571 | break; 572 | 573 | case 'modify2': 574 | var cb = cmd.builder.$callback; 575 | cmd.builder.$callback = function(err, response) { 576 | cmd.builder.options.fields = null; 577 | if (err) { 578 | cb.call(cmd.builder, err, 0); 579 | } else if (response) { 580 | cmd.builder.db.busy = true; 581 | var mod = cmd.fn(response, cmd.builder.db.$output, cmd.builder.db.$outputall); 582 | cmd.builder.db.busy = false; 583 | if (mod) { 584 | cmd.builder.value = mod; 585 | cmd.builder.$callback = cb; 586 | if (cmd.builder.value.$clean) 587 | cmd.builder.value = cmd.builder.value.$clean(); 588 | modify(client, cmd); 589 | } else 590 | cb.call(cmd.builder, err, 0); 591 | } else { 592 | if (cmd.insert) { 593 | cmd.builder.db.busy = true; 594 | mod = cmd.fn(null, cmd.builder.db.$output, cmd.builder.db.$outputall); 595 | cmd.builder.db.busy = false; 596 | if (mod) { 597 | cmd.builder.value = mod; 598 | cmd.builder.$callback = cb; 599 | insert(client, cmd); 600 | } else 601 | cb.call(cmd.builder, err, 0); 602 | } else { 603 | cb.call(cmd.builder, err, 0); 604 | } 605 | } 606 | }; 607 | select(client, cmd); 608 | break; 609 | case 'check': 610 | check(client, cmd); 611 | break; 612 | case 'list': 613 | list(client, cmd); 614 | break; 615 | case 'scalar': 616 | scalar(client, cmd); 617 | break; 618 | case 'insert': 619 | if (cmd.unique) 620 | insertexists(client, cmd); 621 | else 622 | insert(client, cmd); 623 | break; 624 | case 'update': 625 | case 'modify': 626 | modify(client, cmd); 627 | break; 628 | case 'remove': 629 | remove(client, cmd); 630 | break; 631 | case 'query': 632 | query(client, cmd); 633 | break; 634 | default: 635 | cmd.builder.$callback(new Error('Operation "' + cmd.type + '" not found')); 636 | break; 637 | } 638 | } 639 | 640 | exports.run = function(opt, self, cmd, repeated) { 641 | 642 | var conn = self.$conn[opt.id]; 643 | 644 | if (!conn) { 645 | conn = self.$conn[opt.id] = { driver: opt.options.pooling ? createpool(opt) : createclient(opt) }; 646 | conn.$$destroy = destroy; 647 | } 648 | 649 | self.$op = null; 650 | self.busy = true; 651 | 652 | if (conn.client) { 653 | clientcommand(cmd, conn.client, self); 654 | return; 655 | } 656 | 657 | conn.driver.connect(function(err, client) { 658 | 659 | if (err) { 660 | 661 | self.busy = false; 662 | opt.onerror && opt.onerror(err); 663 | 664 | if ((!repeated || repeated < 3) && err.toString().indexOf('many clients') !== -1) { 665 | // try again 666 | setTimeout(function() { 667 | exports.run(opt, self, cmd, (repeated || 0) + 1); 668 | }, 200); 669 | return; 670 | } 671 | 672 | if (cmd.builder) 673 | cmd.builder.$callback(err); 674 | else 675 | cmd.db.$next(err); 676 | 677 | } else { 678 | conn.client = client; 679 | client.$opt = opt; 680 | client.$dbms = self; 681 | clientcommand(cmd, conn.client, self); 682 | } 683 | }); 684 | }; 685 | 686 | exports.blob_remove = function(opt, id, callback) { 687 | if (typeof(id) === 'string') 688 | id = +id; 689 | createpool(opt).connect(function(err, client, done) { 690 | if (err) { 691 | done && done(); 692 | callback && callback(err); 693 | } else { 694 | client.query('DELETE FROM pg_largeobject WHERE loid=' + id, function(err) { 695 | done(); 696 | callback && callback(err); 697 | }); 698 | } 699 | }); 700 | }; 701 | 702 | exports.blob_read = function(opt, id, callback) { 703 | 704 | if (typeof(id) === 'string') 705 | id = +id; 706 | 707 | createpool(opt).connect(function(err, client, done) { 708 | 709 | if (err) 710 | return callback(err); 711 | 712 | if (CANSTATS) 713 | F.stats.performance.dbrm++; 714 | 715 | client.query('BEGIN', function(err) { 716 | 717 | if (err) { 718 | done(); 719 | return callback(err); 720 | } 721 | 722 | if (CANSTATS) 723 | F.stats.performance.dbrm++; 724 | 725 | Lo.create(client).readStream(id, opt.buffersize || 16384, function(err, size, stream) { 726 | if (err) { 727 | client.query('COMMIT', done); 728 | callback(err); 729 | } else { 730 | var cb = () => client.query('COMMIT', done); 731 | stream.on('error', cb); 732 | stream.on('end', cb); 733 | callback(null, stream, { size: parseInt(size) }); 734 | } 735 | }); 736 | }); 737 | }); 738 | }; 739 | 740 | exports.blob_write = function(opt, stream, name, callback) { 741 | 742 | if (typeof(name) === 'function') { 743 | callback = name; 744 | name = undefined; 745 | } 746 | 747 | createpool(opt).connect(function(err, client, done) { 748 | 749 | if (err) 750 | return callback(err); 751 | 752 | if (CANSTATS) 753 | F.stats.performance.dbrm++; 754 | 755 | client.query('BEGIN', function(err) { 756 | 757 | if (err) { 758 | done(); 759 | return callback(err); 760 | } 761 | 762 | if (CANSTATS) 763 | F.stats.performance.dbwm++; 764 | 765 | Lo.create(client).writeStream(opt.buffersize || 16384, function(err, oid, writer) { 766 | 767 | if (err) { 768 | client.query('ROLLBACK', done); 769 | return callback(err); 770 | } 771 | 772 | writer.on('finish', function() { 773 | client.query('COMMIT', done); 774 | callback(null, oid.toString()); 775 | }); 776 | 777 | stream.pipe(writer); 778 | }); 779 | }); 780 | }); 781 | }; 782 | 783 | function prepare_owner(cmd, condition) { 784 | 785 | var tmp = []; 786 | 787 | if (cmd.member) { 788 | for (var i = 0; i < cmd.member.length; i++) 789 | tmp.push(ESCAPE(cmd.member[i])); 790 | } 791 | 792 | var addcondition = []; 793 | if (cmd.condition) { 794 | addcondition.push(''); 795 | if (typeof(cmd.condition) === 'string') { 796 | addcondition.push(val); 797 | } else { 798 | for (var key in cmd.condition) { 799 | var val = cmd.condition[key]; 800 | addcondition.push(key + (val == null ? ' IS ' : '=') + ESCAPE(val)); 801 | } 802 | } 803 | } 804 | 805 | // e.g. userid=ID OR (userid IN (ARR) (AND condition)) 806 | condition.push('(' + cmd.name + '=' + ESCAPE(cmd.value) + (tmp.length ? (' OR (' + cmd.name + ' IN (' + tmp.join(',') + ')' + (addcondition.length ? addcondition.join(' AND ') : '') + ')') : '') + ')'); 807 | } 808 | 809 | function WHERE(builder, scalar, group, params) { 810 | 811 | var condition = []; 812 | var sort = []; 813 | var tmp; 814 | var op = 'AND'; 815 | var opuse = false; 816 | var index = -1; 817 | var current; // temporary for "query" + "cmd.value" and "replace" method because of performance 818 | 819 | var replace = builder.options.params ? function(text) { 820 | var indexer = (+text.substring(1)) - 1; 821 | index = params.push(current[indexer]); 822 | return '$' + index; 823 | } : null; 824 | 825 | for (var i = 0; i < builder.$commands.length; i++) { 826 | var cmd = builder.$commands[i]; 827 | 828 | if (builder.options.islanguage && cmd.name && cmd.name[cmd.name.length - 1] === '§') 829 | cmd.name = cmd.name.substring(0, cmd.name.length - 1) + (builder.options.language || ''); 830 | 831 | switch (cmd.type) { 832 | case 'where': 833 | if (cmd.value === undefined) { 834 | opuse && condition.length && condition.push(op); 835 | condition.push(cmd.name); 836 | } else { 837 | tmp = ESCAPE(cmd.value); 838 | opuse && condition.length && condition.push(op); 839 | condition.push(cmd.name + ((cmd.value == null || tmp == 'null') && cmd.compare === '=' ? ' IS ' : cmd.compare) + tmp); 840 | } 841 | break; 842 | 843 | case 'owner': 844 | opuse && condition.length && condition.push(op); 845 | prepare_owner(cmd, condition); 846 | break; 847 | 848 | case 'custom': 849 | cmd.fn.call(builder, builder, builder.db.$output, builder.db.$lastoutput); 850 | break; 851 | 852 | case 'in': 853 | if (typeof(cmd.value) === 'function') 854 | cmd.value = cmd.value(); 855 | if (cmd.value instanceof Array) { 856 | tmp = []; 857 | for (var j = 0; j < cmd.value.length; j++) { 858 | var val = cmd.value[j]; 859 | if (val && cmd.field) 860 | val = val[cmd.field]; 861 | tmp.push(ESCAPE(val)); 862 | } 863 | opuse && condition.length && condition.push(op); 864 | condition.push(cmd.name + ' IN (' + (tmp.length ? tmp.join(',') : 'NULL') + ')'); 865 | } else { 866 | opuse && condition.length && condition.push(op); 867 | condition.push(cmd.name + '=' + ESCAPE(cmd.field ? cmd.value[cmd.field] : cmd.value)); 868 | } 869 | break; 870 | 871 | case 'notin': 872 | if (typeof(cmd.value) === 'function') 873 | cmd.value = cmd.value(); 874 | if (cmd.value instanceof Array) { 875 | tmp = []; 876 | for (var j = 0; j < cmd.value.length; j++) { 877 | var val = cmd.value[j]; 878 | if (val && cmd.field) 879 | val = val[cmd.field]; 880 | tmp.push(ESCAPE(val)); 881 | } 882 | opuse && condition.length && condition.push(op); 883 | condition.push(cmd.name + ' NOT IN (' + (tmp.length ? tmp.join(',') : 'NULL') + ')'); 884 | } else { 885 | opuse && condition.length && condition.push(op); 886 | condition.push(cmd.name + '<>' + ESCAPE(cmd.field ? cmd.value[cmd.field] : cmd.value)); 887 | } 888 | break; 889 | 890 | case 'between': 891 | opuse && condition.length && condition.push(op); 892 | condition.push('(' + cmd.name + '>=' + ESCAPE(cmd.a) + ' AND ' + cmd.name + '<=' + ESCAPE(cmd.b) + ')'); 893 | break; 894 | 895 | case 'search': 896 | tmp = ESCAPE((!cmd.compare || cmd.compare === '*' ? ('%' + cmd.value + '%') : (cmd.compare === 'beg' ? ('%' + cmd.value) : (cmd.value + '%')))); 897 | opuse && condition.length && condition.push(op); 898 | condition.push(cmd.name + ' ILIKE ' + tmp); 899 | break; 900 | 901 | case 'searchfull': 902 | tmp = ESCAPE('%' + cmd.value.toLowerCase().replace(/y/g, 'i') + '%'); 903 | opuse && condition.length && condition.push(op); 904 | condition.push('REPLACE(LOWER(to_tsvector(' + builder.options.table + '::text)::text), \'y\', \'i\') ILIKE ' + tmp); 905 | break; 906 | 907 | case 'searchall': 908 | tmp = ''; 909 | for (var j = 0; j < cmd.value.length; j++) 910 | tmp += (tmp ? ' AND ' : '') + cmd.name + ' ILIKE ' + ESCAPE('%' + cmd.value[j] + '%'); 911 | opuse && condition.length && condition.push(op); 912 | condition.push('(' + (tmp || '0=1') + ')'); 913 | break; 914 | 915 | case 'fulltext': 916 | tmp = ESCAPE('%' + cmd.value.toLowerCase() + '%'); 917 | opuse && condition.length && condition.push(op); 918 | condition.push('LOWER(' + cmd.name + ') ILIKE ' + tmp); 919 | break; 920 | case 'contains': 921 | opuse && condition.length && condition.push(op); 922 | condition.push('LENGTH(' + cmd.name +'::text)>0'); 923 | break; 924 | case 'query': 925 | opuse && condition.length && condition.push(op); 926 | if (cmd.value) 927 | current = cmd.value; 928 | condition.push('(' + (current == undefined ? cmd.query : cmd.query.replace(REG_PARAMS, replace)) + ')'); 929 | break; 930 | case 'permit': 931 | opuse && condition.length && condition.push(op); 932 | if (cmd.must) 933 | condition.push('(' + ((cmd.useridfield ? ('"' + cmd.useridfield + '"=' + pg_escape(cmd.userid) + ' OR ') : '') + '"' + cmd.name + '" && $' + params.push([cmd.value])) + ')'); 934 | else 935 | condition.push('(' + ((cmd.useridfield ? ('"' + cmd.useridfield + '"=' + pg_escape(cmd.userid) + ' OR ') : '') + 'array_length("' + cmd.name + '",1) IS NULL OR "' + cmd.name + '" && $' + params.push([cmd.value])) + ')'); 936 | break; 937 | case 'empty': 938 | opuse && condition.length && condition.push(op); 939 | condition.push('(' + cmd.name + ' IS NULL OR LENGTH(' + cmd.name + '::text)=0)'); 940 | break; 941 | case 'month': 942 | case 'year': 943 | case 'day': 944 | case 'hour': 945 | case 'minute': 946 | opuse && condition.length && condition.push(op); 947 | condition.push('EXTRACT(' + cmd.type + ' from ' + cmd.name + ')' + cmd.compare + ESCAPE(cmd.value)); 948 | break; 949 | case 'date': 950 | opuse && condition.length && condition.push(op); 951 | condition.push(cmd.name + '::date' + cmd.compare + (cmd.value instanceof Date ? ('\'' + cmd.value.format('yyyy-MM-dd') + '\'::date') : 'null')); 952 | break; 953 | case 'or': 954 | opuse && condition.length && condition.push(op); 955 | op = 'OR'; 956 | opuse = false; 957 | condition.push('('); 958 | continue; 959 | case 'end': 960 | condition.push(')'); 961 | op = 'AND'; 962 | break; 963 | case 'and': 964 | opuse && condition.length && condition.push(op); 965 | op = 'AND'; 966 | break; 967 | case 'sort': 968 | sort.push(cmd.name + ' ' + (cmd.desc ? 'DESC' : 'ASC')); 969 | break; 970 | case 'regexp': 971 | tmp = cmd.value.toString().substring(1); 972 | var g = '~'; 973 | if (tmp[tmp.length - 1] === 'i') { 974 | tmp = tmp.substring(0, tmp.length - 2); 975 | g = '~*'; 976 | } else 977 | tmp = tmp.substring(0, tmp.length - 1); 978 | opuse && condition.length && condition.push(op); 979 | condition.push(cmd.name + g + '\'' + tmp + '\''); 980 | break; 981 | } 982 | opuse = true; 983 | } 984 | 985 | var query = (condition.length ? (' WHERE ' + condition.join(' ')) : '') + (group ? (' GROUP BY ' + group) : ''); 986 | 987 | if (scalar) { 988 | builder.options.sort = sort; 989 | } else { 990 | if (sort.length) 991 | query += ' ORDER BY ' + sort.join(','); 992 | if (builder.options.skip && builder.options.take) 993 | query += ' LIMIT ' + builder.options.take + ' OFFSET ' + builder.options.skip; 994 | else if (builder.options.take) 995 | query += ' LIMIT ' + builder.options.take; 996 | else if (builder.options.skip) 997 | query += ' OFFSET ' + builder.options.skip; 998 | } 999 | 1000 | return query; 1001 | } 1002 | 1003 | function OFFSET(builder) { 1004 | var query = ''; 1005 | var sort = builder.options.sort || EMPTYARRAY; 1006 | if (sort.length) 1007 | query += ' ORDER BY ' + sort.join(','); 1008 | if (builder.options.skip && builder.options.take) 1009 | query += ' LIMIT ' + builder.options.take + ' OFFSET ' + builder.options.skip; 1010 | else if (builder.options.take) 1011 | query += ' LIMIT ' + builder.options.take; 1012 | else if (builder.options.skip) 1013 | query += ' OFFSET ' + builder.options.skip; 1014 | return query; 1015 | } 1016 | 1017 | function FIELDS(builder) { 1018 | 1019 | var output = ''; 1020 | var plus = ''; 1021 | var fields = builder.options.fields; 1022 | 1023 | if (fields && fields.length) { 1024 | for (var i = 0; i < fields.length; i++) { 1025 | var field = fields[i]; 1026 | if (field[0] === '-') { 1027 | 1028 | if (builder.options.fieldsrem) 1029 | builder.options.fieldsrem.push(field.substring(1)); 1030 | else 1031 | builder.options.fieldsrem = [field.substring(1)]; 1032 | 1033 | continue; 1034 | } 1035 | 1036 | if (builder.options.islanguage) { 1037 | if (field[field.length - 1] === '§') { 1038 | field = field.substring(0, field.length - 1); 1039 | field = (field + builder.options.language) + ' AS ' + field; 1040 | } 1041 | } 1042 | 1043 | output += (output ? ',' : '') + field; 1044 | } 1045 | if (output && builder.$joinmeta) 1046 | output += ',' + builder.$joinmeta.a; 1047 | } 1048 | 1049 | fields = builder.options.subquery; 1050 | if (fields && fields.length) { 1051 | for (var i = 0; i < fields.length; i++) 1052 | plus += ',' + (fields[i].name ? ('(' + fields[i].query + ') AS ' + fields[i].name) : fields[i].query); 1053 | } 1054 | 1055 | return (output ? output : '*') + plus; 1056 | } 1057 | 1058 | // Author: https://github.com/segmentio/pg-escape 1059 | // License: MIT 1060 | function pg_escape(val) { 1061 | if (val == null) 1062 | return 'NULL'; 1063 | var backslash = ~val.indexOf('\\'); 1064 | var prefix = backslash ? 'E' : ''; 1065 | val = val.replace(REG_ESCAPE_1, '\'\'').replace(REG_ESCAPE_2, '\\\\'); 1066 | return prefix + '\'' + val + '\''; 1067 | } 1068 | 1069 | function dateToString(dt) { 1070 | 1071 | var arr = []; 1072 | 1073 | arr.push(dt.getFullYear().toString()); 1074 | arr.push((dt.getMonth() + 1).toString()); 1075 | arr.push(dt.getDate().toString()); 1076 | arr.push(dt.getHours().toString()); 1077 | arr.push(dt.getMinutes().toString()); 1078 | arr.push(dt.getSeconds().toString()); 1079 | 1080 | for (var i = 1, length = arr.length; i < length; i++) { 1081 | if (arr[i].length === 1) 1082 | arr[i] = '0' + arr[i]; 1083 | } 1084 | 1085 | return arr[0] + '-' + arr[1] + '-' + arr[2] + ' ' + arr[3] + ':' + arr[4] + ':' + arr[5]; 1086 | } 1087 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Node Database Management System (ORM) 2 | 3 | 4 | - [__Documentation__](https://docs.totaljs.com/dbms/) 5 | - [Website](https://www.totaljs.com/) 6 | - [__Documentation__](https://docs.totaljs.com/total4/) 7 | - [Chat support](https://platform.totaljs.com/?open=messenger) 8 | - [Join __Total.js Telegram__](https://t.me/totalplatform) 9 | - [Support](https://www.totaljs.com/support/) 10 | - supports `PostgreSQL`, `MySQL`, Total.js `TextDB` and part of `MongoDB` 11 | 12 | ## Initialization 13 | 14 | - installation `$ npm install dbms` 15 | - install MySQL: `$ npm install mysql2` 16 | - install PostgreSQL: `$ npm install pg` 17 | - install MongoDB: `$ npm install mongodb` 18 | 19 | ```js 20 | const dbms = require('dbms'); 21 | 22 | dbms.init([alias], connection_string); 23 | // @alias {String} Optional, alias for connection string (default: 'default') 24 | // @connection_string {String} A connection string to DB 25 | 26 | // PostgreSQL 27 | dbms.init('postgresql://user:pass@localhost:5432/dbname'); 28 | dbms.init('mypg', 'postgresql://user:pass@localhost:5432/dbname'); // with a name for more DB engines 29 | 30 | // MySQL & Maria DB 31 | dbms.init('mysql://user:pass@localhost:3306/dbname'); 32 | dbms.init('mysql', 'mysql://user:pass@localhost:3306/dbname'); // with a name for more DB engines 33 | 34 | // Total.js NoSQL embedded 35 | dbms.init('nosql'); 36 | dbms.init('mynosql', 'nosql'); // with a name for more DB engines 37 | 38 | // Total.js Table 39 | dbms.init('table'); 40 | dbms.init('mytable', 'nosql'); // with a name for more DB engines 41 | ``` 42 | 43 | ## Usage 44 | 45 | ```js 46 | // Is a global method 47 | var db = DBMS(); 48 | 49 | // Finds records 50 | // A response: Array 51 | // returns QueryBuilder 52 | db.find('collection_table_name'); 53 | db.find('mypg/collection_table_name'); 54 | db.find('mynosql/collection_table_name'); 55 | db.find('mytable/collection_table_name'); 56 | 57 | // Finds the one record 58 | // A response: Object 59 | // returns QueryBuilder 60 | db.one('collection_table_name'); 61 | db.one('mypg/collection_table_name'); 62 | db.one('mynosql/collection_table_name'); 63 | db.one('mytable/collection_table_name'); 64 | 65 | // Inserts a new record 66 | // A response: Number 67 | // returns QueryBuilder 68 | db.insert('collection_table_name', document, [unique]); 69 | db.insert('mypg/collection_table_name', document, [unique]); 70 | db.insert('mynosql/collection_table_name', document, [unique]); 71 | db.insert('mytable/collection_table_name', document, [unique]); 72 | ``` 73 | 74 | ## Contact 75 | 76 | - Contact 77 | - [Chat support](https://platform.totaljs.com/?open=messenger) 78 | - [Join to __Total.js Telegram__](https://t.me/totalplatform) 79 | -------------------------------------------------------------------------------- /textdb.js: -------------------------------------------------------------------------------- 1 | const BLACKLIST = { dbms: 1 }; 2 | const ISOP = { '+': 1, '-': 1, '*': 1, '/': 1, '=': 1, '!': 1, '#': 1 }; 3 | const TextDB = require('total4/textdb-new'); 4 | 5 | var INSTANCES = {}; 6 | 7 | function select(client, cmd) { 8 | 9 | var builder = cmd.builder; 10 | var opt = builder.options; 11 | var filter = WHERE(builder, null, null); 12 | var fields = FIELDS(builder); 13 | 14 | // opt.table 15 | var data = {}; 16 | 17 | if (fields) 18 | data.fields = fields; 19 | 20 | data.filter = filter.filter; 21 | data.filterarg = { arg: filter.arg }; 22 | 23 | if (filter.sort) 24 | data.sort = filter.sort; 25 | 26 | if (filter.take) 27 | data.take = filter.take; 28 | 29 | if (filter.skip) 30 | data.skip = filter.skip; 31 | 32 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 33 | 34 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table] = TextDB.TextDB(PATH.databases(opt.table))); 35 | 36 | conn.find().assign(data).callback(function(err, response, meta) { 37 | 38 | builder.db.busy = false; 39 | 40 | var rows = response; 41 | err && client.$opt.onerror && client.$opt.onerror(err, data); 42 | 43 | if (opt.first) 44 | rows = rows[0] || null; 45 | 46 | // checks joins 47 | if (!err && builder.$joins) { 48 | client.$dbms._joins(rows, builder); 49 | setImmediate(builder.db.$next); 50 | } else 51 | builder.$callback(err, rows, meta.count); 52 | }); 53 | } 54 | 55 | function check(client, cmd) { 56 | var builder = cmd.builder; 57 | var opt = builder.options; 58 | var filter = WHERE(builder); 59 | var data = {}; 60 | 61 | data.filter = filter.filter; 62 | data.filterarg = { arg: filter.arg }; 63 | data.take = 1; 64 | data.limit = 1; 65 | data.first = true; 66 | 67 | if (!cmd.value && builder.options.params) 68 | cmd.value = []; 69 | 70 | builder.db.$debug && builder.db.$debug(data); 71 | F.$events.dbms && EMIT('dbms', 'select', opt.table, opt.db, builder); 72 | 73 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table] = TextDB.TextDB(PATH.databases(opt.table))); 74 | 75 | conn.find().assign(data).callback(function(err, response, meta) { 76 | builder.db.busy = false; 77 | var is = !err && !!response; 78 | err && client.$opt.onerror && client.$opt.onerror(err, data); 79 | builder.$callback(err, is, meta.count); 80 | }); 81 | } 82 | 83 | function query(client, cmd) { 84 | var builder = cmd.builder; 85 | var opt = builder.options; 86 | var filter = WHERE(builder); 87 | var data = {}; 88 | 89 | data.filter = filter.filter; 90 | data.filterarg = { arg: filter.arg }; 91 | data.scalar = cmd.query; 92 | data.scalararg = cmd.value || {}; 93 | 94 | builder.db.$debug && builder.db.$debug(data); 95 | F.$events.dbms && EMIT('dbms', 'query', opt.table, opt.db, builder); 96 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table || opt.db] = TextDB.TextDB(PATH.databases(opt.table || opt.db))); 97 | 98 | conn.find().assign(data).callback(function(err, response, meta) { 99 | builder.db.busy = false; 100 | err && client.$opt.onerror && client.$opt.onerror(err, data); 101 | builder.$callback(err, response, meta.count); 102 | }); 103 | } 104 | 105 | function scalar(client, cmd) { 106 | 107 | var builder = cmd.builder; 108 | var opt = builder.options; 109 | var scalar = ''; 110 | var data = {}; 111 | var filter = WHERE(builder); 112 | 113 | data.filter = filter.filter; 114 | data.filterarg = { arg: filter.arg }; 115 | data.scalar = scalar; 116 | data.scalararg = {}; 117 | 118 | var name = cmd.name; 119 | if (name) { 120 | var index = name.indexOf(' as '); 121 | if (index !== -1) { 122 | name = name.substring(index + 4); 123 | cmd.name = cmd.name.substring(0, index); 124 | } 125 | } 126 | 127 | switch (cmd.scalar) { 128 | case 'group': 129 | data.scalar = cmd.field ? ('var k=doc.' + cmd.name + '+\'\';if (arg[k]){arg[k]+=doc.' + cmd.field + '||0}else{arg[k]=doc.' + cmd.field + '||0}') : ('var k=doc.' + cmd.name + '+\'\';if (arg[k]){arg[k]++}else{arg[k]=1}'); 130 | break; 131 | default: 132 | if (cmd.field) { 133 | data.scalar = 'var k=doc.' + cmd.name + '+\'\';if (arg[k]){tmp.bk=doc.' + cmd.field + '||0;' + (cmd.scalar === 'max' ? 'if(tmp.bk>arg[k])arg[k]=tmp.bk' : cmd.scalar === 'min' ? 'if(tmp.bktmp.val?tmp.val:arg.min;arg.max=arg.max==null?tmp.val:arg.max': 214 | case '<': 215 | key = key.substring(1); 216 | break; 217 | case '=': 218 | key = key.substring(1); 219 | break; 220 | case '#': 221 | break; 222 | case '!': 223 | // toggle 224 | key = key.substring(1); 225 | if (val) 226 | val = true; 227 | else 228 | val = false; 229 | break; 230 | } 231 | 232 | doc[key] = val == null ? null : typeof(val) === 'function' ? val(builder.value) : val; 233 | } 234 | 235 | // builder.db.$debug && builder.db.$debug(q); 236 | F.$events.dbms && EMIT('dbms', 'insert', opt.table, opt.db, builder); 237 | 238 | var data = {}; 239 | data.payload = doc; 240 | 241 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table] = TextDB.TextDB(PATH.databases(opt.table))); 242 | 243 | conn.insert().assign(data).callback(function(err, response) { 244 | builder.db.busy = false; 245 | err && client.$opt.onerror && client.$opt.onerror(err, data); 246 | builder.$callback(err, err == null ? response : 0); 247 | }); 248 | } 249 | 250 | function insertexists(client, cmd) { 251 | 252 | var builder = cmd.builder; 253 | var opt = builder.options; 254 | var filter = WHERE(cmd.builder); 255 | var data = {}; 256 | 257 | data = {}; 258 | data.take = 1; 259 | data.filter = filter.filter; 260 | data.filterarg = { arg: filter.arg }; 261 | data.limit = 1; 262 | data.first = true; 263 | 264 | F.$events.dbms && EMIT('dbms', 'select', opt.table, data, builder); 265 | 266 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table] = TextDB.TextDB(PATH.databases(opt.table))); 267 | 268 | conn.find().assign(data).callback(function(err, response) { 269 | builder.db.busy = false; 270 | err && client.$opt.onerror && client.$opt.onerror(err, data); 271 | if (response) 272 | builder.$callback(err, 0); 273 | else 274 | insert(client, cmd); 275 | }); 276 | } 277 | 278 | function modify(client, cmd) { 279 | 280 | cmd.builder.options.transform && cmd.builder.options.transform(cmd.builder.value, cmd.builder.db.$output, cmd.builder.db.$lastoutput); 281 | 282 | var keys = Object.keys(cmd.builder.value); 283 | var params = []; 284 | var arr = []; 285 | var builder = []; 286 | var tmp; 287 | for (var i = 0; i < keys.length; i++) { 288 | var key = keys[i]; 289 | var val = cmd.builder.value[key]; 290 | 291 | if (val === undefined || BLACKLIST[key]) 292 | continue; 293 | 294 | if (cmd.builder.options.equal && cmd.builder.options.equal.indexOf(key) !== -1) 295 | continue; 296 | 297 | if (cmd.builder.options.fields && cmd.builder.options.fields.length) { 298 | var skip = true; 299 | for (var j = 0; j < cmd.builder.options.fields.length; j++) { 300 | var field = cmd.builder.options.fields[j]; 301 | if (field[0] === '-') { 302 | field = field.substring(1); 303 | if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 304 | skip = true; 305 | break; 306 | } 307 | skip = false; 308 | } else if (field === key || (ISOP[key[0]] && field === key.substring(1))) { 309 | skip = false; 310 | break; 311 | } else 312 | skip = false; 313 | } 314 | 315 | if (skip) 316 | continue; 317 | } 318 | 319 | var c = key[0]; 320 | 321 | if (typeof(val) === 'function') 322 | val = val(cmd.builder.value); 323 | 324 | switch (c) { 325 | case '-': 326 | case '+': 327 | case '*': 328 | case '/': 329 | key = key.substring(1); 330 | params.push(val ? val : 0); 331 | builder.push('doc.' + key + '=(doc.' + key + '||0)' + c + push(arr, val ? val : 0)); 332 | break; 333 | case '>': 334 | case '<': 335 | tmp = push(arr, val ? val : 0); 336 | key = key.substring(1); 337 | builder.push('doc.' + key + '=(doc.' + key + '||0)' + c + tmp + '?(doc.' + key + '||0):' + tmp); 338 | break; 339 | case '!': 340 | // toggle 341 | key = key.substring(1); 342 | builder.push('doc.' + key + '=!doc.' + key); 343 | break; 344 | case '=': 345 | case '#': 346 | // raw 347 | builder.push('doc.' + key + '=' + val); 348 | break; 349 | default: 350 | builder.push('doc.' + key + '=' + push(arr, val)); 351 | break; 352 | } 353 | } 354 | 355 | var opt = cmd.builder.options; 356 | 357 | if (opt.equal) { 358 | for (var i = 0; i < opt.equal.length; i++) 359 | cmd.builder.where(opt.equal[i], builder.value[opt.equal[i]]); 360 | } 361 | 362 | var filter = WHERE(cmd.builder); 363 | var data = {}; 364 | 365 | data.filter = filter.filter; 366 | data.filterarg = { arg: filter.arg }; 367 | data.modify = builder.join(';'); 368 | data.modifyarg = { arg: arr }; 369 | 370 | if (filter.take) 371 | data.take = filter.take; 372 | 373 | if (filter.skip) 374 | data.skip = filter.skip; 375 | 376 | cmd.builder.db.$debug && cmd.builder.db.$debug(data); 377 | F.$events.dbms && EMIT('dbms', 'update', data, builder); 378 | 379 | data.db = opt.table; 380 | 381 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table] = TextDB.TextDB(PATH.databases(opt.table))); 382 | 383 | conn.update().assign(data).callback(function(err, response, meta) { 384 | cmd.builder.db.busy = false; 385 | err && client.$opt.onerror && client.$opt.onerror(err, data); 386 | if (!response && cmd.insert) { 387 | if (cmd.insert !== true) 388 | cmd.builder.value = cmd.insert; 389 | cmd.builder.options.insert && cmd.builder.options.insert(cmd.builder.value, cmd.builder.options.insertparams); 390 | insert(client, cmd); 391 | } else 392 | cmd.builder.$callback(err, response, meta.count); 393 | }); 394 | } 395 | 396 | function remove(client, cmd) { 397 | var builder = cmd.builder; 398 | var opt = cmd.builder.options; 399 | var filter = WHERE(builder); 400 | var data = {}; 401 | data.filter = filter.filter; 402 | data.filterarg = { arg: filter.arg }; 403 | 404 | if (filter.take) 405 | data.take = filter.take; 406 | 407 | if (filter.skip) 408 | data.skip = filter.skip; 409 | 410 | builder.db.$debug && builder.db.$debug(data); 411 | F.$events.dbms && EMIT('dbms', 'delete', opt.table, opt.db, builder); 412 | 413 | var conn = INSTANCES[opt.table] || (INSTANCES[opt.table] = TextDB.TextDB(PATH.databases(opt.table))); 414 | 415 | conn.remove().assign(data).callback(function(err, response, meta) { 416 | cmd.builder.db.busy = false; 417 | err && client.$opt.onerror && client.$opt.onerror(err, data); 418 | cmd.builder.$callback(err, response, meta.count); 419 | }); 420 | } 421 | 422 | function clientcommand(cmd, client) { 423 | switch (cmd.type) { 424 | case 'transaction': 425 | case 'end': 426 | case 'commit': 427 | case 'rollback': 428 | break; 429 | case 'find': 430 | case 'read': 431 | case 'list': 432 | select(client, cmd); 433 | break; 434 | case 'check': 435 | check(client, cmd); 436 | break; 437 | case 'scalar': 438 | scalar(client, cmd); 439 | break; 440 | case 'insert': 441 | if (cmd.unique) 442 | insertexists(client, cmd); 443 | else 444 | insert(client, cmd); 445 | break; 446 | 447 | case 'modify2': 448 | var cb = cmd.builder.$callback; 449 | cmd.builder.$callback = function(err, response) { 450 | cmd.builder.options.fields = null; 451 | if (err) { 452 | cb.call(cmd.builder, err, 0); 453 | } else if (response) { 454 | var mod = cmd.fn(response); 455 | if (mod) { 456 | cmd.builder.value = mod; 457 | cmd.builder.$callback = cb; 458 | if (cmd.builder.value.$clean) 459 | cmd.builder.value = cmd.builder.value.$clean(); 460 | modify(client, cmd); 461 | } else 462 | cb.call(cmd.builder, err, 0); 463 | } else { 464 | if (cmd.insert) { 465 | mod = cmd.fn(null); 466 | if (mod) { 467 | cmd.builder.value = mod; 468 | cmd.builder.$callback = cb; 469 | insert(client, cmd); 470 | } else 471 | cb.call(cmd.builder, err, 0); 472 | } else { 473 | cb.call(cmd.builder, err, 0); 474 | } 475 | } 476 | }; 477 | select(client, cmd); 478 | break; 479 | 480 | case 'update': 481 | case 'modify': 482 | modify(client, cmd); 483 | break; 484 | case 'remove': 485 | remove(client, cmd); 486 | break; 487 | case 'query': 488 | query(client, cmd); 489 | break; 490 | default: 491 | cmd.builder.$callback(new Error('Operation "' + cmd.type + '" not found')); 492 | break; 493 | } 494 | } 495 | 496 | exports.run = function(opt, self, cmd) { 497 | self.$op = null; 498 | self.busy = true; 499 | self.$opt = opt; 500 | clientcommand(cmd, opt); 501 | }; 502 | 503 | function push(arr, value) { 504 | return 'arg.arg[' + (arr.push(value) - 1) + ']'; 505 | } 506 | 507 | function WHERE(builder) { 508 | 509 | var condition = []; 510 | var sort = ''; 511 | var op = '&&'; 512 | var opuse = false; 513 | var arg = []; 514 | 515 | for (var i = 0; i < builder.$commands.length; i++) { 516 | var cmd = builder.$commands[i]; 517 | 518 | if (builder.options.islanguage && cmd.name && cmd.name[cmd.name.length - 1] === '§') 519 | cmd.name = cmd.name.substring(0, cmd.name.length - 1) + (builder.options.language || ''); 520 | 521 | switch (cmd.type) { 522 | case 'where': 523 | opuse && condition.length && condition.push(op); 524 | if (cmd.compare === '<>') 525 | cmd.compare = '!='; 526 | else if (cmd.compare === '=') 527 | cmd.compare = '=='; 528 | 529 | if (cmd.value === undefined) 530 | condition.push(cmd.name); 531 | else { 532 | var tmp = push(arg, cmd.value); 533 | condition.push('doc.' + cmd.name + ' instanceof Array?(doc.' + cmd.name + '.indexOf(' + tmp + ')' + (cmd.compare === '==' ? '!=' : '==') + '-1):(doc.' + cmd.name + cmd.compare + tmp + ')'); 534 | } 535 | 536 | break; 537 | case 'custom': 538 | cmd.fn.call(builder, builder, builder.db.$output, builder.db.$lastoutput); 539 | break; 540 | case 'in': 541 | 542 | if (typeof(cmd.value) === 'function') 543 | cmd.value = cmd.value(); 544 | 545 | if (cmd.value instanceof Array) { 546 | 547 | if (cmd.field) { 548 | var tmp = []; 549 | for (var j = 0; j < cmd.value.length; j++) { 550 | if (cmd.value[j]) 551 | tmp.push(cmd.value[j][cmd.field]); 552 | } 553 | cmd.value = tmp; 554 | } 555 | 556 | opuse && condition.length && condition.push(op); 557 | condition.push(push(arg, cmd.value) + '.indexOf(doc.' + cmd.name + ')!==-1'); 558 | } else { 559 | opuse && condition.length && condition.push(op); 560 | condition.push('doc.' + cmd.name + '==' + push(arg, cmd.field ? cmd.value[cmd.field] : cmd.value)); 561 | } 562 | break; 563 | case 'notin': 564 | 565 | if (typeof(cmd.value) === 'function') 566 | cmd.value = cmd.value(); 567 | 568 | if (cmd.value instanceof Array) { 569 | 570 | if (cmd.field) { 571 | var tmp = []; 572 | for (var j = 0; j < cmd.value.length; j++) { 573 | if (cmd.value[j]) 574 | tmp.push(cmd.value[j][cmd.field]); 575 | } 576 | cmd.value = tmp; 577 | } 578 | 579 | opuse && condition.length && condition.push(op); 580 | condition.push(push(arg, cmd.value) + '.indexOf(doc.' + cmd.name + ')===-1'); 581 | } else { 582 | opuse && condition.length && condition.push(op); 583 | condition.push('doc.' + cmd.name + '!=' + push(arg, cmd.field ? cmd.value[cmd.field] : cmd.value)); 584 | } 585 | 586 | break; 587 | case 'between': 588 | opuse && condition.length && condition.push(op); 589 | condition.push('(doc.' + cmd.name + '>=' + push(arg, cmd.a) + '&&doc.' + cmd.name + '<=' + push(arg, cmd.b) + ')'); 590 | break; 591 | case 'search': 592 | // tmp = ESCAPE((!cmd.compare || cmd.compare === '*' ? ('%' + cmd.value + '%') : (cmd.compare === 'beg' ? ('%' + cmd.value) : (cmd.value + '%')))); 593 | opuse && condition.length && condition.push(op); 594 | condition.push('doc.' + cmd.name + '.indexOf(' + push(arg, cmd.value) + ')!==-1'); 595 | break; 596 | 597 | case 'searchfull': 598 | // tmp = ESCAPE('%' + cmd.value.toLowerCase().replace(/y/g, 'i') + '%'); 599 | // opuse && condition.length && condition.push(op); 600 | // condition.push('REPLACE(LOWER(to_tsvector(' + builder.options.table + '::text)::text), \'y\', \'i\') ILIKE ' + tmp); 601 | break; 602 | 603 | case 'searchall': 604 | // tmp = ''; 605 | // for (var j = 0; j < cmd.value.length; j++) 606 | // tmp += (tmp ? ' AND ' : '') + cmd.name + ' ILIKE ' + ESCAPE('%' + cmd.value[j] + '%'); 607 | // opuse && condition.length && condition.push(op); 608 | // condition.push('(' + (tmp || '0=1') + ')'); 609 | break; 610 | 611 | case 'fulltext': 612 | // tmp = ESCAPE('%' + cmd.value.toLowerCase() + '%'); 613 | // opuse && condition.length && condition.push(op); 614 | // condition.push('LOWER(' + cmd.name + ') ILIKE ' + tmp); 615 | break; 616 | case 'contains': 617 | opuse && condition.length && condition.push(op); 618 | condition.push('!!doc.' + cmd.name); 619 | break; 620 | case 'query': 621 | opuse && condition.length && condition.push(op); 622 | condition.push(cmd.query); 623 | break; 624 | case 'permit': 625 | break; 626 | case 'empty': 627 | opuse && condition.length && condition.push(op); 628 | condition.push('!doc.' + cmd.name); 629 | break; 630 | case 'month': 631 | case 'year': 632 | case 'day': 633 | case 'hour': 634 | case 'minute': 635 | opuse && condition.length && condition.push(op); 636 | var type = cmd.type === 'month' ? 'Month' : cmd.type === 'year' ? 'FullYear' : cmd.type === 'day' ? 'Date' : cmd.type === 'minute' ? 'Minutes' : cmd.type === 'hours' ? 'Hours' : 'Seconds'; 637 | condition.push('doc.' + cmd.name + ' instanceof Date?(doc.' + cmd.name + '.get' + type + '()===' + cmd.value + '):false'); 638 | break; 639 | case 'date': 640 | opuse && condition.length && condition.push(op); 641 | condition.push(cmd.value instanceof Date ? ('doc.' + cmd.name + ' instanceof Date?(doc.' + cmd.name + '.getDate()===' + cmd.value.getDate() + '&&doc.' + cmd.name + '.getMonth()===' + cmd.value.getMonth() + '&&doc.' + cmd.name + '.getFullYear()===' + cmd.value.getFullYear() + '):false') : ('!doc.' + cmd.name)); 642 | break; 643 | case 'or': 644 | opuse && condition.length && condition.push(op); 645 | op = '||'; 646 | opuse = false; 647 | condition.push('('); 648 | continue; 649 | case 'end': 650 | condition.push(')'); 651 | op = '&&'; 652 | break; 653 | case 'and': 654 | opuse && condition.length && condition.push(op); 655 | op = '&&'; 656 | break; 657 | case 'sort': 658 | sort = cmd.name + '_' + (cmd.desc ? 'desc' : 'asc'); 659 | break; 660 | case 'regexp': 661 | // tmp = cmd.value.toString().substring(1); 662 | // var g = '~'; 663 | // if (tmp[tmp.length - 1] === 'i') { 664 | // tmp = tmp.substring(0, tmp.length - 2); 665 | // g = '~*'; 666 | // } else 667 | // tmp = tmp.substring(0, tmp.length - 1); 668 | // opuse && condition.length && condition.push(op); 669 | // condition.push(cmd.name + g + '\'' + tmp + '\''); 670 | break; 671 | } 672 | opuse = true; 673 | } 674 | 675 | // var query = (condition.length ? (' WHERE ' + condition.join(' ')) : '') + (group ? (' GROUP BY ' + group) : ''); 676 | return { filter: condition.length ? condition.join('') : 'true', arg: arg, sort: sort, take: builder.options.take, skip: builder.options.skip }; 677 | } 678 | 679 | function FIELDS(builder) { 680 | var fields = builder.options.fields || ''; 681 | return fields + (fields && fields.length && builder.$joinmeta ? (',' + builder.$joinmeta.a) : ''); 682 | } 683 | -------------------------------------------------------------------------------- /total.js: -------------------------------------------------------------------------------- 1 | !global.F && require('total.js'); 2 | 3 | exports.run = function(opt, self, cmd) { 4 | 5 | var db = opt.type === 'nosql' ? NOSQL : TABLE; 6 | 7 | switch (cmd.type) { 8 | case 'find': 9 | case 'read': 10 | WHERE(db(cmd.builder.options.table).find(), cmd.builder); 11 | break; 12 | case 'list': 13 | WHERE(db(cmd.builder.options.table).listing(), cmd.builder); 14 | break; 15 | case 'count': 16 | WHERE(db(cmd.builder.options.table).count(), cmd.builder); 17 | break; 18 | case 'scalar': 19 | WHERE(db(cmd.builder.options.table).scalar(), cmd.builder); 20 | break; 21 | case 'insert': 22 | WHERE(db(cmd.builder.options.table).insert(cmd.builder.value, cmd.unique), cmd.builder); 23 | break; 24 | case 'update': 25 | WHERE(db(cmd.builder.options.table).update(cmd.builder.value, cmd.insert), cmd.builder); 26 | break; 27 | case 'modify': 28 | WHERE(db(cmd.builder.options.table).modify(cmd.builder.value, cmd.insert), cmd.builder); 29 | break; 30 | case 'remove': 31 | WHERE(db(cmd.builder.options.table).remove(), cmd.builder); 32 | break; 33 | default: 34 | cmd.builder.$callback(new Error('Operation "' + cmd.type + '" not found')); 35 | break; 36 | } 37 | }; 38 | 39 | function WHERE(db, builder) { 40 | 41 | builder.options.fields && db.fields.apply(db, builder.options.fields); 42 | 43 | for (var i = 0; i < builder.$commands.length; i++) { 44 | var cmd = builder.$commands[i]; 45 | 46 | if (typeof(cmd.value) === 'function') 47 | cmd.value = cmd.value(); 48 | 49 | switch (cmd.type) { 50 | case 'where': 51 | db.where(cmd.name, cmd.compare, cmd.value); 52 | break; 53 | case 'in': 54 | db.in(cmd.name, cmd.value); 55 | break; 56 | case 'notin': 57 | db.notin(cmd.name, cmd.value); 58 | break; 59 | case 'code': 60 | db.code(cmd.value); 61 | break; 62 | case 'between': 63 | if (typeof(cmd.a) === 'function') 64 | cmd.a = cmd.a(); 65 | if (typeof(cmd.b) === 'function') 66 | cmd.b = cmd.b(); 67 | db.between(cmd.name, cmd.a, cmd.b); 68 | break; 69 | case 'search': 70 | db.search(cmd.name, cmd.value, cmd.compare); 71 | break; 72 | case 'fulltext': 73 | db.fulltext(cmd.name, cmd.value, cmd.weight); 74 | break; 75 | case 'contains': 76 | db.contains(cmd.name); 77 | break; 78 | case 'empty': 79 | db.empty(cmd.name); 80 | break; 81 | case 'year': 82 | db.year(cmd.name, cmd.compare, cmd.value); 83 | break; 84 | case 'month': 85 | db.month(cmd.name, cmd.compare, cmd.value); 86 | break; 87 | case 'day': 88 | db.day(cmd.name, cmd.compare, cmd.value); 89 | break; 90 | case 'or': 91 | db.or(); 92 | break; 93 | case 'end': 94 | db.end(); 95 | break; 96 | case 'and': 97 | db.and(); 98 | break; 99 | case 'sort': 100 | db.sort(cmd.name, cmd.desc); 101 | break; 102 | case 'regexp': 103 | db.regexp(cmd.name, cmd.value); 104 | break; 105 | } 106 | } 107 | 108 | builder.options.skip && db.skip(builder.options.skip); 109 | builder.options.take && db.take(builder.options.take); 110 | builder.options.first && db.first(); 111 | db.callback(function(err, response, count) { 112 | builder.$callback(err, response, count); 113 | }); 114 | } --------------------------------------------------------------------------------