antville/code/Global/Sql.js
Tobi Schäfer d2501c2d3a Replace for..each loops with for..of
💡 Needs Helma with enabled ES6 features in Rhino
2023-08-27 00:09:17 +02:00

251 lines
7.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// The Antville Project
// http://code.google.com/p/antville
//
// Copyright 20012014 by the Workers of Antville.
//
// Licensed under the Apache License, Version 2.0 (the ``License'');
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an ``AS IS'' BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileOverview Defines the Sql prototype, a utility for relational queries
*/
/**
* @constructor
*/
var Sql = function(options) {
options || (options = {});
var db = getDBConnection('antville');
var query;
var log = new function() {
var fname = getProperty('sqlLog', 'helma.' + app.getName() + '.sql');
return Packages.org.apache.commons.logging.LogFactory.getLog(fname);
}
var SqlData = function(result) {
var columns = [];
this.values = {};
for (var i=1; i<=result.getColumnCount(); i+=1) {
columns.push(result.getColumnName(i).toLowerCase());
}
this.next = function() {
for (let key of columns) {
this.values[key] = result.getColumnItem(key);
}
return;
}
return this;
}
var quote = function(str) {
if (!options.quote || str === null) {
return str;
}
return str.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
}
var value = function(obj) {
if (obj === null) {
return obj;
}
if (obj === undefined) {
obj = String(obj);
}
switch (obj.constructor) {
case Number:
return obj;
case String:
return quote(obj);
case Date:
return 'from_unixtime(' + (obj.getTime() / 1000) + ')';
case HopObject:
case Object:
return quote(obj.toSource());
}
return quote(String(obj));
}
var resolve = function(args) {
var sql = args[0];
if (args.length > 1) {
var values = Array.prototype.splice.call(args, 1);
if (typeof values[0] === 'object') {
values = values[0];
}
sql = sql.replace(/\$(\w*)/g, function() {
return value(values[arguments[1]]);
});
}
return sql;
}
const prepare = function(args) {
const sql = args[0];
if (args.length > 1) {
const values = Array.prototype.splice.call(args, 1);
const connection = db.getMetaData().getConnection();
const statement = connection.prepareStatement(sql);
for (let index = 1, limit = values.length; index <= limit; index += 1) {
let value = values[index - 1];
switch (typeof value) {
case 'number':
statement.setInt(index, value);
break;
case 'string':
statement.setString(index, value);
default:
statement.setString(index, String(value));
}
}
}
return statement;
};
/**
* Executes an SQL command.
* @param {String} sql The SQL command.
* @returns {Object} The result of the SQL command.
*/
this.execute = function(sql) {
sql = resolve(arguments);
log.info(sql);
if (options.test) {
return app.log(sql);
}
var error;
var result = db.executeCommand(sql);
if (error = db.getLastError()) {
app.log(error);
}
return result;
}
/**
* Retrieves an SQL query.
* @example sql.retrieve('select $1 from $2 order by $1', 'date', 'foo')
* ===> 'select date from foo order by date'
* @returns {String}
*/
this.retrieve = function() {
if (options.prepared === true) {
return query = prepare(arguments);
}
return log.info(query = resolve(arguments));
}
/**
* Traverses over the results of an SQL query.
* @param {Function} callback The callback function executed for each record.
*/
this.traverse = function(callback) {
let rows;
if (query instanceof java.sql.PreparedStatement) {
rows = db.executePreparedRetrieval(query);
} else {
rows = db.executeRetrieval(query);
}
if (rows && rows.next()) {
do {
var sql = new SqlData(rows);
sql.next();
if (!options.test) {
callback.call(sql.values, rows);
}
} while (record = rows.next());
rows.release();
}
return;
}
/**
* @return {String}
*/
this.toString = function() {
return query;
}
return this;
}
/**
* SQL query for retrieving the amount of records in a table.
* @constant
*/
Sql.COUNT = 'select count(*) as count from $0';
/**
* SQL query for retrieving the referrers of a site or a story.
* @constant
*/
Sql.REFERRERS = 'select referrer, count(*) as requests from ' +
"log where context_type = '$0' and context_id = $1 and action = " +
"'main' and created > now() - interval '2 days' group " +
'by referrer order by requests desc, referrer asc';
/**
* SQL command for deleting all log entries older than a specific period.
* @constant
*/
Sql.PURGEREFERRERS = "delete from log where action = 'main' and " +
"created < now() - interval '2 days'";
/**
* SQL query for searching stories.
* @constant
*/
Sql.STORY_SEARCH = "select content.id from content, site, metadata, account as creator, account as modifier where site.id = ? and content.prototype = 'Story' and site.id = content.site_id and content.status in ('public', 'shared', 'open') and content.creator_id = creator.id and content.modifier_id = modifier.id and creator.status <> 'deleted' and modifier.status <> 'deleted' and content.prototype = metadata.parent_type and content.id = metadata.parent_id and metadata.name in ('title', 'text') and lower(metadata.value) like lower(?) group by content.id, content.created order by content.created desc limit ?";
Sql.COMMENT_SEARCH = "select comment.id from content as comment, content as story, site, metadata, account as creator, account as modifier where site.id = ? and comment.prototype = 'Comment' and site.id = comment.site_id and comment.story_id = story.id and story.status in ('public', 'shared', 'open') and story.comment_mode in ('open') and comment.creator_id = creator.id and comment.modifier_id = modifier.id and creator.status <> 'deleted' and modifier.status <> 'deleted' and comment.prototype = metadata.parent_type and comment.id = metadata.parent_id and metadata.name in ('title', 'text') and lower(metadata.value) like lower(?) group by comment.id, comment.created order by comment.created desc limit ?";
/**
* SQL query for searching accounts which are not already members of the desired site.
* @constant
*/
Sql.MEMBERSEARCH = "select id, name, created, status from account where status not in ('blocked', 'deleted') and name $0 '$1' order by name asc limit $2";
/**
* SQL query for retrieving all story IDs in a sites archive.
* @constant
*/
Sql.ARCHIVE = "select id from content where site_id = $0 and prototype = 'Story' and " +
"status in ('public', 'shared', 'open') $1 $2 limit $3 offset $4";
/**
* SQL command for retrieving the size of a sites archive.
* @constant
*/
Sql.ARCHIVESIZE = 'select count(*) as count from content where site_id = $0 ' +
"and prototype = 'Story' and status in ('public', 'shared', 'open') $1";
/**
* SQL part filtering the archive query.
* @see Archive#getFilter
* @constant
*/
Sql.ARCHIVEPART = ' and extract($0 from created) = $1';
/**
* SQL part for applying an order to the archive query.
* @see Archive#stories_macro
* @constant
*/
Sql.ARCHIVEORDER = 'order by created desc';