      1 // The Antville Project
      2 // http://code.google.com/p/antville
      3 //
      4 // Copyright 2007-2011 by Tobi Schäfer.
      5 //
      6 // Copyright 2001–2007 Robert Gaggl, Hannes Wallnöfer, Tobi Schäfer,
      7 // Matthias & Michael Platzer, Christoph Lincke.
      8 //
      9 // Licensed under the Apache License, Version 2.0 (the ``License'');
     10 // you may not use this file except in compliance with the License.
     11 // You may obtain a copy of the License at
     12 //
     13 //    http://www.apache.org/licenses/LICENSE-2.0
     14 //
     15 // Unless required by applicable law or agreed to in writing, software
     16 // distributed under the License is distributed on an ``AS IS'' BASIS,
     17 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     18 // See the License for the specific language governing permissions and
     19 // limitations under the License.
     20 //
     21 // $Revision$
     22 // $LastChangedBy$
     23 // $LastChangedDate$
     24 // $URL$
     25
     26 /**
     27  * @fileOverview Defines the Sql prototype, a utility for relational queries
     28  */
     29
     30 /**
     31  * @constructor
     32  */
     33 var Sql = function(options) {
     34    options || (options = {});
     35    var db = getDBConnection("antville");
     36    var query;
     37
     38    var log = new function() {
     39       var fname = getProperty("sqlLog", "helma." + app.getName() + ".sql");
     40       return Packages.org.apache.commons.logging.LogFactory.getLog(fname);
     41    }
     42
     43    var SqlData = function(result) {
     44       var columns = [];
     45       this.values = {};
     46
     47       for (var i=1; i<=result.getColumnCount(); i+=1) {
     48          columns.push(result.getColumnName(i).toLowerCase());
     49       }
     50
     51       this.next = function() {
     52          for each (var key in columns) {
     53             this.values[key] = result.getColumnItem(key);
     54          }
     55          return;
     56       }
     57
     58       return this;
     59    }
     60
     61    var quote = function(str) {
     62       if (!options.quote || str === null) {
     63          return str;
     64       }
     65       return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
     66    }
     67
     68    var value = function(obj) {
     69       if (obj === null) {
     70          return obj;
     71       }
     72       if (obj === undefined) {
     73          obj = String(obj);
     74       }
     75       switch (obj.constructor) {
     76          case Number:
     77          return obj;
     78          case String:
     79          return quote(obj);
     80          case Date:
     81          return "from_unixtime(" + (obj.getTime() / 1000) + ")";
     82          case HopObject:
     83          case Object:
     84          return quote(obj.toSource());
     85       }
     86       return quote(String(obj));
     87    }
     88
     89    var resolve = function(args) {
     90       var sql = args[0];
     91       if (args.length > 1) {
     92          var values = Array.prototype.splice.call(args, 1);
     93          if (typeof values[0] === "object") {
     94             values = values[0];
     95          }
     96          sql = sql.replace(/\$(\w*)/g, function() {
     97             return value(values[arguments[1]]);
     98          });
     99       }
    100       return sql;
    101    }
    102
    103    /**
    104     *
    105     * @param {String} sql
    106     * @returns {Object}
    107     */
    108    this.execute = function(sql) {
    109       sql = resolve(arguments);
    110       log.info(sql);
    111       if (options.test) {
    112          return app.log(sql);
    113       }
    114       var error;
    115       var result = db.executeCommand(sql);
    116       if (error = db.getLastError()) {
    117          app.log(error);
    118       }
    119       return result;
    120    }
    121
    122    /**
    123     * @returns {String}
    124     */
    125    this.retrieve = function() {
    126       return log.info(query = resolve(arguments));
    127    }
    128
    129    /**
    130     *
    131     * @param {Function} callback
    132     */
    133    this.traverse = function(callback) {
    134       var rows = db.executeRetrieval(query);
    135       if (rows && rows.next()) {
    136          do {
    137             var sql = new SqlData(rows);
    138             sql.next();
    139             if (!options.test) {
    140                callback.call(sql.values, rows);
    141             }
    142          } while (record = rows.next());
    143          rows.release();
    144       }
    145       return;
    146    }
    147
    148    /**
    149     * @return {String}
    150     */
    151    this.toString = function() {
    152       return query;
    153    }
    154
    155    return this;
    156 }
    157
    158 /** @constant */
    159 Sql.COUNT = "select count(*) as count from $0";
    160
    161 /** @constant */
    162 Sql.REFERRERS = "select referrer, count(*) as requests from " +
    163       "log where context_type = '$0' and context_id = $1 and action = " +
    164       "'main' and created > now() - interval '2 days' group " +
    165       "by referrer order by requests desc, referrer asc";
    166
    167 /** @constant */
    168 Sql.PURGEREFERRERS = "delete from log where action = 'main' and " +
    169       "created < now() - interval '2 days'";
    170
    171 /** @constant */
    172 Sql.SEARCH = "select content.id from content, site, metadata where site.id = $0 and " +
    173       "site.id = content.site_id and content.status in ('public', 'shared', 'open') and " +
    174       "content.id = metadata.parent_id and metadata.name in ('title', 'text') and " +
    175       "lower(metadata.value) like lower('%$1%') group by content.id, content.created " +
    176       "order by content.created desc limit $2";
    177
    178 /** @constant */
    179 Sql.MEMBERSEARCH = "select name from account where name $0 '$1' " +
    180       "order by name asc limit $2";
    181
    182 /** @constant */
    183 Sql.ARCHIVE = "select id from content where site_id = $0 and prototype = 'Story' and " +
    184       "status in ('public', 'shared', 'open') $1 $2 limit $3 offset $4";
    185
    186 /** @constant */
    187 Sql.ARCHIVESIZE = "select count(*) as count from content where site_id = $0 " +
    188       "and prototype = 'Story' and status in ('public', 'shared', 'open') $1";
    189
    190 /** @constant */
    191 Sql.ARCHIVEPART = " and extract($0 from created) = $1";
    192
    193 /** @constant */
    194 Sql.ARCHIVEORDER = "order by created desc";
    195