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