1 var Client = require("mysql").Client, 2 comb = require("comb"), 3 Promise = comb.Promise, 4 PromiseList = comb.PromiseList, 5 hitch = comb.hitch, 6 mysqlTypes = require("./types/mysql"), 7 Table = require("../table").Table, 8 MySQLClient = require("./clients/mysql"), 9 SQL = require("./sql"); 10 11 /* 12 MYSQL query adapter to convert a query into mysql syntax; 13 * */ 14 15 var conditionedJoinTypes = { 16 innerJoin : "inner join", 17 fullOuterJoin : "full outer join", 18 rightOuterJoin : "right outer join", 19 leftOuterJoin : "left outer join", 20 fullJoin : "full join", 21 rightJoin : "right join", 22 leftJoin : "left join" 23 }; 24 25 /* These symbols have _join methods created (e.g. natural_join) that 26 call join_table with the symbol. They only accept a single table 27 argument which is passed to join_table, and they raise an error 28 if called with a block. */ 29 var unConditionedJoinTypes = { 30 naturalJoin : "natural join", 31 naturalLeftJoin : "natural left join", 32 naturalRightJoin : "natural right join", 33 naturalFullJoin : "natural full join", 34 crossJoin : "cross join" 35 }; 36 37 // All methods that return modified datasets with a joined table added. 38 var joinMethods = {}; 39 for (var i in conditionedJoinTypes) { 40 joinMethods[i] = conditionedJoinTypes[i]; 41 } 42 for (i in unConditionedJoinTypes) { 43 joinMethods[i] = unConditionedJoinTypes[i]; 44 } 45 46 var inOperator = { 47 "in" : "in", 48 notIn : "not in" 49 }; 50 51 var betweenOperator = { 52 between : "between", 53 notBetween : "not between" 54 }; 55 56 var booleanOperator = { 57 gt : ">", 58 gte : ">=", 59 lt : "<", 60 lte : "<=", 61 eq : "=", 62 neq : "!=" 63 }; 64 65 var logicalOperator = { 66 and : "and", 67 or : "or" 68 }; 69 70 var isOperator = { 71 is : "is", 72 isNot : "is not", 73 isNull : "is null", 74 isNotNull : "is not null" 75 }; 76 77 78 var aggregateOperator = { 79 count : "count", 80 sum : "sum", 81 avg : "avg", 82 min : "min", 83 max : "max", 84 bitAnd : "bit_and", 85 bitOr : "bit_or", 86 bitXor : "bit_xor", 87 std : "std", 88 stdDevPop : "stddev_pop", 89 stdDevSamp : "stddev_samp", 90 stdDev : "stddev", 91 varPop : "var_pop", 92 varSamp : "var_samp", 93 variance : "variance" 94 }; 95 96 var likeOperator = { 97 like : "like", 98 notLike : "not like" 99 }; 100 101 var transformBooleanOperator = function(val) { 102 if (typeof val == "boolean") { 103 val = val.toString(); 104 } else if (val == "unknown") { 105 } else if (val == null) { 106 val = "null"; 107 } else { 108 throw new Error(val + " is not a boolean type"); 109 } 110 return val; 111 }; 112 113 var addIsFunction = function(oper, object, callback) { 114 object[oper] = function(options) { 115 if ((oper == "is" || oper == "isNot") && typeof options != "object") throw new Error("options must be an object {key : value} when using " + oper); 116 if ((oper == "isNull" || oper == "isNotNull") && typeof options != "string") throw new Error("options must be a string <columnName> when using " + oper); 117 return callback.apply(object, [oper, options]); 118 }; 119 }; 120 121 var addJoinFunction = function(oper, object, callback) { 122 object[oper] = function(table, options) { 123 return callback.apply(object, [oper, table, options]); 124 }; 125 }; 126 127 var addGroupFunction = function(oper, object, callback) { 128 var name = "groupAnd" + oper.charAt(0).toUpperCase() + oper.substr(1); 129 object[name] = function(key, options) { 130 object.group(key, options); 131 return callback.apply(object, [oper, key]); 132 }; 133 addFunction(oper, object, callback); 134 }; 135 136 var addFunction = function(oper, object, callback) { 137 object[oper] = function(options) { 138 return callback.apply(object, [oper, options]); 139 }; 140 }; 141 142 143 var createInsertStatement = function(table, object, db) { 144 var sql = "INSERT INTO " + table + " "; 145 var values = [], columns = []; 146 for (var i in object) { 147 columns.push(i); 148 values.push(object[i]); 149 } 150 sql += "(" + columns.join(",") + ") VALUES "; 151 sql += SQL.format("(" + values.map( 152 function() { 153 return "?"; 154 }).join(",") + ");", values); 155 return sql; 156 }; 157 158 /** 159 * @class mysql implementaton of {@link SQL} this class exposes all MySql relevant functionality. 160 * <p>You should not have to use this class directly, but will be exposed for you by specifying the type when initializing moose.createConnection</p> 161 * 162 * @name mysql 163 * @augments SQL 164 * @memberOf moose.adapters 165 */ 166 var Mysql = (module.exports = exports = comb.define(SQL, { 167 instance : { 168 169 sqlObject : null, 170 171 _needLogic : false, 172 173 table : null, 174 175 db : null, 176 177 constructor: function(table, db) { 178 this.super(arguments); 179 //override all super methods for operators 180 for (var i in isOperator) { 181 182 addIsFunction(i, this, this._isOp); 183 } 184 for (i in logicalOperator) { 185 addFunction(i, this, this._logicalOp); 186 } 187 for (i in booleanOperator) { 188 addFunction(i, this, this._booleanOp); 189 } 190 for (i in conditionedJoinTypes) { 191 addJoinFunction(i, this, this._joinOp); 192 } 193 for (i in unConditionedJoinTypes) { 194 addJoinFunction(i, this, this._joinOp); 195 } 196 for (i in inOperator) { 197 addFunction(i, this, this._inOp); 198 } 199 for (i in betweenOperator) { 200 addFunction(i, this, this._betweenOp); 201 } 202 for (i in aggregateOperator) { 203 addGroupFunction(i, this, this._aggregateOp); 204 } 205 for (i in likeOperator) { 206 addFunction(i, this, this._likeOp); 207 } 208 }, 209 210 _set : function(vals) { 211 var sqlObject = this.sqlObject; 212 if (sqlObject.update) { 213 if (!sqlObject.set) { 214 sqlObject.set = "set "; 215 } 216 if (typeof vals == "object" && !(vals instanceof Array)) { 217 var values = []; 218 var set = []; 219 for (var i in vals) { 220 set.push(i + "=?"); 221 values.push(vals[i]); 222 } 223 sqlObject.set += this.format(set.join(","), values); 224 } else { 225 throw new Error("Vals must be an object"); 226 } 227 } 228 return this; 229 }, 230 231 _betweenOp : function(oper, options) { 232 if (!this.sql) this.find(); 233 if (options) { 234 var sqlObject = this.sqlObject; 235 if (sqlObject.having) { 236 this.having(); 237 } else if (!sqlObject.where) { 238 this.where(); 239 } 240 for (var i in options) { 241 if (this._needLogic) { 242 this.and(); 243 } 244 var key = i, val = options[i]; 245 if (val instanceof Array && val.length == 2) { 246 var opts = {}; 247 if (oper == "between") { 248 opts[key] = val[0]; 249 this.gte(opts); 250 opts[key] = val[1]; 251 this.lte(opts); 252 } else if (oper == "notBetween") { 253 opts[key] = val[0]; 254 this.lte(opts); 255 opts[key] = val[1]; 256 this.gte(opts); 257 } else { 258 throw new Error(oper + " is not supported"); 259 } 260 this._needLogic = true; 261 } else { 262 throw new Error("when calling between value must be an array and have a of length 2"); 263 } 264 } 265 } 266 return this; 267 }, 268 269 _inOp : function(oper, options) { 270 if (!this.sql) this.find(); 271 if (options) { 272 oper = inOperator[oper]; 273 var sqlObject = this.sqlObject; 274 var sqlKey = "where"; 275 if (sqlObject.having) { 276 this.having(); 277 sqlKey = "having"; 278 } else if (!sqlObject.where) { 279 this.where(); 280 } 281 for (var i in options) { 282 if (this._needLogic) { 283 this.and(); 284 } 285 var key = i, val = options[i]; 286 if (val instanceof Mysql) { 287 sqlObject[sqlKey] += key + " " + oper + " (" + val.sql + ")"; 288 this._needLogic = true; 289 } else if (val instanceof Array) { 290 var vals = ""; 291 vals = val.map(function() { 292 return "?"; 293 }); 294 sqlObject[sqlKey] += key + " " + oper + " (" + this.format(vals.join(","), val) + ")"; 295 this._needLogic = true; 296 } else if (typeof val == "string") { 297 sqlObject[sqlKey] += key + " in (" + val + ")"; 298 this._needLogic = true; 299 } else { 300 throw new Error("when calling in value must be a string or array"); 301 } 302 } 303 } 304 return this; 305 }, 306 307 _aggregateOp : function(oper, options) { 308 var op = aggregateOperator[oper]; 309 var sqlObject = this.sqlObject; 310 if (sqlObject.update || sqlObject["delete"]) throw new Error(oper + " cannot be used with update or delete"); 311 this._aggregated = true; 312 if (options) { 313 if (typeof options == "string") { 314 if (sqlObject.select) { 315 var lastChar = sqlObject.select.charAt(sqlObject.select.length - 1); 316 if (sqlObject != "select" && lastChar == "*" || lastChar == ' ') { 317 sqlObject.select += ", "; 318 } 319 } else { 320 sqlObject.select = "select "; 321 } 322 sqlObject.select += op + "(" + options + ") as " + options + "_" + op; 323 } else if (options instanceof Array) { 324 options.forEach(this[op], this); 325 } else { 326 throw new Error("when calling " + oper + " you must pass in a string or array or nothing"); 327 } 328 } else { 329 if (sqlObject.select) { 330 if (sqlObject.select.charAt(sqlObject.select.length - 1) == " ") { 331 sqlObject.select = sqlObject.select.substr(0, sqlObject.select.length - 1) + ", "; 332 } 333 } else { 334 sqlObject.select = "select "; 335 } 336 sqlObject.select += op + "(*) as " + op; 337 } 338 if (!sqlObject.find) this._from(); 339 return this; 340 }, 341 342 _logicalOp : function(oper, options) { 343 oper = logicalOperator[oper]; 344 if (this._needLogic) { 345 var sqlObject = this.sqlObject; 346 var sqlKey = "where"; 347 if (sqlObject.having) { 348 //this.having(); 349 sqlKey = "having"; 350 } else if (!sqlObject.where) { 351 this.where(); 352 } 353 sqlObject[sqlKey] += " " + logicalOperator[oper] + " "; 354 this._needLogic = false; 355 if (options) { 356 var params = []; 357 var count = 0; 358 for (var i in options) { 359 if (count) throw new Error(oper + "operation can only be one deep"); 360 this._createSQLFromObject(i, options[i]); 361 count++; 362 } 363 } 364 } else { 365 throw new Error("logical operator not needed"); 366 } 367 return this; 368 }, 369 370 _joinOp : function(oper, table, options) { 371 if (!this.sql) this.find(); 372 var sqlObject = this.sqlObject; 373 var fromClause = "from"; 374 375 if (!sqlObject.update && !sqlObject.from) { 376 this._from(); 377 } else if (sqlObject.update) { 378 fromClause = "update"; 379 } 380 if (typeof table == "string") { 381 if (sqlObject["delete"]) { 382 if (sqlObject["delete"] == "delete ") { 383 sqlObject["delete"] += this.table; 384 } 385 } 386 var joinType; 387 if (oper in conditionedJoinTypes) { 388 if (options) { 389 joinType = conditionedJoinTypes[oper]; 390 sqlObject[fromClause] += " " + joinType + " " + table; 391 if (options instanceof Array) { 392 sqlObject[fromClause] += " using (" + options.join(", ") + ")"; 393 } else if (typeof options == "object") { 394 sqlObject[fromClause] += " on "; 395 for (var i in options) { 396 sqlObject[fromClause] += this.table + "." + i + "=" + table + "." + options[i]; 397 } 398 } else { 399 throw new Error("join options must be an array or object"); 400 } 401 } else { 402 throw new Error(oper + " requires a join condition"); 403 } 404 } else if (oper in unConditionedJoinTypes) { 405 joinType = unConditionedJoinTypes[oper]; 406 sqlObject[fromClause] += " " + joinType + " " + table; 407 } 408 } else if (table instanceof Mysql) { 409 this._joinOp(oper, table.sql, options); 410 } 411 return this; 412 }, 413 414 _booleanOp : function(oper, options) { 415 if (!this.sql) this.find(); 416 var sqlObject = this.sqlObject; 417 var sqlKey = "where"; 418 if (sqlObject.having) { 419 //this.having(); 420 sqlKey = "having"; 421 } else if (!sqlObject.where) { 422 this.where(); 423 } 424 oper = booleanOperator[oper]; 425 var params = []; 426 for (var i in options) { 427 if (this._needLogic) { 428 this.and(); 429 } 430 sqlObject[sqlKey] += this.format(i + " " + oper + " ?", [options[i]]); 431 this._needLogic = true; 432 } 433 return this; 434 }, 435 436 _isOp : function(oper, options) { 437 if (!this.sql) this.find(); 438 var sqlObject = this.sqlObject; 439 var sqlKey = "where"; 440 if (sqlObject.having) { 441 this.having(); 442 sqlKey = "having"; 443 } else if (!sqlObject.where) { 444 this.where(); 445 } 446 oper = isOperator[oper]; 447 var sql = ""; 448 if (typeof options == "object") { 449 for (var i in options) { 450 if (this._needLogic) { 451 this.and(); 452 } 453 sqlObject[sqlKey] += i + " " + oper + " " + transformBooleanOperator(options[i]); 454 this._needLogic = true; 455 } 456 } else { 457 if (this._needLogic) { 458 this.and(); 459 } 460 sqlObject[sqlKey] += options + " " + oper; 461 this._needLogic = true; 462 } 463 return this; 464 }, 465 466 _likeOp : function(oper, options) { 467 if (!this.sql) this.find(); 468 var sqlObject = this.sqlObject; 469 var sqlKey = "where"; 470 if (sqlObject.having) { 471 this.having(); 472 sqlKey = "having"; 473 } else if (!sqlObject.where) { 474 this.where(); 475 } 476 oper = likeOperator[oper]; 477 var sql = ""; 478 if (typeof options == "object") { 479 for (var i in options) { 480 if (this._needLogic) { 481 this.and(); 482 } 483 sqlObject[sqlKey] += this.format(i + " " + oper + " ?", [options[i]]); 484 this._needLogic = true; 485 } 486 } else { 487 throw new Error("when calling like options must be a hash of {columnName : <like condition>}"); 488 } 489 return this; 490 }, 491 492 where : function(options) { 493 if (!this.sql) this.find(); 494 var sqlObject = this.sqlObject; 495 if (!sqlObject.update && !sqlObject.from) { 496 this._from(); 497 } 498 if (!sqlObject.where) { 499 sqlObject.where = "where "; 500 } 501 this._parseObjectAndCreateSQL(options); 502 return this; 503 }, 504 505 select : function(values, options) { 506 var sqlObject = this.sqlObject; 507 if (!sqlObject.update && !sqlObject["delete"]) { 508 if (!sqlObject.select || sqlObject.select.indexOf("*") != -1) { 509 sqlObject.select = "select "; 510 } 511 if (values) { 512 if (typeof values == "string") { 513 sqlObject.select += values; 514 } else if (values instanceof Array) { 515 for (var i in values) { 516 var val = values[i]; 517 if (typeof val == "string") { 518 if (i > 0) sqlObject.select += ", "; 519 sqlObject.select += val; 520 } else { 521 throw new Error("select params must be a string"); 522 } 523 } 524 } 525 } 526 if (options) { 527 this.where(options); 528 } 529 } else { 530 throw new Error("Cannot call select after update or delete"); 531 } 532 return this; 533 }, 534 535 distinct : function() { 536 var sqlObject = this.sqlObject; 537 if (!sqlObject.update && !sqlObject["delete"]) { 538 if (!sqlObject.select) { 539 sqlObject.select = "select distinct *"; 540 } else { 541 sqlObject.select = sqlObject.select.replace("select", "select distinct"); 542 } 543 } else { 544 throw new Error("Cannot call select after update or delete"); 545 } 546 return this; 547 }, 548 549 update : function(values, options) { 550 var sqlObject = this.sqlObject; 551 if (!sqlObject.select && !sqlObject["delete"]) { 552 if (values) { 553 if (!sqlObject.update) { 554 sqlObject.update = "update " + this.table; 555 } 556 this._set(values); 557 if (options) { 558 this.where(options); 559 } 560 } else { 561 throw new Error("To call update you must provide values to update"); 562 } 563 } else { 564 throw new Error("Cannot call udpate after select or delete!"); 565 } 566 return this; 567 }, 568 569 remove : function(values, options) { 570 var sqlObject = this.sqlObject; 571 if (!sqlObject.update && !sqlObject["delete"]) { 572 if (!sqlObject["delete"]) { 573 sqlObject["delete"] = "delete "; 574 } 575 if (values) { 576 if (sqlObject["delete"] == "delete ") { 577 sqlObject["delete"] += this.table; 578 } 579 if (typeof values == "string") { 580 sqlObject["delete"] += ", " + values; 581 } else if (values instanceof Array) { 582 sqlObject["delete"] += ", " + values.join(", "); 583 } 584 } else if (!sqlObject.from) { 585 this._from(); 586 } 587 if (options) { 588 this.where(options); 589 } 590 } else { 591 throw new Error("Cannot call delete after update or delete"); 592 } 593 return this; 594 }, 595 596 /* 597 options = {name : "fred"}, 598 select * from <table> where name = 'fred' 599 options = {x : {gt : 2}} 600 select * from <table> where x > 2 601 options = {x : {lt : 2}} 602 select * from <table> where x < 2 603 options = {x : {gte : 2}} 604 select * from <table> where x >= 2 605 options = {x : {lte : 2}} 606 select * from <table> where x <= 2 607 options = {x : {in : [1, 2, 3]}} 608 select * from <table> where x in (1,2,3); 609 options = {x : {ne : 2}} 610 select * from <table> where x != 2; 611 options = {flag : {is : (TRUE|FALSE|UNKNOWN)}} 612 select * from <table> where flag is (TRUE|FALSE|UNKNOWN); 613 options = {flag : {isNot : (TRUE|FALSE|UNKNOWN)}} 614 select * from <table> where flag IS NOT (TRUE|FALSE|UNKNOWN); 615 options = {x : {isNull : (TRUE|FALSE|UNKNOWN)}} 616 select * from <table> where flag IS NULL; 617 options = {x : {isNotNull : (TRUE|FALSE|UNKNOWN)}} 618 select * from <table> where flag IS NOT NULL; 619 options = {x : {between : [1,5]}} 620 select * from <table> where x BETWEEN 1 AND 5; 621 options = {x : {notBetween : [1,5]}} 622 select * from <table> where x NOT BETWEEN 1 AND 5; 623 options = {name : {like : "Fred"}} 624 select * from <table> where x NOT BETWEEN 1 AND 5; 625 */ 626 find : function(options) { 627 //reset sql 628 var sqlObject = this.sqlObject; 629 if (!sqlObject.select && !sqlObject.update && !sqlObject["delete"]) { 630 sqlObject.select = "select *"; 631 } 632 if (!sqlObject.update && !sqlObject.from) this._from(); 633 if (options) { 634 if (sqlObject.having) { 635 this.having(options); 636 } else { 637 this.where(options); 638 } 639 } 640 return this; 641 }, 642 643 /* 644 * add order tp query 645 * mysql.order(x) 646 * select * from <table> order by x 647 * mysql.order([x,y]) 648 * select * from <table> order by x,y 649 * mysql.order({x : "desc"}) 650 * select * from <table> order by x desc 651 * mysql.order([{x : "desc"}, y]) 652 * select * from <table> order by x desc, y 653 * mysql.order([{x : "desc"}, {y : desc}]) 654 * select * from <table> order by x desc, y desc 655 656 */ 657 order : function(options) { 658 if (!this.sql) this.find(); 659 var sqlObject = this.sqlObject; 660 if (!sqlObject.from) { 661 this._from(); 662 } 663 if (options) { 664 if (!sqlObject.order) { 665 sqlObject.order = "order by "; 666 this._orderedCount = 0; 667 } else if (this._orderedCount) { 668 sqlObject.order += ", "; 669 } 670 if (typeof options == "string") { 671 sqlObject.order += options; 672 } else if (options instanceof Array) { 673 options.forEach(this.order, this); 674 } else if (typeof options == "object") { 675 var count = 0; 676 for (var i in options) { 677 if (count) throw new Error("when providing an object to order only one key is allowed"); 678 var type = options[i]; 679 if (type == 'desc' || type == "asc") { 680 sqlObject.order += i + " " + type; 681 } else { 682 throw new Error("Only 'asc' or 'desc' is allowed as a value for an ordered object"); 683 } 684 } 685 } 686 this._orderedCount++; 687 } 688 return this; 689 }, 690 691 orderBy : function(options) { 692 return this.order(options); 693 }, 694 695 696 limit : function(limit, offset) { 697 var sqlObject = this.sqlObject; 698 if (!sqlObject.update && !sqlObject.from) { 699 this.find(); 700 } 701 if (limit) { 702 if (typeof limit == "number") { 703 sqlObject.limit = "limit " + limit; 704 } else { 705 throw new Error("when using limit the param must be a number"); 706 } 707 } 708 offset && this.offset(offset); 709 return this; 710 }, 711 712 offset : function(offset) { 713 if (!this.sql) this.find(); 714 var sqlObject = this.sqlObject; 715 if (!sqlObject.update && !sqlObject["delete"]) { 716 if (!sqlObject.from) { 717 this._from(); 718 } 719 if (offset) { 720 if (typeof offset == "number") { 721 sqlObject.offset = "offset " + offset; 722 } else { 723 throw new Error("when using offset the param must be a number"); 724 } 725 } 726 } else { 727 throw new Error("Cannot call offset on update or delete"); 728 } 729 return this; 730 }, 731 732 /* 733 * mysql.join(<tablename>, options); 734 * select * from inner join <thisTable 735 * */ 736 join : function(table, options) { 737 return this._joinOp("innerJoin", table, options); 738 739 }, 740 741 /* 742 * Creats a group clause for sql 743 * mysql.group(<columnName>); 744 * select * from <tableName> group by <columnName> 745 * mysql.group([col1,col2...]); 746 * select * from <tableName> group by col1, col2... 747 * mysql.group(col, havingOptions); 748 * select * from <tableName> group by col having <having options> 749 * mysql.group([col1, col2...], {col1 : a}); 750 * select * from <tableName> group by col1, col2.... having col2 = 'a' 751 * */ 752 group : function(key, options) { 753 if (!this.sql) this.find(); 754 var sqlObject = this.sqlObject; 755 if (!sqlObject.update && !sqlObject["delete"]) { 756 if (!sqlObject.from) { 757 this._from(); 758 } 759 if (key) { 760 if (typeof key == "string") { 761 if (!sqlObject.group) { 762 sqlObject.group = "group by"; 763 } 764 sqlObject.group += " " + key; 765 } else if (key instanceof Array) { 766 if (!sqlObject.group) { 767 sqlObject.group = "group by"; 768 } 769 sqlObject.group += " " + key.join(", "); 770 } else { 771 throw new Error("key must be a string or array"); 772 } 773 if (sqlObject.group && options) { 774 this.having(options); 775 } 776 } else { 777 throw new Error("when calling group a grouping column is required"); 778 } 779 } else { 780 throw new Error("Cannot group on an update or delete"); 781 } 782 return this; 783 }, 784 785 /* 786 * Creates a having clause, group must have been previously called 787 * mysql.having({x : 1}); 788 * select * from <tableName> group by <*> having x = 1 789 * you may also use find if a group clause has been defined 790 * */ 791 having : function(options) { 792 var sqlObject = this.sqlObject; 793 if (sqlObject.group) { 794 if (!sqlObject.having) { 795 sqlObject.having = "having "; 796 this._needLogic = false; 797 } 798 if (options) { 799 this._parseObjectAndCreateSQL(options); 800 } 801 } else { 802 throw new Error("a group clause must be previously defined"); 803 } 804 return this; 805 }, 806 807 logicGroup : function(options) { 808 if (!this.sql) this.find(); 809 var sqlObject = this.sqlObject; 810 var sqlKey = "where"; 811 if (sqlObject.having) { 812 this.having(); 813 sqlKey = "having"; 814 } else if (!sqlObject.where) { 815 this.where(); 816 } 817 if (options) { 818 sqlObject[sqlKey] += "("; 819 this.where(options); 820 sqlObject[sqlKey] += ")"; 821 } 822 return this; 823 }, 824 825 /*call this to finish off select clause and not execute 826 *else just call execute(); 827 *i.e you just call mysql.find() or mysql.select(params); 828 *mysql.find.end(); mysql.find.select().end(); 829 * */ 830 end : function() { 831 var sqlObject = this.sqlObject; 832 if (!sqlObject.select && !sqlObject.update && !sqlObject["delete"]) { 833 this.find(); 834 } 835 if (sqlObject.select) { 836 if (!sqlObject.from) this._from(); 837 } 838 return this; 839 } 840 }, 841 842 static : { 843 createTable : function(table, db) { 844 var promise = new Promise(); 845 db.query(table.createTableSql).then(hitch(promise, "callback", true), hitch(promise, "errback")); 846 return promise; 847 }, 848 849 alterTable : function(table, db) { 850 var promise = new Promise(); 851 db.query(table.alterTableSql).then(hitch(promise, "callback", true), hitch(promise, "errback")); 852 return promise; 853 }, 854 855 dropTable : function(table, db) { 856 var promise = new Promise(); 857 db.query(table.dropTableSql).then(hitch(promise, "callback", true), hitch(promise, "errback")); 858 return promise; 859 }, 860 861 save : function(table, object, db) { 862 if (table && object && db) { 863 var promise = new Promise(); 864 var sql = ""; 865 if (object instanceof Array) { 866 sql = object.map( 867 function(o) { 868 return createInsertStatement(table, o, db); 869 }).join(""); 870 } else { 871 sql = createInsertStatement(table, object, db); 872 } 873 db.query(sql).then(hitch(promise, "callback"), hitch(promise, "errback")); 874 return promise; 875 } else { 876 throw new Error("Table, object, and db required when calling mysql.save"); 877 } 878 }, 879 880 schema : function(tableName, db) { 881 if (typeof tableName != "string") throw new Error("tablename must be a string"); 882 if (tableName && db) { 883 var promise = new Promise(); 884 var database = db.database; 885 db.query("DESCRIBE " + escape(tableName)).then(hitch(this, function(database,results) { 886 var obj = {}; 887 if (results.length) { 888 var schema = {database : database}; 889 var pks = []; 890 results.forEach(function(o) { 891 var t = mysqlTypes.fromColDef(o); 892 if (t.isPrimaryKey()) { 893 pks.push(o.Field); 894 } 895 schema[o.Field] = t; 896 }); 897 pks.length && (schema.primaryKey = pks); 898 promise.callback(new Table(tableName, schema)); 899 } else { 900 promise.callback(null); 901 } 902 }, database), hitch(promise, "errback")); 903 return promise; 904 } else { 905 throw new Error("Table name and db conneciton required to retrieve schema"); 906 } 907 }, 908 909 getLastInsertId : function(db) { 910 var promise = new Promise(); 911 var sql = "SELECT LAST_INSERT_ID() as id"; 912 db.query(sql).then(hitch(promise, "callback"), hitch(promise, "errback")); 913 return promise; 914 }, 915 916 foreignKey : mysqlTypes.foreignKey, 917 addForeignKey : mysqlTypes.addForeignKey, 918 dropForeignKey : mysqlTypes.dropForeignKey, 919 primaryKey : mysqlTypes.primaryKey, 920 addPrimaryKey : mysqlTypes.addPrimaryKey, 921 dropPrimaryKey : mysqlTypes.dropPrimaryKey, 922 unique : mysqlTypes.unique, 923 addUnique : mysqlTypes.addUnique, 924 dropUnique : mysqlTypes.dropUnique, 925 dropColumn : mysqlTypes.dropColumn, 926 alterColumn : mysqlTypes.alterColumn, 927 column : mysqlTypes.column, 928 addColumn : mysqlTypes.addColumn, 929 isValidType : mysqlTypes.isValidType, 930 client : MySQLClient.Client, 931 ConnectionPool : MySQLClient.ConnectionPool, 932 types : mysqlTypes.types 933 934 } 935 })); 936 937 938 939 940 941 942