| rev | 
   line source | 
| 
bsw@0
 | 
     1 var stringthesizer = require('./stringthesizer.js');
 | 
| 
bsw@0
 | 
     2 
 | 
| 
bsw@0
 | 
     3 var quoteLiteral = function(str) {
 | 
| 
bsw@0
 | 
     4   if (str.search(/^"[^"]*"/) >= 0) return str;
 | 
| 
bsw@0
 | 
     5   else return str.replace(/"/g, '""').replace(/[^\.]+/g, '"$&"');
 | 
| 
bsw@0
 | 
     6 };
 | 
| 
bsw@0
 | 
     7 
 | 
| 
bsw@0
 | 
     8 function SQLQuery() {
 | 
| 
bsw@0
 | 
     9 }
 | 
| 
bsw@0
 | 
    10 SQLQuery.prototype.assemble = function(nextPlaceholder) {
 | 
| 
bsw@0
 | 
    11   return stringthesizer.stringthesizer(
 | 
| 
bsw@0
 | 
    12     {
 | 
| 
bsw@0
 | 
    13       nextPlaceholder: nextPlaceholder,
 | 
| 
bsw@0
 | 
    14       valueSeparator: ",",
 | 
| 
bsw@0
 | 
    15       coerce: function(value) {
 | 
| 
bsw@0
 | 
    16         if (value instanceof Selector) return value.toStructure();
 | 
| 
bsw@0
 | 
    17         return value;
 | 
| 
bsw@0
 | 
    18       }
 | 
| 
bsw@0
 | 
    19     },
 | 
| 
bsw@0
 | 
    20     this.toStructure()
 | 
| 
bsw@0
 | 
    21   );
 | 
| 
bsw@0
 | 
    22 }
 | 
| 
bsw@0
 | 
    23 
 | 
| 
bsw@0
 | 
    24 function Selector(from) {
 | 
| 
bsw@0
 | 
    25   this._with = [];
 | 
| 
bsw@0
 | 
    26   this._fields = [];
 | 
| 
bsw@0
 | 
    27   this._distinct = false;
 | 
| 
bsw@0
 | 
    28   this._distinctOn = [];
 | 
| 
bsw@0
 | 
    29   this._from = [];
 | 
| 
bsw@0
 | 
    30   this._where = [];
 | 
| 
bsw@0
 | 
    31   this._groupBy = [];
 | 
| 
bsw@0
 | 
    32   this._having = [];
 | 
| 
bsw@0
 | 
    33   this._combine = [];
 | 
| 
bsw@0
 | 
    34   this._orderBy = [];
 | 
| 
bsw@0
 | 
    35   this._limit = null;
 | 
| 
bsw@0
 | 
    36   this._offset = null;
 | 
| 
bsw@0
 | 
    37   this._readLock = [];
 | 
| 
bsw@0
 | 
    38   this._readLockAll = false;
 | 
| 
bsw@0
 | 
    39   this._writeLock = [];
 | 
| 
bsw@0
 | 
    40   this._writeLockAll = false;
 | 
| 
bsw@0
 | 
    41   if (from != null) this.from(from);
 | 
| 
bsw@0
 | 
    42 }
 | 
| 
bsw@0
 | 
    43 Selector.prototype = new SQLQuery();
 | 
| 
bsw@0
 | 
    44 Selector.prototype.addWith = function(expression, selector) {
 | 
| 
bsw@0
 | 
    45   this._with.push(['$ AS ($)', expression, selector]);
 | 
| 
bsw@0
 | 
    46   return this;
 | 
| 
bsw@0
 | 
    47 };
 | 
| 
bsw@0
 | 
    48 Selector.prototype.addDistinctOn = function(expression) {
 | 
| 
bsw@0
 | 
    49   if (this._distinct) throw "Cannot combine DISTINCT with DISTINCT ON.";
 | 
| 
bsw@0
 | 
    50   this._distinctOn.push(expression);
 | 
| 
bsw@0
 | 
    51   return this;
 | 
| 
bsw@0
 | 
    52 };
 | 
| 
bsw@0
 | 
    53 Selector.prototype.setDistinct = function() {
 | 
| 
bsw@0
 | 
    54   if (this._distinctOn.length > 0) throw "Cannot combine DISTINCT with DISTINCT ON.";
 | 
| 
bsw@0
 | 
    55   this._distinct = true;
 | 
| 
bsw@0
 | 
    56   return this;
 | 
| 
bsw@0
 | 
    57 };
 | 
| 
bsw@0
 | 
    58 Selector.prototype.addFrom = function(expression, alias, condition) {
 | 
| 
bsw@0
 | 
    59   var first = this._from.length == 0;
 | 
| 
bsw@0
 | 
    60   if (!first) {
 | 
| 
bsw@0
 | 
    61     if (condition == null) this._from.push('CROSS JOIN')
 | 
| 
bsw@0
 | 
    62     else this._from.push('INNER JOIN')
 | 
| 
bsw@0
 | 
    63   }
 | 
| 
bsw@0
 | 
    64   if (expression instanceof Selector) {
 | 
| 
bsw@0
 | 
    65     if (alias == null) this._from.push(['($) AS "subquery"', expression]);
 | 
| 
bsw@0
 | 
    66     else this._from.push(['($) AS "$"', expression, alias]);
 | 
| 
bsw@0
 | 
    67   } else {
 | 
| 
bsw@0
 | 
    68     if (alias == null) this._from.push(expression);
 | 
| 
bsw@0
 | 
    69     else this._from.push(['$ AS "$"', expression, alias]);
 | 
| 
bsw@0
 | 
    70   }
 | 
| 
bsw@0
 | 
    71   if (condition != null) {
 | 
| 
bsw@0
 | 
    72     if (first) {
 | 
| 
bsw@0
 | 
    73       this.addWhere(condition);
 | 
| 
bsw@0
 | 
    74     } else {
 | 
| 
bsw@0
 | 
    75       this._from.push('ON');
 | 
| 
bsw@0
 | 
    76       this._from.push(condition);
 | 
| 
bsw@0
 | 
    77     }
 | 
| 
bsw@0
 | 
    78   }
 | 
| 
bsw@0
 | 
    79   return this;
 | 
| 
bsw@0
 | 
    80 };
 | 
| 
bsw@0
 | 
    81 Selector.prototype.addWhere = function(expression) {
 | 
| 
bsw@0
 | 
    82   this._where.push(['($)', expression]);
 | 
| 
bsw@0
 | 
    83   return this;
 | 
| 
bsw@0
 | 
    84 };
 | 
| 
bsw@0
 | 
    85 Selector.prototype.addGroupBy = function(expression) {
 | 
| 
bsw@0
 | 
    86   this._groupBy.push(expression);
 | 
| 
bsw@0
 | 
    87   return this;
 | 
| 
bsw@0
 | 
    88 };
 | 
| 
bsw@0
 | 
    89 Selector.prototype.addHaving = function(expression) {
 | 
| 
bsw@0
 | 
    90   this._having.push(['($)', expression]);
 | 
| 
bsw@0
 | 
    91   return this;
 | 
| 
bsw@0
 | 
    92 };
 | 
| 
bsw@0
 | 
    93 Selector.prototype.addCombine = function(expression) {
 | 
| 
bsw@0
 | 
    94   this._combine.push(expression);
 | 
| 
bsw@0
 | 
    95   return this;
 | 
| 
bsw@0
 | 
    96 };
 | 
| 
bsw@0
 | 
    97 Selector.prototype.addOrderBy = function(expression) {
 | 
| 
bsw@0
 | 
    98   this._orderBy.push(expression);
 | 
| 
bsw@0
 | 
    99   return this;
 | 
| 
bsw@0
 | 
   100 };
 | 
| 
bsw@0
 | 
   101 Selector.prototype.limit = function(count) {
 | 
| 
bsw@0
 | 
   102   this._limit = count;
 | 
| 
bsw@0
 | 
   103   return this;
 | 
| 
bsw@0
 | 
   104 };
 | 
| 
bsw@0
 | 
   105 Selector.prototype.offset = function(count) {
 | 
| 
bsw@0
 | 
   106   this._offset = count;
 | 
| 
bsw@0
 | 
   107   return this;
 | 
| 
bsw@0
 | 
   108 };
 | 
| 
bsw@0
 | 
   109 Selector.prototype.forShare = function() {
 | 
| 
bsw@0
 | 
   110   this._readLockAll = true;
 | 
| 
bsw@0
 | 
   111   return this;
 | 
| 
bsw@0
 | 
   112 };
 | 
| 
bsw@0
 | 
   113 Selector.prototype.forShareOf = function(expression) {
 | 
| 
bsw@0
 | 
   114   this._readLock.push(expression);
 | 
| 
bsw@0
 | 
   115   return this;
 | 
| 
bsw@0
 | 
   116 };
 | 
| 
bsw@0
 | 
   117 Selector.prototype.forUpdate = function() {
 | 
| 
bsw@0
 | 
   118   this._writeLockAll = true;
 | 
| 
bsw@0
 | 
   119   return this;
 | 
| 
bsw@0
 | 
   120 };
 | 
| 
bsw@0
 | 
   121 Selector.prototype.forUpdateOf = function(expression) {
 | 
| 
bsw@0
 | 
   122   this._writeLock.push(expression);
 | 
| 
bsw@0
 | 
   123   return this;
 | 
| 
bsw@0
 | 
   124 };
 | 
| 
bsw@0
 | 
   125 Selector.prototype.resetFields = function() {
 | 
| 
bsw@0
 | 
   126   this._fields = [];
 | 
| 
bsw@0
 | 
   127   return this;
 | 
| 
bsw@0
 | 
   128 };
 | 
| 
bsw@0
 | 
   129 Selector.prototype.addField = function(expression, alias, options) {
 | 
| 
bsw@0
 | 
   130   var self = this;
 | 
| 
bsw@0
 | 
   131   var processOption = function(option) {
 | 
| 
bsw@0
 | 
   132     if (option == "distinct") {
 | 
| 
bsw@0
 | 
   133       if (alias == null) self.addDistinctOn(expression);
 | 
| 
bsw@0
 | 
   134       else self.addDistinctOn(['"$"', alias]);
 | 
| 
bsw@0
 | 
   135     } else if (option == "grouped") {
 | 
| 
bsw@0
 | 
   136       if (alias == null) self.addGroupBy(expression);
 | 
| 
bsw@0
 | 
   137       else self.addGroupBy(['"$"', alias]);
 | 
| 
bsw@0
 | 
   138     } else {
 | 
| 
bsw@0
 | 
   139       throw "Unexpected option passed to addField(...).";
 | 
| 
bsw@0
 | 
   140     }
 | 
| 
bsw@0
 | 
   141   }
 | 
| 
bsw@0
 | 
   142   if (alias == null) this._fields.push(expression);
 | 
| 
bsw@0
 | 
   143   else this._fields.push(['$ AS "$"', expression, alias]);
 | 
| 
bsw@0
 | 
   144   if (options != null) {
 | 
| 
bsw@0
 | 
   145     if (options instanceof Array) options.forEach(processOption);
 | 
| 
bsw@0
 | 
   146     else processOption(options);
 | 
| 
bsw@0
 | 
   147   }
 | 
| 
bsw@0
 | 
   148   return this;
 | 
| 
bsw@0
 | 
   149 };
 | 
| 
bsw@0
 | 
   150 Selector.prototype.join = Selector.prototype.addFrom;
 | 
| 
bsw@0
 | 
   151 Selector.prototype.from = function(expression, alias, condition) {
 | 
| 
bsw@0
 | 
   152   if (this._from.length > 0) {
 | 
| 
bsw@0
 | 
   153     error();
 | 
| 
bsw@0
 | 
   154   }
 | 
| 
bsw@0
 | 
   155   return this.addFrom(expression, alias, condition);
 | 
| 
bsw@0
 | 
   156 };
 | 
| 
bsw@0
 | 
   157 Selector.prototype.leftJoin = function(expression, alias, condition) {
 | 
| 
bsw@0
 | 
   158   var first = this._from.length == 0;
 | 
| 
bsw@0
 | 
   159   if (!first) this._from.push('LEFT OUTER JOIN');
 | 
| 
bsw@0
 | 
   160   if (alias == null) this._from.push(expression);
 | 
| 
bsw@0
 | 
   161   else this._from.push(['$ AS "$"', expression, alias]);
 | 
| 
bsw@0
 | 
   162   if (condition != null) {
 | 
| 
bsw@0
 | 
   163     if (first) {
 | 
| 
bsw@0
 | 
   164       this.addWhere(condition);
 | 
| 
bsw@0
 | 
   165     } else {
 | 
| 
bsw@0
 | 
   166       this._from.push('ON');
 | 
| 
bsw@0
 | 
   167       this._from.push(condition);
 | 
| 
bsw@0
 | 
   168     }
 | 
| 
bsw@0
 | 
   169   }
 | 
| 
bsw@0
 | 
   170 };
 | 
| 
bsw@0
 | 
   171 Selector.prototype.union = function(expression) {
 | 
| 
bsw@0
 | 
   172   this.addCombine(['UNION $', expression]);
 | 
| 
bsw@0
 | 
   173   return this;
 | 
| 
bsw@0
 | 
   174 };
 | 
| 
bsw@0
 | 
   175 Selector.prototype.unionAll = function(expression) {
 | 
| 
bsw@0
 | 
   176   this.addCombine(['UNION ALL $', expression]);
 | 
| 
bsw@0
 | 
   177   return this;
 | 
| 
bsw@0
 | 
   178 };
 | 
| 
bsw@0
 | 
   179 Selector.prototype.intersect = function(expression) {
 | 
| 
bsw@0
 | 
   180   this.addCombine(['INTERSECT $', expression]);
 | 
| 
bsw@0
 | 
   181   return this;
 | 
| 
bsw@0
 | 
   182 };
 | 
| 
bsw@0
 | 
   183 Selector.prototype.intersectAll = function(expression) {
 | 
| 
bsw@0
 | 
   184   this.addCombine(['INTERSECT ALL $', expression]);
 | 
| 
bsw@0
 | 
   185   return this;
 | 
| 
bsw@0
 | 
   186 };
 | 
| 
bsw@0
 | 
   187 Selector.prototype.except = function(expression) {
 | 
| 
bsw@0
 | 
   188   this.addCombine(['EXCEPT $', expression]);
 | 
| 
bsw@0
 | 
   189   return this;
 | 
| 
bsw@0
 | 
   190 };
 | 
| 
bsw@0
 | 
   191 Selector.prototype.exceptAll = function(expression) {
 | 
| 
bsw@0
 | 
   192   this.addCombine(['EXCEPT ALL $', expression]);
 | 
| 
bsw@0
 | 
   193   return this;
 | 
| 
bsw@0
 | 
   194 };
 | 
| 
bsw@0
 | 
   195 Selector.prototype.toStructure = function() {
 | 
| 
bsw@0
 | 
   196   var parts = [];
 | 
| 
bsw@0
 | 
   197   parts.push('SELECT');
 | 
| 
bsw@0
 | 
   198   if (this._distinct) parts.push('DISTINCT');
 | 
| 
bsw@0
 | 
   199   else if (this._distinctOn.length > 0)
 | 
| 
bsw@0
 | 
   200     parts.push(['DISTINCT ON ($$)', ', ', this._distinctOn]);
 | 
| 
bsw@0
 | 
   201   parts.push(["$$", ", ", this._fields]);
 | 
| 
bsw@0
 | 
   202   if (this._from.length > 0)    parts.push(['FROM $$',     ' ',     this._from]);
 | 
| 
bsw@0
 | 
   203   if (this._where.length > 0)   parts.push(['WHERE $$',    ' AND ', this._where]);
 | 
| 
bsw@0
 | 
   204   if (this._groupBy.length > 0) parts.push(['GROUP BY $$', ', ',    this._groupBy]);
 | 
| 
bsw@0
 | 
   205   if (this._having.length > 0)  parts.push(['HAVING $$',   ' AND ', this._having]);
 | 
| 
bsw@0
 | 
   206   this._combine.forEach(function(entry) { parts.push(entry); });
 | 
| 
bsw@0
 | 
   207   if (this._orderBy.length > 0) parts.push(['ORDER BY $$', ', ',    this._orderBy]);
 | 
| 
bsw@0
 | 
   208   if (this._limit != null)      parts.push(['LIMIT ?',  this._limit]);
 | 
| 
bsw@0
 | 
   209   if (this._offset != null)     parts.push(['OFFSET ?', this._offset]);
 | 
| 
bsw@0
 | 
   210   if (this._writeLockAll) parts.push('FOR UPDATE');
 | 
| 
bsw@0
 | 
   211   else {
 | 
| 
bsw@0
 | 
   212     if (this._readLockAll) parts.push('FOR SHARE');
 | 
| 
bsw@0
 | 
   213     else if (this._readLock.length > 0)
 | 
| 
bsw@0
 | 
   214       parts.push(['FOR SHARE OF $$', ', ', this._readLock]);
 | 
| 
bsw@0
 | 
   215     if (this._writeLock.length > 0)
 | 
| 
bsw@0
 | 
   216       parts.push(['FOR UPDATE OF $$', ', ', this._writeLock]);
 | 
| 
bsw@0
 | 
   217   }
 | 
| 
bsw@0
 | 
   218   return ["$$", " ", parts];
 | 
| 
bsw@0
 | 
   219 };
 | 
| 
bsw@0
 | 
   220 
 | 
| 
bsw@0
 | 
   221 function SQLInsert(table) {
 | 
| 
bsw@0
 | 
   222   this._with = [];
 | 
| 
bsw@0
 | 
   223   if (table == null) this._table = null;
 | 
| 
bsw@0
 | 
   224   else this._table = table;
 | 
| 
bsw@0
 | 
   225   this._columns = [];
 | 
| 
bsw@0
 | 
   226   this._values = [];
 | 
| 
bsw@0
 | 
   227   this._query = null;
 | 
| 
bsw@0
 | 
   228   this._returning = [];
 | 
| 
bsw@0
 | 
   229 }
 | 
| 
bsw@0
 | 
   230 SQLInsert.prototype = new SQLQuery();
 | 
| 
bsw@0
 | 
   231 SQLInsert.prototype.addWith = Selector.prototype.addWith;
 | 
| 
bsw@0
 | 
   232 SQLInsert.prototype.table = function(expression) {
 | 
| 
bsw@0
 | 
   233   this._table = expression;
 | 
| 
bsw@0
 | 
   234   return this;
 | 
| 
bsw@0
 | 
   235 };
 | 
| 
bsw@0
 | 
   236 SQLInsert.prototype.addValues = function(mapping) {
 | 
| 
bsw@0
 | 
   237   if (this._query != null) throw "Cannot combine query with values.";
 | 
| 
bsw@0
 | 
   238   for (key in mapping) {
 | 
| 
bsw@0
 | 
   239     this._columns.push(key);
 | 
| 
bsw@0
 | 
   240     this._values.push(['?', mapping[key]]);
 | 
| 
bsw@0
 | 
   241   }
 | 
| 
bsw@0
 | 
   242 };
 | 
| 
bsw@0
 | 
   243 SQLInsert.prototype.addValueExpressions = function(mapping) {
 | 
| 
bsw@0
 | 
   244   if (this._query != null) throw "Cannot combine query with values.";
 | 
| 
bsw@0
 | 
   245   for (key in mapping) {
 | 
| 
bsw@0
 | 
   246     this._columns.push(key);
 | 
| 
bsw@0
 | 
   247     this._values.push(mapping[key]);
 | 
| 
bsw@0
 | 
   248   }
 | 
| 
bsw@0
 | 
   249 };
 | 
| 
bsw@0
 | 
   250 SQLInsert.prototype.query = function(columns, expression) {
 | 
| 
bsw@0
 | 
   251   if (this._values.length > 0) throw "Cannot combine query with values.";
 | 
| 
bsw@0
 | 
   252   this._columns = columns;
 | 
| 
bsw@0
 | 
   253   this._select = expression;
 | 
| 
bsw@0
 | 
   254 };
 | 
| 
bsw@0
 | 
   255 SQLInsert.prototype.addReturning = function(expression, alias) {
 | 
| 
bsw@0
 | 
   256   if (alias == null) this._returning.push(expression);
 | 
| 
bsw@0
 | 
   257   else this._returning.push(['$ AS "$"', expression, alias]);
 | 
| 
bsw@0
 | 
   258 };
 | 
| 
bsw@0
 | 
   259 SQLInsert.prototype.toStructure = function() {
 | 
| 
bsw@0
 | 
   260   var parts = [];
 | 
| 
bsw@0
 | 
   261   parts.push('INSERT INTO');
 | 
| 
bsw@0
 | 
   262   if (this._table == null) throw "Missing table for INSERT.";
 | 
| 
bsw@0
 | 
   263   parts.push(this._table);
 | 
| 
bsw@0
 | 
   264   if (this._columns.length > 0) parts.push(['($$)', ', ', this._columns]);
 | 
| 
bsw@0
 | 
   265   if (this._values.length > 0) parts.push(['VALUES ($$)', ', ', this._values]);
 | 
| 
bsw@0
 | 
   266   else if (this._query == null) parts.push('DEFAULT VALUES');
 | 
| 
bsw@0
 | 
   267   else parts.push(this._query);
 | 
| 
bsw@0
 | 
   268   if (this._returning.length > 0)
 | 
| 
bsw@0
 | 
   269     parts.push(['RETURNING $$', ', ', this._returning]);
 | 
| 
bsw@0
 | 
   270   return ["$$", " ", parts];
 | 
| 
bsw@0
 | 
   271 };
 | 
| 
bsw@0
 | 
   272 
 | 
| 
bsw@0
 | 
   273 function SQLUpdate(table) {
 | 
| 
bsw@0
 | 
   274   this._with = [];
 | 
| 
bsw@0
 | 
   275   if (table == null) this._table = null;
 | 
| 
bsw@0
 | 
   276   else this._table = table;
 | 
| 
bsw@0
 | 
   277   this._columns = [];
 | 
| 
bsw@0
 | 
   278   this._values = [];
 | 
| 
bsw@0
 | 
   279   this._query = null;
 | 
| 
bsw@0
 | 
   280   this._from = [];
 | 
| 
bsw@0
 | 
   281   this._where = [];
 | 
| 
bsw@0
 | 
   282   this._returning = [];
 | 
| 
bsw@0
 | 
   283 }
 | 
| 
bsw@0
 | 
   284 SQLUpdate.prototype = new SQLQuery();
 | 
| 
bsw@0
 | 
   285 SQLUpdate.prototype.addWith = Selector.prototype.addWith;
 | 
| 
bsw@0
 | 
   286 SQLUpdate.prototype.table = function(expression, alias) {
 | 
| 
bsw@0
 | 
   287   if (alias == null) this._table = expression;
 | 
| 
bsw@0
 | 
   288   else this._table = ['$ AS "$"', expression, alias];
 | 
| 
bsw@0
 | 
   289   return this;
 | 
| 
bsw@0
 | 
   290 }
 | 
| 
bsw@0
 | 
   291 SQLUpdate.prototype.addValues = SQLInsert.prototype.addValues;
 | 
| 
bsw@0
 | 
   292 SQLUpdate.prototype.addValueExpressions = SQLInsert.prototype.addValueExpressions;
 | 
| 
bsw@0
 | 
   293 SQLUpdate.prototype.query = SQLInsert.prototype.query;
 | 
| 
bsw@0
 | 
   294 SQLUpdate.prototype.addFrom = Selector.prototype.addFrom;
 | 
| 
bsw@0
 | 
   295 SQLUpdate.prototype.join = Selector.prototype.addFrom;
 | 
| 
bsw@0
 | 
   296 SQLUpdate.prototype.leftJoin = function(expression, alias, condition) {
 | 
| 
bsw@0
 | 
   297   if (this._from.length == 0)
 | 
| 
bsw@0
 | 
   298     throw "First join for UPDATE or DELETE must not be a left join.";
 | 
| 
bsw@0
 | 
   299   this._from.push('LEFT OUTER JOIN');
 | 
| 
bsw@0
 | 
   300   if (alias == null) this._from.push(expression);
 | 
| 
bsw@0
 | 
   301   else this._from.push(['$ AS "$"', expression, alias]);
 | 
| 
bsw@0
 | 
   302   if (condition != null) {
 | 
| 
bsw@0
 | 
   303     this._from.push('ON');
 | 
| 
bsw@0
 | 
   304     this._from.push(condition);
 | 
| 
bsw@0
 | 
   305   }
 | 
| 
bsw@0
 | 
   306 };
 | 
| 
bsw@0
 | 
   307 SQLUpdate.prototype.addWhere = Selector.prototype.addWhere;
 | 
| 
bsw@0
 | 
   308 SQLUpdate.prototype.addReturning = SQLInsert.prototype.addReturning;
 | 
| 
bsw@0
 | 
   309 SQLUpdate.prototype.toStructure = function() {
 | 
| 
bsw@0
 | 
   310   var parts = [];
 | 
| 
bsw@0
 | 
   311   parts.push('UPDATE');
 | 
| 
bsw@0
 | 
   312   if (this._table == null) throw "Missing table for UPDATE.";
 | 
| 
bsw@0
 | 
   313   parts.push(this._table);
 | 
| 
bsw@0
 | 
   314   parts.push('SET');
 | 
| 
bsw@0
 | 
   315   if (this._columns.length == 0) throw "Missing columns for UPDATE.";
 | 
| 
bsw@0
 | 
   316   if (this._query == null) {
 | 
| 
bsw@0
 | 
   317     for (var i=0; i<this._columns.length; i++) {
 | 
| 
bsw@0
 | 
   318       parts.push(
 | 
| 
bsw@0
 | 
   319         [ (i==this._columns.length-1) ? '$ = $' : '$ = $,',
 | 
| 
bsw@0
 | 
   320           this._columns[i],
 | 
| 
bsw@0
 | 
   321           this._values[i]]
 | 
| 
bsw@0
 | 
   322       );
 | 
| 
bsw@0
 | 
   323     }
 | 
| 
bsw@0
 | 
   324   } else {
 | 
| 
bsw@0
 | 
   325     parts.push(['($$) = ($$)', ', ', this._columns, ', ', this._values]);
 | 
| 
bsw@0
 | 
   326   }
 | 
| 
bsw@0
 | 
   327   if (this._from.length > 0) parts.push(['FROM $$', ' ', this._from]);
 | 
| 
bsw@0
 | 
   328   if (this._where.length > 0) parts.push(['WHERE $$', ' AND ', this._where]);
 | 
| 
bsw@0
 | 
   329   if (this._returning.length > 0)
 | 
| 
bsw@0
 | 
   330     parts.push(['RETURNING $$', ', ', this._returning]);
 | 
| 
bsw@0
 | 
   331   return ["$$", " ", parts];
 | 
| 
bsw@0
 | 
   332 };
 | 
| 
bsw@0
 | 
   333 
 | 
| 
bsw@0
 | 
   334 function SQLDelete(table) {
 | 
| 
bsw@0
 | 
   335   this._with = [];
 | 
| 
bsw@0
 | 
   336   if (table == null) this._table = null;
 | 
| 
bsw@0
 | 
   337   else this._table = table;
 | 
| 
bsw@0
 | 
   338   this._from = [];  // USING clause
 | 
| 
bsw@0
 | 
   339   this._where = [];
 | 
| 
bsw@0
 | 
   340   this._returning = [];
 | 
| 
bsw@0
 | 
   341 }
 | 
| 
bsw@0
 | 
   342 SQLDelete.prototype = new SQLQuery();
 | 
| 
bsw@0
 | 
   343 SQLDelete.prototype.addWith = Selector.prototype.addWith;
 | 
| 
bsw@0
 | 
   344 SQLDelete.prototype.table = SQLUpdate.prototype.table;
 | 
| 
bsw@0
 | 
   345 SQLDelete.prototype.addValues = SQLInsert.prototype.addValues;
 | 
| 
bsw@0
 | 
   346 SQLDelete.prototype.addValueExpressions = SQLInsert.prototype.addValueExpressions;
 | 
| 
bsw@0
 | 
   347 SQLDelete.prototype.addFrom = Selector.prototype.addFrom;
 | 
| 
bsw@0
 | 
   348 SQLDelete.prototype.addUsing = Selector.prototype.addFrom;
 | 
| 
bsw@0
 | 
   349 SQLDelete.prototype.join = Selector.prototype.addFrom;
 | 
| 
bsw@0
 | 
   350 SQLDelete.prototype.leftJoin = SQLUpdate.prototype.leftJoin;
 | 
| 
bsw@0
 | 
   351 SQLDelete.prototype.addWhere = Selector.prototype.addWhere;
 | 
| 
bsw@0
 | 
   352 SQLDelete.prototype.addReturning = SQLInsert.prototype.addReturning;
 | 
| 
bsw@0
 | 
   353 SQLDelete.prototype.toStructure = function() {
 | 
| 
bsw@0
 | 
   354   var parts = [];
 | 
| 
bsw@0
 | 
   355   parts.push('DELETE FROM');
 | 
| 
bsw@0
 | 
   356   if (this._table == null) throw "Missing table for DELETE.";
 | 
| 
bsw@0
 | 
   357   parts.push(this._table);
 | 
| 
bsw@0
 | 
   358   if (this._from.length > 0) parts.push(['USING $$', ' ', this._from]);
 | 
| 
bsw@0
 | 
   359   if (this._where.length > 0) parts.push(['WHERE $$', ' AND ', this._where]);
 | 
| 
bsw@0
 | 
   360   if (this._returning.length > 0)
 | 
| 
bsw@0
 | 
   361     parts.push(['RETURNING $$', ', ', this._returning]);
 | 
| 
bsw@0
 | 
   362   return ["$$", " ", parts];
 | 
| 
bsw@0
 | 
   363 };
 | 
| 
bsw@0
 | 
   364 
 | 
| 
bsw@0
 | 
   365 function Upserter(table, keys) {
 | 
| 
bsw@0
 | 
   366   if (table == null) this._table = null;
 | 
| 
bsw@0
 | 
   367   else this._table = table;
 | 
| 
bsw@0
 | 
   368   this._columns = [];
 | 
| 
bsw@0
 | 
   369   this._values = [];
 | 
| 
bsw@0
 | 
   370   this._keys = [];
 | 
| 
bsw@0
 | 
   371   if (keys) this.addKeys(keys);
 | 
| 
bsw@0
 | 
   372 }
 | 
| 
bsw@0
 | 
   373 
 | 
| 
bsw@0
 | 
   374 Upserter.prototype.addValues = SQLInsert.prototype.addValues;
 | 
| 
bsw@0
 | 
   375 Upserter.prototype.addValueExpressions = SQLInsert.prototype.addValueExpressions;
 | 
| 
bsw@0
 | 
   376 Upserter.prototype.addKeys = function(keys) {
 | 
| 
bsw@0
 | 
   377   var self = this;
 | 
| 
bsw@0
 | 
   378   keys.forEach(function(key) { self._keys.push(key); });
 | 
| 
bsw@0
 | 
   379 };
 | 
| 
bsw@0
 | 
   380 Upserter.prototype.applyWhere = function(sqlQuery) {
 | 
| 
bsw@0
 | 
   381   for (var i=0; i<this._columns.length; i++) {
 | 
| 
bsw@0
 | 
   382     var column = this._columns[i];
 | 
| 
bsw@0
 | 
   383     var value = this._values[i];
 | 
| 
bsw@0
 | 
   384     for (var j=0; j<this._keys.length; j++) if (this._keys[j] == column) break;
 | 
| 
bsw@0
 | 
   385     if (j<this._keys.length) sqlQuery.addWhere(['$ = $', column, value]);
 | 
| 
bsw@0
 | 
   386   }
 | 
| 
bsw@0
 | 
   387 }
 | 
| 
bsw@0
 | 
   388 Upserter.prototype.getSelector = function() {
 | 
| 
bsw@0
 | 
   389   var selector = new Selector(this._table).addField('NULL');
 | 
| 
bsw@0
 | 
   390   this.applyWhere(selector);
 | 
| 
bsw@0
 | 
   391   return selector;
 | 
| 
bsw@0
 | 
   392 }
 | 
| 
bsw@0
 | 
   393 Upserter.prototype.getSQLInsert = function() {
 | 
| 
bsw@0
 | 
   394   var sqlInsert = new SQLInsert(this._table);
 | 
| 
bsw@0
 | 
   395   sqlInsert._columns = this._columns;
 | 
| 
bsw@0
 | 
   396   sqlInsert._values = this._values;
 | 
| 
bsw@0
 | 
   397   return sqlInsert;
 | 
| 
bsw@0
 | 
   398 }
 | 
| 
bsw@0
 | 
   399 Upserter.prototype.getSQLUpdate = function() {
 | 
| 
bsw@0
 | 
   400   var sqlUpdate = new SQLUpdate(this._table);
 | 
| 
bsw@0
 | 
   401   for (var i =0; i<this._columns.length; i++) {
 | 
| 
bsw@0
 | 
   402     var column = this._columns[i];
 | 
| 
bsw@0
 | 
   403     var value = this._values[i];
 | 
| 
bsw@0
 | 
   404     for (var j=0; j<this._keys.length; j++) if (this._keys[j] == column) break;
 | 
| 
bsw@0
 | 
   405     if (j==this._keys.length) {
 | 
| 
bsw@0
 | 
   406       sqlUpdate._columns.push(column);
 | 
| 
bsw@0
 | 
   407       sqlUpdate._values.push(value);
 | 
| 
bsw@0
 | 
   408     }
 | 
| 
bsw@0
 | 
   409   }
 | 
| 
bsw@0
 | 
   410   if (sqlUpdate._columns.length == 0) return null;
 | 
| 
bsw@0
 | 
   411   this.applyWhere(sqlUpdate);
 | 
| 
bsw@0
 | 
   412   return sqlUpdate;
 | 
| 
bsw@0
 | 
   413 }
 | 
| 
bsw@0
 | 
   414 
 | 
| 
bsw@0
 | 
   415 
 | 
| 
bsw@0
 | 
   416 exports.SQLQuery = SQLQuery;
 | 
| 
bsw@0
 | 
   417 exports.Selector = Selector;
 | 
| 
bsw@0
 | 
   418 exports.Upserter = Upserter;
 | 
| 
bsw@0
 | 
   419 exports.SQLInsert = SQLInsert;
 | 
| 
bsw@0
 | 
   420 exports.SQLUpdate = SQLUpdate;
 | 
| 
bsw@0
 | 
   421 exports.SQLDelete = SQLDelete;
 | 
| 
bsw@0
 | 
   422 
 |