Class that is used for querying/retrieving datasets from a database.
Dynamically generated methods include
Features that a particular patio.Dataset supports are shown in the example below. If you wish to implement an adapter please override these values depending on the database that you are developing the adapter for.
var ds = DB.from("test"); //The default values returned //Whether this dataset quotes identifiers. //Whether this dataset quotes identifiers. ds.quoteIdentifiers //=>true //Whether this dataset will provide accurate number of rows matched for //delete and update statements. Accurate in this case is the number of //rows matched by the dataset's filter. ds.providesAccurateRowsMatched; //=>true //Times Whether the dataset requires SQL standard datetimes (false by default, // as most allow strings with ISO 8601 format). ds.requiresSqlStandardDate; //=>false //Whether the dataset supports common table expressions (the WITH clause). ds.supportsCte; //=>true //Whether the dataset supports the DISTINCT ON clause, false by default. ds.supportsDistinctOn; //=>false //Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default. ds.supportsIntersectExcept; //=>true //Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default ds.supportsIntersectExceptAll; //=>true //Whether the dataset supports the IS TRUE syntax. ds.supportsIsTrue; //=>true //Whether the dataset supports the JOIN table USING (column1, ...) syntax. ds.supportsJoinUsing; //=>true //Whether modifying joined datasets is supported. ds.supportsModifyingJoin; //=>false //Whether the IN/NOT IN operators support multiple columns when an ds.supportsMultipleColumnIn; //=>true //Whether the dataset supports timezones in literal timestamps ds.supportsTimestampTimezone; //=>false //Whether the dataset supports fractional seconds in literal timestamps ds.supportsTimestampUsecs; //=>true //Whether the dataset supports window functions. ds.supportsWindowFunctions; //=>false
Each dataset does not actually send any query to the database until an action method has been called upon it(with the exception of patio.Dataset#graph because columns from the other table might need retrived in order to set up the graph). Each action returns a comb.Promise that will be resolved with the result or errback, it is important that you account for errors otherwise it can be difficult to track down issues. The list of action methods is:
Property | Type | Default Value | Description |
ACTION_METHODS | String[] |
['all', 'one', 'avg', 'count', 'columns', 'remove', 'forEach', 'isEmpty', 'first',
'get', 'import', 'insert', 'save', 'insertMultiple', 'saveMultiple', 'interval', 'last',
'map', 'max', 'min', 'multiInsert', 'range', 'selectHash', 'selectMap', 'selectOrderMap', 'set',
'singleRecord', 'singleValue', 'sum', 'toCsv', 'toHash', 'truncate', 'update', 'stream']
| List of action methods avaiable on the dataset. |
AND_SEPARATOR | property | " AND " | Default SQL AND separator. |
BOOL_FALSE | property | "'f'" | Default SQL boolean false operator. |
BOOL_TRUE | property | "'t'" | Default SQL boolean true operator. |
COLUMN_CHANGE_OPTS | property | ["select", "sql", "from", "join"] | The dataset options that require the removal of cached columns if changed. |
COMMA_SEPARATOR | property | ' | Default SQL comma sperator. |
CONDITIONED_JOIN_TYPES | property | ["inner", "fullOuter", "rightOuter", "leftOuter", "full", "right", "left"] | These strings have {name}Join methods created (e.g. patio.Dataset#innerJoin) that call patio.Dataset#joinTable with the string, passing along the arguments and block from the method call. |
COUNT_FROM_SELF_OPTS | String[] |
["distinct", "group", "sql", "limit", "compounds"]
| List of options that can interfere with the aggregation of a patio.Dataset |
COUNT_OF_ALL_AS_COUNT | property | sql.count(sql.literal('*')).as("count") | Default COUNT expression. |
DATASET_ALIAS_BASE_NAME | property | 't' | Default alias for datasets. |
DELETE_CLAUSE_METHODS | property | clauseMethods("delete" | Default SQL DELETE clause methods. This may be overrode by adapters. |
FEATURES | property |
["quoteIdentifiers","providesAccurateRowsMatched","requiresSqlStandardDateTimes","supportsCte",
"supportsDistinctOn","supportsIntersectExcept","supportsIntersectExceptAll","supportsIsTrue","supportsJoinUsing",
"supportsModifyingJoins","supportsMultipleColumnIn","supportsTimestampTimezones","supportsTimestampUsecs",
"supportsWindowFunctions"]
Array of features.
| |
FOR_UPDATE | property | ' FOR UPDATE' | Default FOR UPDATE SQL fragment. |
FROM | property | "FROM" | Default FROM clause |
INSERT_CLAUSE_METHODS | property | clauseMethods("insert" | Default SQL INSERT clause. This may be overrode by adapters. |
IS_LITERALS | property | {NULL: 'NULL', true: 'TRUE', false: 'FALSE'} | Hash of IS literals |
IS_OPERATORS | property | ComplexExpression.IS_OPERATORS | Defaults IS OPERATORS. See patio.sql.ComplexExpression.IS_OPERATORS. |
JOIN_METHODS | property | ["join", "joinTable"] | All methods that return modified datasets with a joined table added. |
NON_SQL_OPTIONS | property | ["server", "defaults", "overrides", "graph", "eagerGraph", "graphAliases"] | Which options don't affect the SQL generation. Used by patio.Dataset#simpleSelectAll to determine if this is a simple SELECT * FROM table. |
NULL | property | "NULL" | Defaults SQL NULL. |
N_ARITY_OPERATORS | property | ComplexExpression.N_ARITY_OPERATORS | Defaults N(Multi arity) OPERATORS. See patio.sql.ComplexExpression.N_ARITY_OPERATORS. |
QUALIFY_KEYS | property | ["select", "where", "having", "order", "group"] | Default SQL clauses that need qualifying. This may be overrode by adapters. |
QUERY_METHODS | property | ['addGraphAliases', "and", "distinct", "eager", "except", "exclude", "filter", "find", "is", "isNot",
"eq", "neq", "lt", "lte", "gt", "gte", "forUpdate", "from", "fromSelf", "graph", "grep", "group",
"groupAndCount", "groupBy", "having", "intersect", "invert", "limit", "lockStyle", "naked", "or", "order",
"orderAppend", "orderBy", "orderMore", "orderPrepend", "qualify", "reverse",
"reverseOrder", "select", "selectAll", "selectAppend", "selectMore", "setDefaults",
"setGraphAliases", "setOverrides", "unfiltered", "ungraphed", "ungrouped", "union", "unlimited",
"unordered", "where", "with", "withRecursive", "withSql"] | Methods that return modified datasets |
QUESTION_MARK | property | /\?/g | Regexp used to replace '?' in patio.sql.PlaceHolderLiteralString |
RETURNING | property | " RETURNING " | Default SQL 'RETURNING' literal string |
SELECT_CLAUSE_METHODS | property | clauseMethods("select" | Default SQL SELECT clause. This may be overrode by adapters. |
SPACE | property | " " | Default space to use when building SQL queries |
SQL_WITH | property | "WITH" | Default SQL WITH base. This may be overrode by adapters. |
TWO_ARITY_OPERATORS | property | ComplexExpression.TWO_ARITY_OPERATORS | Defaults TWO OPERATORS. See patio.sql.ComplexExpression.TWO_ARITY_OPERATORS. |
UNCONDITIONED_JOIN_TYPES | property | ["natural", "naturalLeft", "naturalRight", "naturalFull", "cross"] | These strings have {name}Join methods created (e.g. naturalJoin) that call patio.Dataset#joinTable. They only accept a single table argument which is passed to patio.Dataset#joinTable, and they throw an error if called with a block. |
UPDATE_CLAUSE_METHODS | property | clauseMethods("update" | Default SQL UPDATE clause. This may be overrode by adapters. |
WILDCARD | property | new LiteralString('*') | Default SQL '*' literal string. |
Property | Type | Default Value | Description |
columns | {comb.Promise}
Returns a promise that is resolved with the columns in the result set in order as an array of strings.
If the columns are currently cached, then the promise is immediately resolved with the cached value. Otherwise,
a SELECT query is performed to retrieve a single row in order to get the columns.
If you are looking for all columns for a single table and maybe some information about
each column (e.g. database type), see {@link patio.Database#schema}.
DB.from("table").columns.chain(function(columns){ // => ["id", "name"] }); | function (){
if (this.__columns) {
return asyncArray(this.__columns);
} else {
var ds = this.unfiltered().unordered().mergeOptions({distinct: null, limit: 1});
return asyncArray(ds.forEach().chain(function () {
var columns = this.__columns = ds.__columns || [];
return columns;
}.bind(this)));
}
} | |
firstSourceAlias | String | The first source (primary table) for this dataset. If the table is aliased, returns the aliased name. throws a {patio.DatasetError} tf the dataset doesn't have a table. DB.from("table").firstSourceAlias; //=> "table" DB.from("table___t").firstSourceAlias; //=> "t" | |
firstSourceTable | String | The first source (primary table) for this dataset. If the dataset doesn't have a table, raises a patio.erros.DatasetError. DB.from("table").firstSourceTable; //=> "table" DB.from("table___t").firstSourceTable; //=> "t" | |
hasSelectSource | Boolean | true if this dataset already has a select sources. | |
identifierInputMethod | String | this is the method that will be called on each identifier returned from the database. This value will be defaulted to whatever the identifierInputMethod is on the database used in initialization. | |
identifierOutputMethod | String | this is the method that will be called on each identifier sent to the database. This value will be defaulted to whatever the identifierOutputMethod is on the database used in initialization. | |
isSimpleSelectAll | Boolean | Returns true if this dataset is a simple SELECT * FROM {table}, otherwise false. DB.from("items").isSimpleSelectAll; //=> true DB.from("items").filter({a : 1}).isSimpleSelectAll; //=> false | |
joinSourceList | patio.sql.Identifier[] | [] | a list of join sources |
providesAccurateRowsMatched | boolean | true | Whether this dataset will provide accurate number of rows matched for delete and update statements. Accurate in this case is the number of rows matched by the dataset's filter. |
quoteIdentifiers | boolean | true | Whether this dataset quotes identifiers. |
requiresSqlStandardDate | boolean | false | Whether the dataset requires SQL standard datetimes (false by default, as most allow strings with ISO 8601 format). |
rowCb | Function | callback to be invoked for each row returned from the database. the return value will be used as the result of query. The rowCb can also return a promise, The resolved value of the promise will be used as result. | |
sourceList | patio.sql.Identifier[] | [] | a list of sources for this dataset. |
supportsCte | boolean | true | Whether the dataset supports common table expressions (the WITH clause). |
supportsDistinctOn | boolean | false | Whether the dataset supports the DISTINCT ON clause, false by default. |
supportsIntersectExcept | boolean | true | Whether the dataset supports the INTERSECT and EXCEPT compound operations, true by default. |
supportsIntersectExceptAll | boolean | true | Whether the dataset supports the INTERSECT ALL and EXCEPT ALL compound operations, true by default. |
supportsIsTrue | boolean | true | Whether the dataset supports the IS TRUE syntax. |
supportsJoinUsing | boolean | true | Whether the dataset supports the JOIN table USING (column1, ...) syntax. |
supportsModifyingJoin | boolean | false | Whether modifying joined datasets is supported. |
supportsMultipleColumnIn | boolean | true | Whether the IN/NOT IN operators support multiple columns when an |
supportsTimestampTimezone | boolean | false | Whether the dataset supports timezones in literal timestamps |
supportsTimestampUsecs | boolean | true | Whether the dataset supports fractional seconds in literal timestamps |
supportsWindowFunctions | boolean | false | Whether the dataset supports window functions. |
function (db,opts){ this._super(arguments); this.db = db; this.__opts = {}; this.__rowCb = null; if (db) { this.__quoteIdentifiers = db.quoteIdentifiers; this.__identifierInputMethod = db.identifierInputMethod; this.__identifierOutputMethod = db.identifierOutputMethod; } }
function (){ return this._optionsOverlap(this._static.COUNT_FROM_SELF_OPTS) ? this.fromSelf() : this.unordered(); }
Do a simple join of the arguments (which should be strings) separated by commas
Argumentsfunction (args){ return args.join(this._static.COMMA_SEPARATOR); }
array of elements to make a condition specifier out of
the value to assign a value if one is not provided.
[[]]
an array of two element arrays.
function (arr,defaultOp){ var ret = []; arr.forEach(function (a) { if (isString(a)) { a = this.stringToIdentifier(a); } if (isInstanceOf(a, Identifier)) { ret.push([a, defaultOp]); } else if (isHash(a)) { ret = ret.concat(array.toArray(a)); } else { throw new QueryError("Invalid condition specifier " + a); } }, this); return ret; }
SQL fragment for specifying an alias. expression should already be literalized.
Argumentsfunction (expression,alias){ return string.format("%s AS %s", expression, this.quoteIdentifier(alias)); }
Converts an array of column names into a comma seperated string of column names. If the array is empty, a wildcard (*) is returned.
Argumentsfunction (columns){ return (!columns || columns.length === 0) ? this._static.WILDCARD : this.__expressionList(columns); }
ds.__createBetweenExpression({x : [1,2]}) => //=> WHERE ((x >= 1) AND (x <= 10)) ds.__createBetweenExpression({x : [1,2]}, true) => //=> WHERE ((x < 1) OR (x > 10))Arguments
object where the keys are columns and the values are two element arrays.
if set to true it inverts the between to make it not between the two values
patio.sql.BooleanExpression
a boolean expression containing the between expression.
function (obj,invert){ var pairs = []; for (var i in obj) { var v = obj[i]; if (isArray(v) && v.length) { var ident = this.stringToIdentifier(i); pairs.push(new BooleanExpression("AND", new BooleanExpression("gte", ident, v[0]), new BooleanExpression("lte", ident, v[1]))); } else { throw new QueryError("Between requires an array for the value"); } } var ret = pairs.length === 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs)); return invert ? BooleanExpression.invert(ret) : ret; }
Creates a boolean expression that each key is compared to its value using the provided operator.
Exampleds.__createBoolExpression("gt", {x : 1, y:2, z : 5}) //=> WHERE ((x > 1) AND (y > 2) AND (z > 5)) ds.__createBoolExpression("gt", [[x, 1], [y,2], [z, 5]) //=> WHERE ((x > 1) AND (y > 2) AND (z > 5)) ds.__createBoolExpression("lt", {x : 1, y:2, z : 5}) //=> WHERE ((x < 1) AND (y < 2) AND (z < 5)) ds.__createBoolExpression("lt", [[x, 1], [y,2], [z, 5]) //=> WHERE ((x < 1) AND (y < 2) AND (z < 5))Arguments
valid boolean expression operator to capare each K,V pair with
object or two dimensional array containing key value pairs
patio.sql.BooleanExpression
boolean expression joined by a AND of each key value pair compared by the op
function (op,obj){ var pairs = []; if (Expression.isConditionSpecifier(obj)) { if (isHash(obj)) { obj = array.toArray(obj); } obj.forEach(function (pair) { pairs.push(new BooleanExpression(op, new Identifier(pair[0]), pair[1])); }); } return pairs.length === 1 ? pairs[0] : BooleanExpression.fromArgs(["AND"].concat(pairs)); }
function (opts){ return merge({server: this.__opts.server || "default"}, opts || {}); }
Converts an array of expressions into a comma separated string of expressions.
Argumentsfunction (columns){ return columns.map(this.literal, this).join(this._static.COMMA_SEPARATOR); }
identifier to resolve to a string.
String
the string version of the identifier.
function (identifier){ return isString(identifier) ? this.__hashIdentifierToName(this.stringToIdentifier(identifier)) : isInstanceOf(identifier, Identifier) ? identifier.value : isInstanceOf(identifier, QualifiedIdentifier) ? identifier.column : isInstanceOf(identifier, AliasedExpression) ? identifier.alias : identifier; }
SQL fragment specifying a table name.
function (t){ return isString(t) ? this._quotedIdentifier(t) : this.literal(t); }
DB.from("items").filter({id, [1,2,3]})._addGroupedCondition("AND", "OR", [{price: {lt : 0}}, {price: {gt: 10}]).sql; //=> SELECT * FROM items WHERE ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10))) DB.from("items")._addGroupedCondition("AND", "OR", [{price: {lt : 0}}, {price: {gt: 10}]).sql; //=> SELECT * FROM items WHERE ((price < 0) OR (price > 10))Arguments
String
: there is an existing where/having clause, this arg will join the new condition group to it. "AND" if undef. Should be "AND" or "OR"
String
: conditions will be joined this. Same expectations as the first param.
patio.Dataset
a cloned dataset with the condition group added to the WHERE/HAVING clause.
function (addedByBool,groupedByBool){ groupedByBool = isUndefined(groupedByBool) ? "AND" : groupedByBool; var tOpts = this.__opts, clause = (tOpts.having ? "having" : "where"), clauseObj = tOpts[clause]; var args = argsToArray(arguments, 2); args = args.length === 1 ? args[0] : args; var opts = {}; if (clauseObj) { addedByBool = isUndefined(addedByBool) ? "AND" : addedByBool; opts[clause] = new BooleanExpression(addedByBool, clauseObj, this._filterExpr(args, null, groupedByBool)); } else { opts[clause] = this._filterExpr(args, null, groupedByBool); } return this.mergeOptions(opts); }
Return a [@link patio.sql._Query#fromSelf} dataset if an order or limit is specified, so it works as expected with UNION, EXCEPT, and INTERSECT clauses.
Sourcefunction (){ var opts = this.__opts; return (opts["limit"] || opts["order"]) ? this.fromSelf() : this; }
The alias to use for datasets, takes a number to make sure the name is unique.
Argumentsfunction (number){ return this._static.DATASET_ALIAS_BASE_NAME + number; }
function (){ return this._selectFromSql(); }
function (){ return this._selectOrderSql(); }
function (){ return this._selectWhereSql(); }
function (clause){ var cond = argsToArray(arguments).slice(1), cb; if (cond.length && isFunction(cond[cond.length - 1])) { cb = cond.pop(); } cond = cond.length === 1 ? cond[0] : cond; if ((cond == null || cond === undefined || cond === "") || (isArray(cond) && cond.length === 0 && !cb) || (isObject(cond) && isEmpty(cond) && !cb)) { return this.mergeOptions(); } else { cond = this._filterExpr(cond, cb); var cl = this.__opts[clause]; cl && (cond = new BooleanExpression("AND", cl, cond)); var opts = {}; opts[clause] = cond; return this.mergeOptions(opts); } }
function (expr,cb,joinCond){ expr = (isUndefined(expr) || isNull(expr) || (isArray(expr) && !expr.length)) ? null : expr; if (expr && cb) { return new BooleanExpression(joinCond || "AND", this._filterExpr(expr, null, joinCond), this._filterExpr(cb, null, joinCond)); } else if (cb) { expr = cb; } if (isInstanceOf(expr, Expression)) { if (isInstanceOf(expr, NumericExpression, StringExpression)) { throw new QueryError("Invalid SQL Expression type : " + expr); } return expr; } else if (isArray(expr)) { if (expr.length) { var first = expr[0]; if (isString(first)) { return new PlaceHolderLiteralString(first, expr.slice(1), true); } else if (Expression.isConditionSpecifier(expr)) { return BooleanExpression.fromValuePairs(expr, joinCond); } else { return BooleanExpression.fromArgs([joinCond || "AND"].concat(expr.map(function (e) { return this._filterExpr(e, null, "AND"); }, this))); } } } else if (isFunction(expr)) { return this._filterExpr(expr.call(sql, sql), null, joinCond); } else if (isBoolean(expr)) { return new BooleanExpression("NOOP", expr); } else if (isString(expr)) { return this.stringToIdentifier(expr); } else if (isInstanceOf(expr, LiteralString)) { return new LiteralString("(" + expr + ")"); } else if (isHash(expr)) { return BooleanExpression.fromValuePairs(expr, joinCond); } else { throw new QueryError("Invalid filter argument"); } }
function (order){ var ret = order; if (order) { ret = order.map(function (o) { if (isInstanceOf(o, OrderedExpression)) { return o.invert(); } else { return new OrderedExpression(isString(o) ? new Identifier(o) : o); } }, this); } return ret; }
SQL fragment specifying a JOIN type, splits a camelCased join type and converts to uppercase/
Argumentsfunction (joinType){ return (joinType || "").replace(/([a-z]+)|([A-Z][a-z]+)/g, function (m) { return m.toUpperCase() + " "; }).trimRight() + " JOIN"; }
SQL fragment for Array. Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
function (v){ return Expression.isConditionSpecifier(v) ? this._literalExpression(BooleanExpression.fromValuePairs(v)) : this._arraySql(v); }
SQL fragment for a boolean.
function (b){ return b ? this._static.BOOL_TRUE : this._static.BOOL_FALSE; }
SQL fragment for Buffer, treated as an expression
function (b){ return "X'" + b.toString("hex") + "'"; }
SQL fragment for Dataset. Does a subselect inside parantheses.
function (dataset){ return string.format("(%s)", this._subselectSql(dataset)); }
SQL fragment for Date, using the ISO8601 format.
function (date){ return (this.requiresSqlStandardDateTimes ? "DATE '" : "'") + patio.dateToString(date) + "'"; }
SQL fragment for SQL::Expression, result depends on the specific type of expression.
function (v){ return v.toString(this); }
SQL fragment for Hash, treated as an expression
function (v){ return this._literalExpression(BooleanExpression.fromValuePairs(v)); }
SQL fragment for json. Doubles ' by default.
function (v){ throw new QueryError("Json not supported."); }
SQL fragment for null
function (){ return this._static.NULL; }
SQL fragment for a number.
function (num){ var ret = "" + num; if (isNaN(num) || num === Infinity) { ret = string.format("'%s'", ret); } return ret; }
SQL fragment for Hash, treated as an expression
function (v){ return this._literalExpression(BooleanExpression.fromValuePairs(v)); }
SQL fragment for a type of object not handled by patio.dataset._Sql#literal. If object has a method sqlLiteral then it is called with this dataset as the first argument, otherwise raises an error. Classes implementing sqlLiteral should call a class-specific method on the dataset provided and should add that method to patio.dataset.Dataset, allowing for adapters to provide customized literalizations. If a database specific type is allowed, this should be overriden in a subclass.
function (v){ if (isFunction(v.sqlLiteral)) { return v.sqlLiteral(this); } else { throw new QueryError(string.format("can't express %j as a SQL literal", [v])); } }
SQL fragment for String. Doubles \ and ' by default.
function (v){ var parts = this._splitString(v); var table = parts[0], column = parts[1], alias = parts[2], ret; if (!alias) { if (column && table) { ret = this._literalExpression(QualifiedIdentifier.fromArgs([table, column])); } else { ret = "'" + v.replace(/\\/g, "\\\\").replace(/'/g, "''") + "'"; } } else { if (column && table) { ret = new AliasedExpression(QualifiedIdentifier.fromArgs([table, column]), alias); } else { ret = new AliasedExpression(new Identifier(column), alias); } ret = this.literal(ret); } return ret; }
SQL fragment for a timestamp, using the ISO8601 format.
function (v){ return this.formatTimestamp(v, this._static.TIME_FORMAT); }
SQL fragment for a timestamp, using the ISO8601 format.
function (v){ return this.formatTimestamp(v, this._static.TIMESTAMP_FORMAT); }
SQL fragment for a year.
function (o){ return patio.dateToString(o, this._static.YEAR_FORMAT); }
Return true if the dataset has a non-null value for any key in opts.
Argumentsthe options to compate this datasets options to
Boolean
true if the dataset has a non-null value for any key in opts.
function (opts){ var o = []; for (var i in this.__opts) { if (!isUndefinedOrNull(this.__opts[i])) { o.push(i); } } return intersect(compact(o), opts).length !== 0; }
Qualify the given expression to the given table.
Argumentsthe table to qualify the expression to
patio.sql.Expression
: the expression to qualify
function (e,table){ var h, i, args; if (isString(e)) { //this should not be hit but here just for completeness return this.stringToIdentifier(e); } else if (isArray(e)) { return e.map(function (exp) { return this._qualifiedExpression(exp, table); }, this); } else if (isInstanceOf(e, Identifier)) { return new QualifiedIdentifier(table, e); } else if (isInstanceOf(e, OrderedExpression)) { return new OrderedExpression(this._qualifiedExpression(e.expression, table), e.descending, {nulls: e.nulls}); } else if (isInstanceOf(e, AliasedExpression)) { return new AliasedExpression(this._qualifiedExpression(e.expression, table), e.alias); } else if (isInstanceOf(e, CaseExpression)) { args = [this._qualifiedExpression(e.conditions, table), this._qualifiedExpression(e.def, table)]; if (e.hasExpression) { args.push(this._qualifiedExpression(e.expression, table)); } return CaseExpression.fromArgs(args); } else if (isInstanceOf(e, Cast)) { return new Cast(this._qualifiedExpression(e.expr, table), e.type); } else if (isInstanceOf(e, SQLFunction)) { return SQLFunction.fromArgs([e.f].concat(this._qualifiedExpression(e.args, table))); } else if (isInstanceOf(e, ComplexExpression)) { return ComplexExpression.fromArgs([e.op].concat(this._qualifiedExpression(e.args, table))); } else if (isInstanceOf(e, SubScript)) { return new SubScript(this._qualifiedExpression(e.f, table), this._qualifiedExpression(e.sub, table)); } else if (isInstanceOf(e, PlaceHolderLiteralString)) { args = []; var eArgs = e.args; if (isHash(eArgs)) { h = {}; for (i in eArgs) { h[i] = this._qualifiedExpression(eArgs[i], table); } args = h; } else { args = this._qualifiedExpression(eArgs, table); } return new PlaceHolderLiteralString(e.str, args, e.parens); } else if (isHash(e)) { h = {}; for (i in e) { h[this._qualifiedExpression(i, table) + ""] = this._qualifiedExpression(e[i], table); } return h; } else { return e; } }
the columns selected
function (){ return " " + this.__columnList(this.__opts.select); }
the EXCEPT, INTERSECT, or UNION clause. This uses a subselect for the compound datasets used, because using parantheses doesn't work on all databases.
function (){ var opts = this.__opts, compounds = opts.compounds, ret = []; if (compounds) { compounds.forEach(function (c) { var type = c[0], dataset = c[1], all = c[2]; ret.push(string.format(" %s%s %s", type.toUpperCase(), all ? " ALL" : "", this._subselectSql(dataset))); }, this); } return ret.join(""); }
the DISTINCT clause.
function (){ var distinct = this.__opts.distinct, ret = []; if (distinct) { ret.push(" DISTINCT"); if (distinct.length) { ret.push(format(" ON (%s)", this.__expressionList(distinct))); } } return ret.join(""); }
the sql to add the list of tables to select FROM
function (){ var from = this.__opts.from; return from ? string.format(" %s%s", this._static.FROM, this._sourceList(from)) : ""; }
the GROUP BY clause
function (){ var group = this.__opts.group; return group ? string.format(" GROUP BY %s", this.__expressionList(group)) : ""; }
the sql to add the filter criteria in the HAVING clause
function (){ var having = this.__opts.having; return having ? string.format(" HAVING %s", this.literal(having)) : ""; }
the JOIN clause.
function (){ var join = this.__opts.join, ret = []; if (join) { join.forEach(function (j) { ret.push(this.literal(j)); }, this); } return ret.join(""); }
the LIMIT and OFFSET clauses.
function (){ var ret = [], limit = this.__opts.limit, offset = this.__opts.offset; !isUndefined(limit) && !isNull(limit) && (ret.push(format(" LIMIT %s", this.literal(limit)))); !isUndefined(offset) && !isNull(offset) && (ret.push(format(" OFFSET %s", this.literal(offset)))); return ret.join(""); }
SQL for different locking modes.
function (){ var lock = this.__opts.lock, ret = []; if (lock) { if (lock === "update") { ret.push(this._static.FOR_UPDATE); } else { ret.push(" ", lock); } } return ret.join(""); }
the SQL ORDER BY clause fragment.
function (){ var order = this.__opts.order; return order ? string.format(" ORDER BY %s", this.__expressionList(order)) : ""; }
the SQL WHERE clause fragment.
function (){ var where = this.__opts.where; return where ? string.format(" WHERE %s", this.literal(where)) : ""; }
SQL WITH clause fragment.
function (sql){ var wit = this.__opts["with"]; if (wit && wit.length) { //sql.length = 0; var base = sql.join(""); sql.length = 0; sql.push([this._selectWithSqlBase(), wit.map(function (w) { return [ this.quoteIdentifier(w.name), (w.args ? ("(" + this.__argumentList(w.args) + ")") : ""), " AS ", this._literalDataset(w.dataset) ].join(""); }, this).join(this._static.COMMA_SEPARATOR), base].join(" ")); } }
The base keyword to use for the SQL WITH clause
function (){ return this._static.SQL_WITH; }
Converts an array of source names into into a comma separated list.
Argumentsfunction (source){ if (!Array.isArray(source)) { source = [source]; } if (!source || !source.length) { throw new QueryError("No source specified for the query"); } return " " + source.map( function (s) { return this.__tableRef(s); }, this).join(this._static.COMMA_SEPARATOR); }
Splits a possible implicit alias, handling both patio.sql.AliasedExpressions and strings. Returns an array of two elements, with the first being the main expression, and the second being the alias. Alias may be null if it is a string that does not contain an alias {table}_{alias}.
Argumentsfunction (c){ var ret; if (isInstanceOf(c, AliasedExpression)) { ret = [c.expression, c.alias]; } else if (isString(c)) { var parts = this._splitString(c), cTable = parts[0], column = parts[1], alias = parts[2]; if (alias) { ret = [cTable ? new QualifiedIdentifier(cTable, column) : column, alias]; } else { ret = [c, null]; } } else { ret = [c, null]; } return ret; }
Can either be a string or null.
Example//columns table__column___alias //=> table.column as alias table__column //=> table.column //tables schema__table___alias //=> schema.table as alias schema__table //=> schema.table //name and alias columnOrTable___alias //=> columnOrTable as aliasArguments
String[]
an array with the elements being:
function (s){ var ret, m; if ((m = s.match(this._static.COLUMN_REF_RE1)) !== null) { ret = m.slice(1); } else if ((m = s.match(this._static.COLUMN_REF_RE2)) !== null) { ret = [null, m[1], m[2]]; } else if ((m = s.match(this._static.COLUMN_REF_RE3)) !== null) { ret = [m[1], m[2], null]; } else { ret = [null, s, null]; } return ret; }
SQL to use if this dataset uses static SQL. Since static SQL can be a PlaceholderLiteralString in addition to a String, we literalize nonstrings.
function (sql){ return isString(sql) ? sql : this.literal(sql); }
SQL fragment for a subselect using the given database's SQL.
function (ds){ return ds.sql; }
Returns a string that is the name of the table.
Argumentsthe object to get the table name from.
String
the name of the table.
patio.QueryError
If the name is not a String patio.sql.Identifier, patio.sql.QualifiedIdentifier or patio.sql.AliasedExpression.
function (name){ var ret; if (isString(name)) { var parts = this._splitString(name); var schema = parts[0], table = parts[1], alias = parts[2]; ret = (schema || alias) ? alias || table : table; } else if (isInstanceOf(name, Identifier)) { ret = name.value; } else if (isInstanceOf(name, QualifiedIdentifier)) { ret = this._toTableName(name.column); } else if (isInstanceOf(name, AliasedExpression)) { ret = this.__toAliasedTableName(name.alias); } else { throw new QueryError("Invalid object to retrieve the table name from"); } return ret; }
Return the unaliased part of the identifier. Handles both implicit aliases in strings, as well as patio.sql.AliasedExpressions. Other objects are returned as is.
ArgumentsString|patio.sql.AliasedExpression|*
: the object to un alias
patio.sql.QualifiedIdentifier|String|*
the unaliased portion of the identifier
function (c){ if (isString(c)) { var parts = this._splitString(c); var table = parts[0], column = parts[1]; if (table) { return new QualifiedIdentifier(table, column); } return column; } else if (isInstanceOf(c, AliasedExpression)) { return c.expression; } else { return c; } }
function (){ return this._selectOrderSql(); }
The SQL fragment specifying the columns and values to SET.
function (){ var values = this.__opts.values, defs = this.__opts.defaults, overrides = this.__opts.overrides; var st = [" SET "]; if (isArray(values)) { var v = [], mergedDefsAndOverrides = false, length = values.length, ident, val; for (var i = 0; i < length; i++) { val = values[i]; if (isHash(val)) { mergedDefsAndOverrides = true; val = merge({}, defs || {}, val); val = merge({}, val, overrides || {}); for (var j in val) { ident = this.stringToIdentifier(j); v.push(this.quoteIdentifier(ident) + " = " + this.literal(val[j])); } } else if (isInstanceOf(val, Expression)) { v.push(this._literalExpression(val).replace(/^\(|\)$/g, "")); } else { v.push(val); } } if (!mergedDefsAndOverrides) { val = merge({}, defs || {}); val = merge({}, val, overrides || {}); for (i in val) { ident = this.stringToIdentifier(i); v.push(this.quoteIdentifier(ident) + " = " + this.literal(val[i])); } } st.push(v.join(this._static.COMMA_SEPARATOR)); } else { st.push(values); } return st.join(""); }
SQL fragment specifying the tables to delete from. Includes join table if modifying joins is allowed.
function (sql){ var ret = [this._sourceList(this.__opts.from)]; if (this.supportsModifyingJoins) { ret.push(this._selectJoinSql()); } return ret.join(""); }
function (){ return this._selectWhereSql(); }
Adds the given graph aliases to the list of graph aliases to use, unlike patio.Dataset#setGraphAliases, which replaces the list (the equivalent of patio.Dataset#selectMore when graphing). See patio.Dataset#setGraphAliases.
Examplevar DB = patio.defaultDatabase; // SELECT ..., table.column AS someAlias DB.from("table").addGraphAliases({someAlias : ["table", "column"]); //returns from graphing // => {table : {column : someAlias_value, ...}, ...}Arguments
the graph aliases to use. Where key is the alias name and the value is an array where arr[0] = 'tableName' arr[1] = "colName'.
patio.Dataset
deep copy of the original dataset with the added graphAliases.
function (graphAliases){ var ds = this.selectMore.apply(this, this.__graphAliasColumns(graphAliases)); ds.__opts.graphAliases = merge((ds.__opts.graphAliases || (ds.__opts.graph ? ds.__opts.graph.columnAliases : {}) || {}), graphAliases); return ds; }
function (ae){ return this.__asSql(this.literal(ae.expression), ae.alias); }
Returns a Promise that is resolved with an array with all records in the dataset. If a block is given, the array is iterated over after all items have been loaded.
Example// SELECT * FROM table DB.from("table").all().chain(function(res){ //res === [{id : 1, ...}, {id : 2, ...}, ...]; }); // Iterate over all rows in the table var myArr = []; var rowPromise = DB.from("table").all(function(row){ myArr.push(row);}); rowPromise.chain(function(rows){ //=> rows == myArr; });Arguments
a block to be called with each item. The return value of the block is ignored.
a block to invoke when the action is done
comb.Promise
a promise that is resolved with an array of rows.
function (block,cb){ var self = this; var ret = asyncArray(this.forEach().chain(function (records) { return self.postLoad(records); })); if (block) { ret = ret.forEach(block); } return ret.classic(cb).promise(); }
Same as patio.Dataset@qualifyTo except that it forces qualification on methods called after it has been called.
Example//qualfyTo would generate DB.from("items").qualifyTo("i").filter({id : 1}); //=> SELECT i.* FROM items WHERE (id = 1) //alwaysQualify qualifies filter also. DB.from("items").alwaysQualify("i").filter({id : 1}); //=> SELECT i.* FROM items WHERE (i.id = 1)Arguments
this.firstSourceAlias
] : the table to qualify to.
patio.Dataset
a cloned dataset that will always qualify.
function (table){ return this.mergeOptions({alwaysQualify: table || this.firstSourceAlias}); }
Adds a further filter to an existing filter using AND. This method is identical to patio.Dataset#filter except it expects an existing filter.
For parameter types see patio.Dataset#filter.
ExampleDB.from("table").filter("a").and("b").sql; //=>SELECT * FROM table WHERE a AND bReturns
patio.Dataset
a cloned dataset with the condition added to the WHERE/HAVING clause added.
patio.QueryError
If no WHERE?HAVING clause exists.
function (){ var tOpts = this.__opts, clauseObj = tOpts[tOpts.having ? "having" : "where"]; if (clauseObj) { return this.filter.apply(this, arguments); } else { throw new QueryError("No existing filter found"); } }
Adds a group of ANDed conditions wrapped in parens to an existing where/having clause by an AND. If there isn't yet a clause, a where clause is created with the ANDed conditions
For parameter types see patio.Dataset#filter.
ExampleDB.from("items").filter({id, [1,2,3]}).andGroupedAnd([{price: {gt : 0}}, {price: {lt: 10}]).sql; //=> SELECT * FROM items WHERE ((id IN (1, 2, 3)) AND ((price > 0) AND (price < 10))) DB.from("items").andGroupedAnd([{price: {gt : 0}}, {price: {lt: 10}]).sql; //=> SELECT * FROM items WHERE ((price > 0) AND (price < 10))Arguments
patio.Dataset
a cloned dataset with the condition 'and group' added to the WHERE/HAVING clause.
function (filterExp){ return this._addGroupedCondition("AND", "AND", filterExp); }
Adds a group of ORed conditions wrapped in parens, connected to an existing where/having clause by an AND. If the where/having clause doesn't yet exist, a where clause is created with the ORed group.
For parameter types see patio.Dataset#filter.
ExampleDB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql; //=> SELECT * FROM items WHERE ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10))) DB.from("items").andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql; //=> SELECT * FROM items WHERE ((price < 0) OR (price > 10)) DB.from("items").filter({x:1}).andGroupedOr([{a:1, b:2}, {c:3, d:4}]).sql; //=> SELECT * FROM items WHERE ((x = 1) AND (((a = 1) AND (b = 2)) OR ((c = 3) AND (d = 4)))Arguments
patio.Dataset
a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.
function (filterExp){ return this._addGroupedCondition("AND", "OR", filterExp); }
Returns a promise that is resolved with the average value for the given column.
Example// SELECT avg(number) FROM table LIMIT 1 DB.from("table").avg("number").chain(function(avg){ // avg === 3 });Arguments
the column to average
the callback to invoke when the action is done.
comb.Promise
a promise that is resolved with the average value of the column.
function (column,cb){ return this.__aggregateDataset().get(sql.avg(this.stringToIdentifier(column)), cb); }
Returns a cloned dataset with a between clause added to the where clause.
Exampleds.notBetween({x:[1, 2]}).sql; //=> SELECT * FROM test WHERE ((x >= 1) OR (x <= 2)) ds.find({x:{notBetween:[1, 2]}}).sql; //=> SELECT * FROM test WHERE ((x >= 1) OR (x <= 2))Arguments
object where the key is the column and the value is an array where the first element is the item to be greater than or equal to than and the second item is less than or equal to than.
patio.Dataset
a cloned dataset with a between clause added to the where clause.
function (obj){ return this.filter(this.__createBetweenExpression(obj)); }
function (constant){ return this.literal(constant); }
function (ce){ var sql = ['(CASE ']; if (ce.expression) { sql.push(this.literal(ce.expression), " "); } var conds = ce.conditions; if (isArray(conds)) { conds.forEach(function (cond) { sql.push(format("WHEN %s THEN %s", this.literal(cond[0]), this.literal(cond[1]))); }, this); } else if (isHash(conds)) { for (var i in conds) { sql.push(format("WHEN %s THEN %s", this.literal(i), this.literal(conds[i]))); } } return format("%s ELSE %s END)", sql.join(""), this.literal(ce.def)); }
function (expr,type){ return string.format("CAST(%s AS %s)", this.literal(expr), this.db.castTypeLiteral(type)); }
function (ca){ return string.format("%s.*", this.quoteSchemaTable(ca.table)); }
function (op,args){ var newOp; var isOperators = this._static.IS_OPERATORS, isLiterals = this._static.IS_LITERALS, l; if ((newOp = isOperators[op]) != null) { var r = args[1], v = isNull(r) ? isLiterals.NULL : isLiterals[r]; if (r == null || this.supportsIsTrue) { if (isUndefined(v)) { throw new QueryError(string.format("Invalid argument('%s') used for IS operator", r)); } l = args[0]; return string.format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp, v); } else if (op === "IS") { return this.complexExpressionSql("EQ", args); } else { return this.complexExpressionSql("OR", [BooleanExpression.fromArgs(["NEQ"].concat(args)), new BooleanExpression("IS", args[0], null)]); } } else if (["IN", "NOTIN"].indexOf(op) !== -1) { var cols = args[0], vals = args[1], colArray = isArray(cols), valArray = false, emptyValArray = false; if (isArray(vals)) { valArray = true; emptyValArray = vals.length === 0; } if (colArray) { if (emptyValArray) { if (op === "IN") { return this.literal(BooleanExpression.fromValuePairs(cols.map(function (x) { return [x, x]; }), "AND", true)); } else { return this.literal({1: 1}); } } else if (!this.supportsMultipleColumnIn) { if (valArray) { var expr = BooleanExpression.fromArgs(["OR"].concat(vals.map(function (vs) { return BooleanExpression.fromValuePairs(array.zip(cols, vs)); }))); return this.literal(op === "IN" ? expr : expr.invert()); } } else { //If the columns and values are both arrays, use _arraySql instead of //literal so that if values is an array of two element arrays, it //will be treated as a value list instead of a condition specifier. return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols), ComplexExpression.IN_OPERATORS[op], valArray ? this._arraySql(vals) : this.literal(vals)); } } else { if (emptyValArray) { if (op === "IN") { return this.literal(BooleanExpression.fromValuePairs([ [cols, cols] ], "AND", true)); } else { return this.literal({1: 1}); } } else { return format("(%s %s %s)", isString(cols) ? cols : this.literal(cols), ComplexExpression.IN_OPERATORS[op], this.literal(vals)); } } } else if ((newOp = this._static.TWO_ARITY_OPERATORS[op]) != null) { l = args[0]; return format("(%s %s %s)", isString(l) ? l : this.literal(l), newOp, this.literal(args[1])); } else if ((newOp = this._static.N_ARITY_OPERATORS[op]) != null) { return string.format("(%s)", args.map(this.literal, this).join(" " + newOp + " ")); } else if (op === "NOT") { return string.format("NOT %s", this.literal(args[0])); } else if (op === "NOOP") { return this.literal(args[0]); } else { throw new QueryError("Invalid operator " + op); } }
Add the dataset to the list of compounds
Argumentsthe type of compound (i.e. "union", "intersect")
the dataset to add to
[options={}] compound option to use (i.e {all : true})
patio.Dataset
ds with the dataset added to the compounds.
function (type,dataset,options){ var ds = this._compoundFromSelf().mergeOptions({compounds: (array.toArray(this.__opts.compounds || [])).concat([ [type, dataset._compoundFromSelf(), options.all] ])}); return options.fromSelf === false ? ds : ds.fromSelf(options); }
function (constant){ return "" + constant; }
Returns a promise that is resolved with the number of records in the dataset.
Example// SELECT COUNT(*) AS count FROM table LIMIT 1 DB.from("table").count().chain(function(count){ //count === 3; });Arguments
the callback to invoke when the action is done.
comb.Promise
a promise that is resolved with the the number of records in the dataset.
function (cb){ return this.__aggregateDataset().get(sql.COUNT(sql.literal("*")).as("count")).chain(function (res) { return parseInt(res, 10); }).classic(cb); }
Returns a copy of the dataset with the SQL DISTINCT clause. The DISTINCT clause is used to remove duplicate rows from the output. If arguments are provided, uses a DISTINCT ON clause, in which case it will only be distinct on those columns, instead of all returned columns.
ExampleDB.from("items").distinct().sqll //=> SELECT DISTINCT * FROM items DB.from("items").order("id").distinct("id").sql; //=> SELECT DISTINCT ON (id) * FROM items ORDER BY idArguments
variable number of arguments used to create the DISTINCT ON clause.
patio.Dataset
a cloned dataset with the DISTINCT/DISTINCT ON clause added.
patio.QueryError
If arguments are given and DISTINCT ON is not supported.
function (args){ args = argsToArray(arguments); if (args.length && !this.supportsDistinctOn) { throw new QueryError("DISTICT ON is not supported"); } args = args.map(function (a) { return isString(a) ? new Identifier(a) : a; }); return this.mergeOptions({distinct: args}); }
Allows the loading of another query and combining them in one patio command. Queries can be related or completely unrelated. All data comes back as JSON NOT Patio models.
ExampleDB.from('company').filter({name: 'Amazon'}) .eager({ // company from parent query is passed in and usable in the eager query. leader: (company) => DB.from('leader').filter({id: company.leaderId}).one() }) // Load completely unrelated data. .eager({org: () => DB.from('organization').one() }) .one()}) { id: 1, name: 'Amazon.com', leader: { id: 1, name: 'Jeff' }, org: { id: 1, name: 'Google Inc.' } } Can do one to many loading for every item in the parent dataset. Be careful doing this because it can lead to lots of extra queries. DB.from('company').filter({state: 'IA'}) .eager({invoices: (company) => DB.from('invoices').filter({companyId: company.id}).one() }) .all()}) [ { id: 1, name: 'Principal', invoices: [ { id: 1, amount: 200}, { id: 2, amount: 300}, ] }, { id: 2, name: 'John Deere', invoices: [ { id: 3, amount: 200}, { id: 4, amount: 300}, ] } ]Arguments
function (includeDatasets,fromModel){ var ds = this.mergeOptions({}), originalRowCb = ds.rowCb; if(!ds.__opts._eagerAssoc) { ds.__opts._eagerAssoc = includeDatasets; ds.rowCb = function (topLevelResults) { function toObject(thing) { if (!thing) { return comb.when(thing); } if (Array.isArray(thing)) { return comb.when(thing.map(function(item) { return toObject(item); })); } if ('toObject' in thing) { return comb.when(thing.toObject()); } return comb.when(thing); } var eagerResults = {}, whens = []; if (!originalRowCb) { // pass through for when topLevelResults is already resolved originalRowCb = function(r){return r;}; } return comb.when(originalRowCb(topLevelResults)).chain(function(maybeModel) { whens = Object.keys(ds.__opts._eagerAssoc).map(function(key) { return ds.__opts._eagerAssoc[key](maybeModel).chain(function(result) { return toObject(result).chain(function(res) { eagerResults[key] = res; return result; }); }); }); return comb.when(whens).chain(function () { return toObject(maybeModel).chain(function(json) { // merge associations on to main data return Object.assign(json, eagerResults); }); }); }); }; } return ds.mergeOptions({ _eagerAssoc: Object.assign(ds.__opts._eagerAssoc, includeDatasets) }); }
Returns a cloned dataset with an equal expression added to the WHERE clause.
ExampleDB.from("test").eq({x : 1}); //=> SELECT * FROM test WHERE (x = 1) DB.from("test").eq({x : 1, y : 10}); //=> SELECT * FROM test WHERE ((x = 1) AND (y = 10))Arguments
object used to create the equal expression
patio.Dataset
a cloned dataset with the equal expression added to the WHERE clause.
function (obj){ return this.filter(this.__createBoolExpression("eq", obj)); }
Adds an EXCEPT clause using a second dataset object. An EXCEPT compound dataset returns all rows in the current dataset that are not in the given dataset.
ExampleDB.from("items").except(DB.from("other_items")).sql; //=> SELECT * FROM items EXCEPT SELECT * FROM other_items DB.from("items").except(DB.from("other_items"), {all : true, fromSelf : false}).sql; //=> SELECT * FROM items EXCEPT ALL SELECT * FROM other_items DB.from("items").except(DB.from("other_items"), {alias : "i"}).sql; //=>SELECT * FROM (SELECT * FROM items EXCEPT SELECT * FROM other_items) AS iArguments
the dataset to use to create the EXCEPT clause.
options to use when creating the EXCEPT clause
String|patio.sql.Identifier
: Use the given value as the patio.Dataset#fromSelf alias.
Boolean
: Set to true to use EXCEPT ALL instead of EXCEPT, so duplicate rows can occur
Boolean
: Set to false to not wrap the returned dataset in a patio.Dataset#fromSelf, use with care.
patio.Dataset
a cloned dataset with the EXCEPT clause added.
patio.QueryError
if the operation is not supported.
function (dataset,opts){ opts = isUndefined(opts) ? {} : opts; if (!isHash(opts)) { opts = {all: true}; } if (!this.supportsIntersectExcept) { throw new QueryError("EXCEPT not supoorted"); } else if (opts.hasOwnProperty("all") && !this.supportsIntersectExceptAll) { throw new QueryError("EXCEPT ALL not supported"); } return this.compoundClone("except", dataset, opts); }
Performs the inverse of patio.Dataset#filter. Note that if you have multiple filter conditions, this is not the same as a negation of all conditions. For argument types see patio.Dataset#filter
ExampleDB.from("items").exclude({category : "software").sql; //=> SELECT * FROM items WHERE (category != 'software') DB.from("items").exclude({category : 'software', id : 3}).sql; //=> SELECT * FROM items WHERE ((category != 'software') OR (id != 3))Returns
patio.Dataset
a cloned dataset with the excluded conditions applied to the HAVING/WHERE clause.
function (){ var cond = argsToArray(arguments), tOpts = this.__opts; var clause = (tOpts["having"] ? "having" : "where"), clauseObj = tOpts[clause]; cond = cond.length > 1 ? cond : cond[0]; cond = this._filterExpr.call(this, cond); cond = BooleanExpression.invert(cond); if (clauseObj) { cond = new BooleanExpression("AND", clauseObj, cond); } var opts = {}; opts[clause] = cond; return this.mergeOptions(opts); }
function (sql,opts){ return this.db.execute(sql, merge({server: this.__opts.server || "readOnly"}, opts || {})); }
function (sql,opts){ return this.db.executeDdl(sql, this.__defaultServerOpts(opts || {})); }
function (sql,opts){ return this.db.executeDui(sql, this.__defaultServerOpts(opts || {})); }
function (sql,opts){ return this.db.executeInsert(sql, this.__defaultServerOpts(opts || {})); }
function (sql,opts){ opts = opts || {}; var ret; if (opts.stream) { ret = this.fetchStreamedRows(sql, opts); } else { ret = this.fetchPromisedRows(sql, opts); } return ret; }
Returns a copy of the dataset with the given conditions applied to it. If the query already has a HAVING clause, then the conditions are applied to the HAVING clause otherwise they are applied to the WHERE clause.
ExampleDB.from("items").filter({id : 3}).sql; //=> SELECT * FROM items WHERE (id = 3) DB.from("items").filter('price < ?', 100) //=> SELECT * FROM items WHERE price < 100 DB.from("items").filter({id, [1,2,3]}).filter({id : {between : [0, 10]}}).sql; //=> SELECT * FROM items WHERE ((id IN (1, 2, 3)) AND ((id >= 0) AND (id <= 10))) DB.from("items").filter('price < 100'); //=> SELECT * FROM items WHERE price < 100 DB.from("items").filter("active").sql; //=> SELECT * FROM items WHERE active DB.from("items").filter(function(){ return this.price.lt(100); }); //=> SELECT * FROM items WHERE (price < 100) //Multiple filter calls can be chained for scoping: DB.from("items").filter(:category => 'software').filter{price < 100} //=> SELECT * FROM items WHERE ((category = 'software') AND (price < 100))Arguments
filters to apply to the WHERE/HAVING clause. Description of each:
filter also takes a cb, which should return one of the above argument types, and is treated the same way. This block is called with an patio.sql object which can be used to dynaically create expression. For more details on the sql object see patio.sql
NOTE:If both a cb and regular arguments are provided, they get ANDed together.
patio.Dataset
a cloned dataset with the filter arumgents applied to the WHERE/HAVING clause.
function (args,cb){ args = [this.__opts["having"] ? "having" : "where"].concat(argsToArray(arguments)); return this._filter.apply(this, args); }
function (){ var args = [this.__opts["having"] ? "having" : "where"].concat(argsToArray(arguments)); return this._filter.apply(this, args); }
If a integer argument is given, it is interpreted as a limit, and then returns all matching records up to that limit.
If no arguments are passed, it returns the first matching record.
If a function taking no arguments is passed in as the last parameter then it is assumed to be a filter block. If the a funciton is passed in that takes arguments then it is assumed to be a callback. You may also pass in both the second to last argument being a filter function, and the last being a callback.
If any other type of argument(s) is passed, it is given to patio.Dataset#filter and the first matching record is returned. Examples:
Examplecomb.executeInOrder(DB.from("table"), function(ds){ // SELECT * FROM table LIMIT 1 ds.first(); // => {id : 7} // SELECT * FROM table LIMIT 2 ds.first(2); // => [{id : 6}, {id : 4}] // SELECT * FROM table WHERE (id = 2) LIMIT 1 ds.first({id : 2}) // => {id : 2} // SELECT * FROM table WHERE (id = 3) LIMIT 1 ds.first("id = 3"); // => {id : 3} // SELECT * FROM table WHERE (id = 4) LIMIT 1 ds.first("id = ?", 4); // => {id : 4} // SELECT * FROM table WHERE (id > 2) LIMIT 1 ds.first(function(){return this.id.gt(2);}); // => {id : 5} // SELECT * FROM table WHERE ((id > 4) AND (id < 6)) LIMIT 1 ds.first("id > ?", 4, function(){ return this.id.lt(6); }); // => {id : 5} // SELECT * FROM table WHERE (id < 2) LIMIT 2 ds.first(2, function(){ return this.id.lt(2) }); // => [{id:1}] });Arguments
varargs to be used to limit/filter the result set.
comb.Promise
a promise that is resolved with the either the first matching record. Or an array of items if a limit was provided as the first argument.
function (args){ args = comb(arguments).toArray(); var cb, block = isFunction(args[args.length - 1]) ? args.pop() : null; if (block && block.length > 0) { cb = block; block = isFunction(args[args.length - 1]) ? args.pop() : null; } var ds = block ? this.filter(block) : this; if (!args.length) { return ds.singleRecord(cb); } else { args = (args.length === 1) ? args[0] : args; if (isNumber(args)) { return ds.limit(args).all(null, cb); } else { return ds.filter(args).singleRecord(cb); } } }
Iterates over the records in the dataset as they are returned from the database adapter.
Example// SELECT * FROM table DB.from("table").forEach(function(row){ //....do something });Arguments
the block to invoke for each row.
the callback to invoke when the action is done.
comb.Promise
a promise that is resolved when the action has completed.
function (block,cb){ var rowCb, ret; if (this.__opts.graph) { ret = this.graphEach(block); } else { ret = this.fetchRows(this.selectSql); if ((rowCb = this.rowCb)) { ret = ret.map(function (r) { return rowCb(r); }); } if (block) { ret = ret.forEach(block); } } return ret.classic(cb); }
DB.from("table").forUpdate() //=> SELECT * FROM table FOR UPDATEReturns
patio.Dataset
a cloned dataset with a "update" lock style.
function (){ return this.lockStyle("update"); }
Returns a copy of the dataset with the source changed. If no source is given, removes all tables. If multiple sources are given, it is the same as using a CROSS JOIN (cartesian product) between all tables.
Examplevar dataset = DB.from("items"); dataset.from().sql; //=> SELECT * dataset.from("blah").sql //=> SELECT * FROM blah dataset.from("blah", "foo") //=> SELECT * FROM blah, foo dataset.from({a:"b"}).sql; //=> SELECT * FROM a AS b dataset.from(dataset.from("a").group("b").as("c")).sql; //=> "SELECT * FROM (SELECT * FROM a GROUP BY b) AS c"Arguments
tables to select from
patio.Dataset
a cloned dataset with the FROM clause overridden.
function (source){ source = argsToArray(arguments); var tableAliasNum = 0, sources = []; source.forEach(function (s) { if (isInstanceOf(s, Dataset)) { sources.push(new AliasedExpression(s, this._datasetAlias(++tableAliasNum))); } else if (isHash(s)) { for (var i in s) { sources.push(new AliasedExpression(new Identifier(i), s[i])); } } else if (isString(s)) { sources.push(this.stringToIdentifier(s)); } else { sources.push(s); } }, this); var o = {from: sources.length ? sources : null}; if (tableAliasNum) { o.numDatasetSources = tableAliasNum; } return this.mergeOptions(o); }
Returns a dataset selecting from the current dataset. Supplying the alias option controls the alias of the result.
Exampleds = DB.from("items").order("name").select("id", "name") //=> SELECT id,name FROM items ORDER BY name ds.fromSelf().sql; //=> SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS t1 ds.fromSelf({alias : "foo"}).sql; //=> SELECT * FROM (SELECT id, name FROM items ORDER BY name) AS fooArguments
options
String|patio.sql.Identifier
: alias to use
patio.Dataset
a cloned dataset with the FROM clause set as the current dataset.
function (opts){ opts = isUndefined(opts) ? {} : opts; var fs = {}; var nonSqlOptions = this._static.NON_SQL_OPTIONS; Object.keys(this.__opts).forEach(function (k) { if (nonSqlOptions.indexOf(k) === -1) { fs[k] = null; } }); return this.mergeOptions(fs).from(opts["alias"] ? this.as(opts["alias"]) : this); }
function (f){ var args = f.args; return string.format("%s%s", f.f, args.length === 0 ? '()' : this.literal(args)); }
Return the column value for the first matching record in the dataset.
Example// SELECT id FROM table LIMIT 1 DB.from("table").get("id").chain(function(val){ // val === 3 }); // SELECT sum(id) FROM table LIMIT 1 ds.get(sql.sum("id")).chain(function(val){ // val === 6; }); // SELECT sum(id) FROM table LIMIT 1 ds.get(function(){ return this.sum("id"); }).chain(function(val){ // val === 6; });Arguments
the column to filter on can be anything that patio.Dataset#select accepts.
the callback to invoke when the action is done.
comb.Promise
a promise that will be resolved will the value requested.
function (column,cb){ return this.select(column).singleValue(cb); }
Allows you to join multiple datasets/tables and have the result set split into component tables.
This differs from the usual usage of join, which returns the result set as a single hash.
//CREATE TABLE artists (id INTEGER, name TEXT); //CREATE TABLE albums (id INTEGER, name TEXT, artist_id INTEGER); var DB = patio.defaultDatabase, ds = db.from("artists"); ds.leftOuterJoin("albums", {artistId : "id"}).first //=> {id : albums.id, name : albums.name, artist_id : albums.artist_id} var p = comb.executeInOrder(ds, function(ds){ var graphedDs = ds.graph("albums", {artist_id : "id"}); return graphedDs.first(); }); p.chain(function(obj){ //obj == {artists : { id : artists.id, name : artists.name }, albums : { id : albums.id, name : albums.name, artist_id=>albums.artist_id } } });
Using a join such as leftOuterJoin, the attribute names that are shared between the tables are combined in the single return hash. You can get around that by using patio.Dataset#select with correct aliases for all of the columns, but it is simpler to use patio.Dataset#graph and have the result set split for you. In addition, patio.Dataset#graph respects any patio.Dataset#rowCb of the current dataset and the datasets you use with patio.Dataset#graph.
If you are graphing a table and all columns for that table are null, this indicates that no matching rows existed in the table, so graph will return null. instead of a hash with all nil values:
// Psuedo code there will be promises returned /// If the artist doesn't have any albums var DB = patio.defaultDatabase, ds = db.from("artists"); var obj = ds.graph(:albums, :artist_id=>:id).first() //obj == { artists : {id : artists.id, name : artists.name}, albums : null};Arguments
This can be a string (representing a table), another patio.Dataset, or an object that has a dataset property that returns a string or dataset.
Any condition(s) allowed by patio.Dataset#joinTable.
options to use when creating the graph
A function that is passed to patio.Dataset#joinTable.
String|sql.LiteralString|sql.Identifier
: The alias to use when the receiver is not a graphed dataset but it contains multiple FROM tables or a JOIN. In this case, the receiver is wrapped in a patio.Dataset#fromSelf before graphing, and this option determines the alias to use.
String|sql.LiteralString|sql.Identifier
: The qualifier of implicit conditions, see patio.Dataset#joinTable.
[options.joinType="leftOuter"] The type of join to use (passed to patio.Dataset#joinTable.).
[options.select] An array of columns to select. When not used, selects all columns in the given dataset. When set to false, selects no columns and is like simply joining the tables, though graph keeps some metadata about the join that makes it important to use patio.Dataset#graph instead of patio.Dataset#joinTable
String|sql.LiteralString|sql.Identifier
: The alias to use for the table. If not specified, doesn't alias the table. You will get an error if the the alias (or table) name is used more than once.
function (dataset,joinConditions,options,block){ var ret = new Promise(); var args = argsToArray(arguments, 1); block = isFunction(args[args.length - 1]) ? args.pop() : null; joinConditions = args.shift() || null; options = args.shift() || {}; // Allow the use of a model, dataset, or string as the first argument // Find the table name/dataset based on the argument dataset.hasOwnProperty("dataset") && (dataset = dataset.dataset); var tableAlias = options.tableAlias, table; if (isString(dataset)) { table = sql.identifier(dataset); dataset = this.db.from(dataset); isUndefinedOrNull(tableAlias) && (tableAlias = table); } else if (isInstanceOf(dataset, Dataset)) { if (dataset.isSimpleSelectAll) { table = dataset.__opts.from[0]; isUndefinedOrNull(tableAlias) && (tableAlias = table); } else { table = dataset; isUndefinedOrNull(tableAlias) && (tableAlias = this._datasetAlias((this.__opts.numDatasetSources || 0) + 1)); } } else { throw new QueryError("The dataset arg should be a string, dataset or model"); } var aliases; // Only allow table aliases that haven't been used var thisOpts = this.__opts, thisOptsGraph = thisOpts.graph; if (isObject(thisOptsGraph) && isHash((aliases = thisOptsGraph.tableAliases)) && !isUndefinedOrNull(aliases[tableAlias.value])) { raiseAliasError(options); } // Use a from_self if this is already a joined table var ds = (!thisOptsGraph && (thisOpts.from.length > 1 || thisOpts.join)) ? this.fromSelf({alias: options.fromSelfAlias || this.firstSourceAlias}) : this; // Join the table early in order to avoid cloning the dataset twice ds = ds.joinTable(options.joinType || "leftOuter", table, joinConditions, {tableAlias: tableAlias, implicitQualifier: options.implicitQualifier}, block); var opts = ds.__opts; // Whether to include the table in the result set var addTable = isBoolean(options.select) ? options.select : true; // Whether to add the columns to the list of column aliases var addColumns = isUndefinedOrNull(opts.graphAliases); // Setup the initial graph data structure if it doesn't exist var graph; var populateGraphPromise; if (isUndefinedOrNull((graph = opts.graph))) { var master = this._toTableName(ds.firstSourceAlias); ("" + master === "" + tableAlias) && raiseAliasError(options); // Master hash storing all .graph related information graph = opts.graph = {}; // Associates column aliases back to tables and columns var columnAliases = graph.columnAliases = {}; // Associates table alias (the master is never aliased) var tableAliases = graph.tableAliases = {}; tableAliases[master] = this; // All columns in the master table are never // aliased, but are not included if set_graph_aliases // has been used. if (addColumns) { var select = opts.select = []; populateGraphPromise = this.columns.chain(function (cols) { cols.forEach(function (column) { columnAliases[column] = [master, column]; select.push(new sql.QualifiedIdentifier(master, column)); }); return graph; }); } else { populateGraphPromise = new Promise().callback(graph); } } else { populateGraphPromise = new Promise().callback(graph); } return populateGraphPromise.chain(function (graph) { var ret; // Add the table alias to the list of aliases // Even if it isn't been used in the result set, // we add a key for it with a nil value so we can check if it // is used more than once var tableAliases = graph.tableAliases; tableAliases[tableAlias] = addTable ? dataset : null; // Add the columns to the selection unless we are ignoring them if (addTable && addColumns) { var select = opts.select; var columnAliases = graph.columnAliases; // Which columns to add to the result set var dsColPromise; if (options.select) { dsColPromise = new Promise().callback(options.select); } else { dsColPromise = dataset.columns; } // If the column hasn't been used yet, don't alias it. // If it has been used, try tableColumn. ret = dsColPromise.chain(function (cols) { cols.forEach(function (column) { var colAlias, identifier; if (columnAliases[column]) { var columnAlias = format("%s_%s", [tableAlias, column]); colAlias = columnAlias; identifier = new sql.QualifiedIdentifier(tableAlias, column).as(columnAlias); } else { colAlias = column; identifier = new sql.QualifiedIdentifier(tableAlias, column); } columnAliases[colAlias] = [tableAlias, column]; select.push(identifier); }); return ds; }); } else { ret = ds; } return ret; }); }
Fetch the rows, split them into component table parts, transform and run the patio.Dataset#rowCb on each part (if applicable), and yield a hash of the parts.
Argumentsfunction (cb){ // Reject tables with nil datasets, as they are excluded from // the result set var datasets = toArray(this.__opts.graph.tableAliases).filter(function (e) { return !isUndefinedOrNull(e[1]); }); // Get just the list of table aliases into a local variable, for speed var tableAliases = datasets.map(function (e) { return e[0]; }); datasets = datasets.map(function (e) { return [e[0], e[1], e[1].rowCb]; }); // Use the manually set graph aliases, if any, otherwise // use the ones automatically created by .graph var columnAliases = this.__opts.graphAliases || this.__opts.graph.columnAliases; var ret = this.fetchRows(this.selectSql).map(function (r) { var graph = {}; // Create the sub hashes, one per table tableAliases.forEach(function (ta) { graph[ta] = {}; }); // Split the result set based on the column aliases // If there are columns in the result set that are // not in column_aliases, they are ignored for (var colAlias in columnAliases) { var tc = columnAliases[colAlias]; var ta = tc[0], column = tc[1]; !graph[ta] && (graph[ta] = {}); graph[ta][column] = r[colAlias]; } datasets.forEach(function (d) { var ta = d[0], ds = d[1], dsCb = d[2]; var g = graph[ta]; if (!isEmpty(g) && Object.keys(g).some(function (x) { return !isUndefinedOrNull(g[x]); })) { graph[ta] = dsCb ? dsCb(g) : g; } else { graph[ta] = null; } }); return graph; }); if (cb) { ret.forEach(cb); } return ret; }
Match any of the columns to any of the patterns. The terms can be strings (which use LIKE) or regular expressions (which are only supported on MySQL and PostgreSQL). Note that the total number of pattern matches will be columns[].length * terms[].length, which could cause performance issues.
ExampleDB.from("items").grep("a", "%test%").sql; //=> SELECT * FROM items WHERE (a LIKE '%test%'); DB.from("items").grep(["a", "b"], ["%test%" "foo"]).sql; //=> SELECT * FROM items WHERE ((a LIKE '%test%') OR (a LIKE 'foo') OR (b LIKE '%test%') OR (b LIKE 'foo')) DB.from("items").grep(['a', 'b'], ["%foo%", "%bar%"], {allPatterns : true}).sql; //=> SELECT * FROM a WHERE (((a LIKE '%foo%') OR (b LIKE '%foo%')) AND ((a LIKE '%bar%') OR (b LIKE '%bar%'))) DB.from("items").grep(["a", "b"], ['%foo%", "%bar%", {allColumns : true})sql; //=> SELECT * FROM a WHERE (((a LIKE '%foo%') OR (a LIKE '%bar%')) AND ((b LIKE '%foo%') OR (b LIKE '%bar%'))) DB.from("items").grep(["a", "b"], ["%foo%", "%bar%"], {allPatterns : true, allColumns : true}).sql; //=> SELECT * FROM a WHERE ((a LIKE '%foo%') AND (b LIKE '%foo%') AND (a LIKE '%bar%') AND (b LIKE '%bar%'))Arguments
columns to search
patters to search with
options to use when searching. NOTE If both allColumns and allPatterns are true, all columns must match all patterns
Boolean
: All columns must be matched to any of the given patterns.
Boolean
: All patterns must match at least one of the columns.
Boolean
: Use a case insensitive pattern match (the default is case sensitive if the database supports it).
patio.Dataset
a dataset with the LIKE clauses added
function (columns,patterns,opts){ opts = isUndefined(opts) ? {} : opts; var conds; if (opts.hasOwnProperty("allPatterns")) { conds = array.toArray(patterns).map(function (pat) { return BooleanExpression.fromArgs( [(opts.allColumns ? "AND" : "OR")] .concat(array.toArray(columns) .map(function (c) { return StringExpression.like(c, pat, opts); }))); }); return this.filter(BooleanExpression.fromArgs([opts.allPatterns ? "AND" : "OR"].concat(conds))); } else { conds = array.toArray(columns) .map(function (c) { return BooleanExpression.fromArgs(["OR"].concat(array.toArray(patterns).map(function (pat) { return StringExpression.like(c, pat, opts); }))); }); return this.filter(BooleanExpression.fromArgs([opts.allColumns ? "AND" : "OR"].concat(conds))); } }
Returns a copy of the dataset with the results grouped by the value of the given columns.
ExampleDB.from("items").group("id") //=>SELECT * FROM items GROUP BY id DB.from("items").group("id", "name") //=> SELECT * FROM items GROUP BY id, nameArguments
columns to group by.
patio.Dataset
a cloned dataset with the GROUP BY clause added.
function (columns){ columns = argsToArray(arguments); var self = this; return this.mergeOptions({group: (array.compact(columns).length === 0 ? null : columns.map(function (c) { return isString(c) ? self.stringToIdentifier(c) : c; }))}); }
Returns a dataset grouped by the given column with count by group. Column aliases may be supplied, and will be included in the select clause.
ExampleDB.from("items").groupAndCount("name").all() //=> SELECT name, count(*) AS count FROM items GROUP BY name //=> [{name : 'a', count : 1}, ...] DB.from("items").groupAndCount("first_name", "last_name").all() //SELECT first_name, last_name, count(*) AS count FROM items GROUP BY first_name, last_name //=> [{first_name : 'a', last_name : 'b', count : 1}, ...] DB.from("items").groupAndCount("first_name___name").all() //=> SELECT first_name AS name, count(*) AS count FROM items GROUP BY first_name //=> [{name : 'a', count:1}, ...]Arguments
columns to croup and count on.
patio.Dataset
a cloned dataset with the GROUP clause and count added.
function (columns){ columns = argsToArray(arguments); var group = this.group.apply(this, columns.map(function (c) { return this._unaliasedIdentifier(c); }, this)); return group.select.apply(group, columns.concat([this._static.COUNT_OF_ALL_AS_COUNT])); }
function (){ return this.group.apply(this, arguments); }
Returns a cloned dataset with a greater than expression added to the WHERE clause.
ExampleDB.from("test").gt({x : 1}); //=> SELECT * FROM test WHERE (x > 1) DB.from("test").gt({x : 1, y : 10}); //=> SELECT * FROM test WHERE ((x > 1) AND (y > 10))Arguments
object used to create the greater than expression.
patio.Dataset
a cloned dataset with the greater than expression added to the WHERE clause.
function (obj){ return this.filter(this.__createBoolExpression("gt", obj)); }
Returns a cloned dataset with a greater than or equal to expression added to the WHERE clause.
ExampleDB.from("test").gte({x : 1}); //=> SELECT * FROM test WHERE (x >= 1) DB.from("test").gte({x : 1, y : 10}); //=> SELECT * FROM test WHERE ((x >= 1) AND (y >= 10))Arguments
Object
: object used to create the greater than or equal to expression.
patio.Dataset
a cloned dataset with the greater than or equal to expression added to the WHERE clause.
function (arr){ arr = this.__arrayToConditionSpecifier(argsToArray(arguments), "gte"); return this.filter(this.__createBoolExpression("gte", arr)); }
Returns a copy of the dataset with the HAVING conditions changed. See patio.Dataset#filter for argument types.
ExampleDB.from("items").group("sum").having({sum : 10}).sql; //=> SELECT * FROM items GROUP BY sum HAVING (sum = 10)Returns
patio.Dataset
a cloned dataset with HAVING clause changed or added.
function (){ var cond = argsToArray(arguments).map(function (s) { return isString(s) && s !== '' ? this.stringToIdentifier(s) : s; }, this); return this._filter.apply(this, ["having"].concat(cond)); }
Inserts multiple records into the associated table. This method can be used to efficiently insert a large number of records into a table in a single query if the database supports it. Inserts are automatically wrapped in a transaction.
This method is called with a columns array and an array of value arrays:
// INSERT INTO table (x, y) VALUES (1, 2) // INSERT INTO table (x, y) VALUES (3, 4) DB.from("table").import(["x", "y"], [[1, 2], [3, 4]]).
This method also accepts a dataset instead of an array of value arrays:
// INSERT INTO table (x, y) SELECT a, b FROM table2 DB.from("table").import(["x", "y"], DB.from("table2").select("a", "b"));
The method also accepts a commitEvery option that specifies the number of records to insert per transaction. This is useful especially when inserting a large number of records, e.g.:
// this will commit every 50 records DB.from("table").import(["x", "y"], [[1, 2], [3, 4], ...], {commitEvery : 50});Arguments
The columns to insert values for. This array will be used as the base for each values item in the values array.
Array of arrays of values to insert into the columns.
options
the callback to invoke when the action is done.
Number
: the number of records to insert per transaction.
comb.Promise
a promise that is resolved once all records have been inserted.
function (columns,values,opts,cb){ if (isFunction(opts)) { cb = opts; opts = null; } opts = opts || {}; var ret, self = this; if (isInstanceOf(values, Dataset)) { ret = this.db.transaction(function () { return self.insert(columns, values); }); } else { if (!values.length) { ret = new Promise().callback(); } else if (!columns.length) { throw new QueryError("Invalid columns in import"); } var sliceSize = opts.commitEvery || opts.slice, result = []; if (sliceSize) { ret = asyncArray(partition(values, sliceSize)).forEach(function (entries, offset) { offset = (offset * sliceSize); return self.db.transaction(opts, function () { return when(self.multiInsertSql(columns, entries).map(function (st, index) { return self.executeDui(st).chain(function (res) { result[offset + index] = res; }); })); }); }, 1); } else { var statements = this.multiInsertSql(columns, values); ret = this.db.transaction(function () { return when(statements.map(function (st, index) { return self.executeDui(st).chain(function (res) { result[index] = res; }); })); }); } } return ret.chain(function () { return flatten(result); }).classic(cb).promise(); }
function (v){ var i = this.__identifierInputMethod; v = v.toString(this); return !isUndefinedOrNull(i) ? isFunction(v[i]) ? v[i]() : isFunction(comb[i]) ? comb[i](v) : v : v; }
Inserts values into the associated table. The returned value is generally the value of the primary key for the inserted row, but that is adapter dependent.
Example// INSERT INTO items DEFAULT VALUES DB.from("items").insert() // INSERT INTO items DEFAULT VALUES DB.from("items").insert({}); // INSERT INTO items VALUES (1, 2, 3) DB.from("items").insert([1,2,3]); // INSERT INTO items (a, b) VALUES (1, 2) DB.from("items").insert(["a", "b"], [1,2]); // INSERT INTO items (a, b) VALUES (1, 2) DB.from("items").insert({a : 1, b : 2}); // INSERT INTO items SELECT * FROM old_items DB.from("items").insert(DB.from("old_items")); // INSERT INTO items (a, b) SELECT * FROM old_items DB.from("items").insert(["a", "b"], DB.from("old_items"));Arguments
patio.Dataset|patio.sql.LiteralString|Array|Object|patio.sql.BooleanExpression|...
: values to insert into the database. The INSERT statement generated depends on the type.
Function
: the callback to invoke when the action is done.
comb.Promise
a promise that is typically resolved with the ID of the inserted row.
function (){ var args = argsToArray(arguments); var cb = isFunction(args[args.length - 1]) ? args.pop() : null; return this.executeInsert(this.insertSql.apply(this, args)).classic(cb); }
Inserts multiple values. If a block is given it is invoked for each item in the given array before inserting it. See patio.Dataset#multiInsert as a possible faster version that inserts multiple records in one SQL statement.
Params see @link patio.Dataset#insert
ExampleDB.from("table").insertMultiple([{x : 1}, {x : 2}]); //=> INSERT INTO table (x) VALUES (1) //=> INSERT INTO table (x) VALUES (2) DB.from("table").insertMultiple([{x : 1}, {x : 2}], function(row){ row.y = row.x * 2; }); //=> INSERT INTO table (x, y) VALUES (1, 2) //=> INSERT INTO table (x, y) VALUES (2, 4)Arguments
See patio.Dataset#insert for possible values.
a function to be called before each item is inserted.
a function to be called when the aciton is complete
comb.PromiseList
a promiseList that should be resolved with the id of each item inserted in the order that was in the array.
function (array,block,cb){ var promises, ret; if (block) { ret = when(array.map(function (i) { return this.insert(block(i)); }, this)); } else { ret = when(array.map(function (i) { return this.insert(i); }, this)); } return ret.classic(cb).promise(); }
Returns an INSERT SQL query string. See patio.dataset._Actions#insert
ExampleDB.from("items").insertSql({a : 1}); //=> INSERT INTO items (a) VALUES (1) var ds = DB.from("test"); //default values ds.insertSql(); //=> INSERT INTO test DEFAULT VALUES //with hash ds.insertSql({name:'wxyz', price:342}); //=> INSERT INTO test (name, price) VALUES ('wxyz', 342) ds.insertSql({}); //=> INSERT INTO test DEFAULT VALUES //object that has a values property ds.insertSql({values:{a:1}}); //=> INSERT INTO test (a) VALUES (1) //arbitrary value ds.insertSql(123); //=> INSERT INTO test VALUES (123) //with dataset ds.insertSql(DB.from("something").filter({x:2})); //=> INSERT INTO test SELECT * FROM something WHERE (x = 2) //with array ds.insertSql('a', 2, 6.5); //=> INSERT INTO test VALUES ('a', 2, 6.5)Arguments
values to insert into the database. The INSERT statement generated depends on the type.
String
a INSERT SQL query string
patio.QueryError
if there are Different number of values and columns given to insertSql or if an invalid BooleanExpresion is given.
function (values){ values = argsToArray(arguments); var opts = this.__opts; if (opts.sql) { return this._staticSql(opts.sql); } this.__checkModificationAllowed(); var columns = []; switch (values.length) { case 0 : //we have no values return this.insertSql({}); case 1 : var vals = values[0], v; if (isInstanceOf(vals, Dataset, LiteralString) || isArray(vals)) { values = vals; } else if (vals.hasOwnProperty("values") && isObject((v = vals.values))) { return this.insertSql(v); } else if (isHash(vals)) { vals = merge({}, opts.defaults || {}, vals); vals = merge({}, vals, opts.overrides || {}); values = []; for (var i in vals) { columns.push(i); values.push(vals[i]); } } else if (isInstanceOf(vals, BooleanExpression)) { var op = vals.op; values = []; if (!isUndefinedOrNull(this._static.TWO_ARITY_OPERATORS[op])) { var args = vals.args; columns.push(args[0]); values.push(args[1]); } else { throw new QueryError("Invalid Expression op: " + op); } } break; case 2 : var v0 = values[0], v1 = values[1]; if (isArray(v0) && isArray(v1) || isInstanceOf(v1, Dataset, LiteralString)) { columns = v0, values = v1; if (isArray(values) && columns.length !== values.length) { throw new QueryError("Different number of values and columns given to insertSql"); } } break; } columns = columns.map(function (k) { return isString(k) ? new Identifier(k) : k; }, this); return this.mergeOptions({columns: columns, values: values})._insertSql(); }
Adds an INTERSECT clause using a second dataset object. An INTERSECT compound dataset returns all rows in both the current dataset and the given dataset.
ExampleDB.from("items").intersect(DB.from("other_items")).sql; //=> SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS t1 DB.from("items").intersect(DB.from("other_items"), {all : true, fromSelf : false}).sql; //=> SELECT * FROM items INTERSECT ALL SELECT * FROM other_items DB.from("items").intersect(DB.from("other_items"), {alias : "i"}).sql; //=> SELECT * FROM (SELECT * FROM items INTERSECT SELECT * FROM other_items) AS iArguments
the dataset to intersect
options
String|patio.sql.Identifier
: Use the given value as the patio.Dataset#fromSelf alias
Boolean
: Set to true to use INTERSECT ALL instead of INTERSECT, so duplicate rows can occur
Boolean
: Set to false to not wrap the returned dataset in a patio.Dataset#fromSelf.
patio.Dataset
a cloned dataset with the INTERSECT clause.
patio.QueryError
if the operation is not supported.
function (dataset,opts){ opts = isUndefined(opts) ? {} : opts; if (!isHash(opts)) { opts = {all: opts}; } if (!this.supportsIntersectExcept) { throw new QueryError("INTERSECT not supported"); } else if (opts.all && !this.supportsIntersectExceptAll) { throw new QueryError("INTERSECT ALL not supported"); } return this.compoundClone("intersect", dataset, opts); }
Returns a promise that is resolved with the interval between minimum and maximum values for the given column.
Example// SELECT (max(id) - min(id)) FROM table LIMIT 1 DB.from("table").interval("id").chain(function(interval){ //(e.g) interval === 6 });Arguments
to find the interval of.
a function to be called when the aciton is complete
comb.Promise
a promise that will be resolved with the interval between the min and max values of the column.
function (column,cb){ return this.__aggregateDataset().get(sql.max(column).minus(sql.min(column)), cb); }
Inverts the current filter.
ExampleDB.from("items").filter({category : 'software'}).invert() //=> SELECT * FROM items WHERE (category != 'software')Example
DB.from("items").filter({category : 'software', id : 3}).invert() //=> SELECT * FROM items WHERE ((category != 'software') OR (id != 3))Returns
patio.Dataset
a cloned dataset with the filter inverted.
function (){ var having = this.__opts.having, where = this.__opts.where; if (!(having || where)) { throw new QueryError("No current filter"); } var o = {}; if (having) { o.having = BooleanExpression.invert(having); } if (where) { o.where = BooleanExpression.invert(where); } return this.mergeOptions(o); }
Returnes a cloned dataset with the IS expression added to the WHERE clause.
ExampleDB.from("test").is({boolFlag : null}); => SELECT * FROM test WHERE (boolFlag IS NULL); DB.from("test").is({boolFlag : false, otherFlag : true, name : null}); => SELECT * FROM test WHERE ((boolFlag IS FALSE) AND (otherFlag IS TRUE) AND (name IS NULL));Arguments
object used to create the IS expression for.
patio.Dataset
a cloned dataset with the IS expression added to the WHERE clause.
function (obj){ return this.filter(this.__createBoolExpression("is", obj)); }
Returns a promise that is resolved with true if no records exist in the dataset, false otherwise.
Example// SELECT 1 FROM table LIMIT 1 DB.from("table").isEmpty().chain(function(isEmpty){ // isEmpty === false });Arguments
a function to callback when action is done
comb.Promise
a promise that is resolved with a boolean indicating if the table is empty.
function (cb){ return this.get(1).chain(function (res) { return isUndefinedOrNull(res) || res.length === 0; }.bind(this)).classic(cb); }
Returnes a cloned dataset with the IS FALSE boolean expression added to the WHERE clause.
ExampleDB.from("test").isFalse("boolFlag"); => SELECT * FROM test WHERE (boolFlag IS FALSE); DB.from("test").isFalse("boolFlag", "otherFlag"); => SELECT * FROM test WHERE (boolFlag IS FALSE AND otherFlag IS FALSE);Arguments
variable number of arguments to create an IS FALSE expression for.
patio.Dataset
a cloned dataset with the IS FALSE expression added to the WHERE clause.
function (arr){ arr = this.__arrayToConditionSpecifier(argsToArray(arguments), false); return this.filter(this.__createBoolExpression("is", arr)); }
Returnes a cloned dataset with the IS NOT expression added to the WHERE clause.
ExampleDB.from("test").isNot({boolFlag : null}); => SELECT * FROM test WHERE (boolFlag IS NOT NULL); DB.from("test").isNot({boolFlag : false, otherFlag : true, name : null}); => SELECT * FROM test WHERE ((boolFlag IS NOT FALSE) AND (otherFlag IS NOT TRUE) AND (name IS NOT NULL));Arguments
object used to create the IS NOT expression for.
patio.Dataset
a cloned dataset with the IS NOT expression added to the WHERE clause.
function (obj){ return this.filter(this.__createBoolExpression("isNot", obj)); }
Returnes a cloned dataset with the IS NOT FALSE boolean expression added to the WHERE clause.
ExampleDB.from("test").isNotFalse("boolFlag"); => SELECT * FROM test WHERE (boolFlag IS NOT FALSE); DB.from("test").isNotFalse("boolFlag", "otherFlag"); => SELECT * FROM test WHERE (boolFlag IS NOT FALSE AND otherFlag IS NOT FALSE);Arguments
variable number of arguments to create an IS NOT FALSE expression for.
patio.Dataset
a cloned dataset with the IS NOT FALSE expression added to the WHERE clause.
function (arr){ arr = this.__arrayToConditionSpecifier(argsToArray(arguments), false); return this.filter(this.__createBoolExpression("isNot", arr)); }
Returnes a cloned dataset with the IS NOT NULL boolean expression added to the WHERE clause.
ExampleDB.from("test").isNotNull("boolFlag"); => SELECT * FROM test WHERE (boolFlag IS NOT NULL); DB.from("test").isNotNull("boolFlag", "otherFlag"); => SELECT * FROM test WHERE (boolFlag IS NOT NULL AND otherFlag IS NOT NULL);Arguments
variable number of arguments to create an IS NOT NULL expression for.
patio.Dataset
a cloned dataset with the IS NOT NULL expression added to the WHERE clause.
function (arr){ arr = this.__arrayToConditionSpecifier(argsToArray(arguments), null); return this.filter(this.__createBoolExpression("isNot", arr)); }
Returnes a cloned dataset with the IS NOT TRUE boolean expression added to the WHERE clause.
ExampleDB.from("test").isNotTrue("boolFlag"); => SELECT * FROM test WHERE (boolFlag IS NOT TRUE); DB.from("test").isNotTrue("boolFlag", "otherFlag"); => SELECT * FROM test WHERE (boolFlag IS NOT TRUE AND otherFlag IS NOT TRUE);Arguments
variable number of arguments to create an IS NOT TRUE expression for.
patio.Dataset
a cloned dataset with the IS NOT TRUE expression added to the WHERE clause.
function (arr){ arr = this.__arrayToConditionSpecifier(argsToArray(arguments), true); return this.filter(this.__createBoolExpression("isNot", arr)); }
Returnes a cloned dataset with the IS NULL boolean expression added to the WHERE clause.
ExampleDB.from("test").isNull("boolFlag"); => SELECT * FROM test WHERE (boolFlag IS NULL); DB.from("test").isNull("boolFlag", "otherFlag"); => SELECT * FROM test WHERE (boolFlag IS NULL AND otherFlag IS NULL);Arguments
variable number of arguments to create an IS NULL expression for.
patio.Dataset
a cloned dataset with the IS NULL expression added to the WHERE clause.
function (arr){ arr = this.__arrayToConditionSpecifier(argsToArray(arguments), null); return this.filter(this.__createBoolExpression("is", arr)); }
Returnes a cloned dataset with the IS TRUE boolean expression added to the WHERE clause.
ExampleDB.from("test").isTrue("boolFlag"); => SELECT * FROM test WHERE (boolFlag IS TRUE); DB.from("test").isTrue("boolFlag", "otherFlag"); => SELECT * FROM test WHERE (boolFlag IS TRUE AND otherFlag IS TRUE);Arguments
variable number of arguments to create an IS TRUE expression for.
patio.Dataset
a cloned dataset with the IS TRUE expression added to the WHERE clause.
function (arr){ arr = this.__arrayToConditionSpecifier(argsToArray(arguments), true); return this.filter(this.__createBoolExpression("is", arr)); }
Returns a cloned dataset with an inner join applied.
Sourcefunction (){ return this.innerJoin.apply(this, arguments); }
function (jc){ var table = jc.table, tableAlias = jc.tableAlias; if (table === tableAlias) { tableAlias = null; } var tref = this.__tableRef(table); return string.format(" %s %s", this._joinTypeSql(jc.joinType), tableAlias ? this.__asSql(tref, tableAlias) : tref); }
function (jc){ return string.format("%s ON %s", this.joinClauseSql(jc), this.literal(this._filterExpr(jc.on))); }
Returns a joined dataset. Uses the following arguments:
ExampleDB.from("items").joinTable("leftOuter", "categories", [["categoryId", "id"],["categoryId", [1, 2, 3]]]).sql; //=>'SELECT * FROM `items` LEFT OUTER JOIN `categories` ON ( (`categories`.`categoryId` = `items`.`id`) AND (`categories`.`categoryId` IN (1,2, 3)) ) DB.from("items").leftOuter("categories", [["categoryId", "id"],["categoryId", [1, 2, 3]]]).sql; //=>'SELECT * FROM `items` LEFT OUTER JOIN `categories` ON ( (`categories`.`categoryId` = `items`.`id`) AND (`categories`.`categoryId` IN (1,2, 3)) ) DB.from("items").leftOuterJoin("categories", {categoryId:"id"}).sql //=> SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id") DB.from("items").rightOuterJoin("categories", {categoryId:"id"}).sql //=> SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id") DB.from("items").fullOuterJoin("categories", {categoryId:"id"}).sql //=> SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id") DB.from("items").innerJoin("categories", {categoryId:"id"}).sql //=> SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."categoryId" = "items"."id") DB.from("items").leftJoin("categories", {categoryId:"id"}).sql //=> SELECT * FROM "items" LEFT JOIN "categories" ON ("categories"."categoryId" = "items"."id") DB.from("items").rightJoin("categories", {categoryId:"id"}).sql //=> SELECT * FROM "items" RIGHT JOIN "categories" ON ("categories"."categoryId" = "items"."id") DB.from("items").fullJoin("categories", {categoryId:"id"}).sql //=> SELECT * FROM "items" FULL JOIN "categories" ON ("categories"."categoryId" = "items"."id") DB.from("items").naturalJoin("categories").sql //=> SELECT * FROM "items" NATURAL JOIN "categories" DB.from("items").naturalLeftJoin("categories").sql //=> SELECT * FROM "items" NATURAL LEFT JOIN "categories" DB.from("items").naturalRightJoin("categories").sql //=> SELECT * FROM "items" NATURAL RIGHT JOIN "categories" DB.from("items").naturalFullJoin("categories").sql //=> SELECT * FROM "items" NATURAL FULL JOIN "categories"' DB.from("items").crossJoin("categories").sql //=> SELECT * FROM "items" CROSS JOIN "categories"Arguments
the type of join to do.
depends on the type.
an object of options.
cb - The cb argument should only be given if a JOIN with an ON clause is used, in which case it is called with
undefined
] String|patio.sql.Identifier
: the name of the table's alias when joining, necessary for joining to the same table more than once. No alias is used by default.
undefined
] String|patio.sql.Identifier
: The name to use for qualifying implicit conditions. By default, the last joined or primary table is used.
patio.Dataset
a cloned dataset joined using the arguments.
function (type,table,expr,options,cb){ var args = argsToArray(arguments); if (isFunction(args[args.length - 1])) { cb = args[args.length - 1]; args.pop(); } else { cb = null; } type = args.shift(), table = args.shift(), expr = args.shift(), options = args.shift(); expr = isUndefined(expr) ? null : expr, options = isUndefined(options) ? {} : options; var h; var usingJoin = isArray(expr) && expr.length && expr.every(function (x) { return isString(x) || isInstanceOf(x, Identifier); }); if (usingJoin && !this.supportsJoinUsing) { h = {}; expr.forEach(function (s) { h[s] = s; }); return this.joinTable(type, table, h, options); } var tableAlias, lastAlias; if (isHash(options)) { tableAlias = options.tableAlias; lastAlias = options.implicitQualifier; } else if (isString(options) || isInstanceOf(options, Identifier)) { tableAlias = options; lastAlias = null; } else { throw new QueryError("Invalid options format for joinTable %j4", [options]); } var tableAliasNum, tableName; if (isInstanceOf(table, Dataset)) { if (!tableAlias) { tableAliasNum = (this.__opts.numDatasetSources || 0) + 1; tableAlias = this._datasetAlias(tableAliasNum); } tableName = tableAlias; } else { if (!isUndefined(table.tableName)) { table = table.tableName; } if (isArray(table)) { table = table.map(this.stringToIdentifier, this); } else { table = isString(table) ? this.stringToIdentifier(table) : table; var parts = this._splitAlias(table), implicitTableAlias = parts[1]; table = parts[0]; tableAlias = tableAlias || implicitTableAlias; tableName = tableAlias || table; } } var join; if (!expr && !cb) { join = new JoinClause(type, table, tableAlias); } else if (usingJoin) { if (cb) { throw new QueryError("cant use a cb if an array is given"); } join = new JoinUsingClause(expr, type, table, tableAlias); } else { lastAlias = lastAlias || this.__opts["lastJoinedTable"] || this.firstSourceAlias; if (Expression.isConditionSpecifier(expr)) { var newExpr = []; for (var i in expr) { var val = expr[i]; if (isArray(val) && val.length === 2) { i = val[0], val = val[1]; } var k = this.qualifiedColumnName(i, tableName), v; if (isInstanceOf(val, Identifier)) { v = val.qualify(lastAlias); } else { v = val; } newExpr.push([k, v]); } expr = newExpr; } if (isFunction(cb)) { var expr2 = cb.apply(sql, [tableName, lastAlias, this.__opts.join || []]); expr = expr ? new BooleanExpression("AND", expr, expr2) : expr2; } join = new JoinOnClause(expr, type, table, tableAlias); } var opts = {join: (this.__opts.join || []).concat([join]), lastJoinedTable: tableName}; if (tableAliasNum) { opts.numDatasetSources = tableAliasNum; } return this.mergeOptions(opts); }
function (jc){ return string.format("%s USING (%s)", this.joinClauseSql(jc), this.__columnList(jc.using)); }
Reverses the order and then runs first. Note that this will not necessarily give you the last record in the dataset, unless you have an unambiguous order.
Example// SELECT * FROM table ORDER BY id DESC LIMIT 1 DB.from("table").order("id").last().chain(function(lastItem){ //...(e.g lastItem === {id : 10}) }); // SELECT * FROM table ORDER BY id ASC LIMIT 2 DB.from("table").order(sql.id.desc()).last(2).chain(function(lastItems){ //...(e.g lastItems === [{id : 1}, {id : 2}); });Arguments
See patio.Dataset#first for argument types.
comb.Promise
a promise that will be resolved with a single object or array depending on the arguments provided.
patio.error.QueryError
If there is not currently an order for this dataset.
function (args){ if (!this.__opts.order) { throw new QueryError("No order specified"); } var ds = this.reverse(); return ds.first.apply(ds, arguments); }
function (){ return this.grep.apply(this, arguments); }
If given an integer, the dataset will contain only the first l results. If a second argument is given, it is used as an offset. To use an offset without a limit, pass null as the first argument.
ExampleDB.from("items").limit(10) //=> SELECT * FROM items LIMIT 10 DB.from("items").limit(10, 20) //=> SELECT * FROM items LIMIT 10 OFFSET 20 DB.from("items").limit([3, 7]).sql //=> SELECT * FROM items LIMIT 5 OFFSET 3'); DB.from("items").limit(null, 20) //=> SELECT * FROM items OFFSET 20 DB.from("items").limit('6', sql['a() - 1']).sql => 'SELECT * FROM items LIMIT 6 OFFSET a() - 1');Arguments
the limit to apply
the offset to apply
patio.Dataset
a cloned dataset witht the LIMIT and OFFSET applied.
function (limit,offset){ if (this.__opts.sql) { return this.fromSelf().limit(limit, offset); } if (isArray(limit) && limit.length === 2) { offset = limit[0]; limit = limit[1] - limit[0] + 1; } if (isString(limit) || isInstanceOf(limit, LiteralString)) { limit = parseInt("" + limit, 10); } if (isNumber(limit) && limit < 1) { throw new QueryError("Limit must be >= 1"); } var opts = {limit: limit}; if (offset) { if (isString(offset) || isInstanceOf(offset, LiteralString)) { offset = parseInt("" + offset, 10); isNaN(offset) && (offset = 0); } if (isNumber(offset) && offset < 0) { throw new QueryError("Offset must be >= 0"); } opts.offset = offset; } return this.mergeOptions(opts); }
Returns a literal representation of a value to be used as part of an SQL expression.
ExampleDB.from("items").literal("abc'def\\") //=> "'abc''def\\\\'" DB.from("items").literal("items__id") //=> "items.id" DB.from("items").literal([1, 2, 3]) //=> "(1, 2, 3)" DB.from("items").literal(DB.from("items")) //=> "(SELECT * FROM items)" DB.from("items").literal(sql.x.plus(1).gt("y")); //=> "((x + 1) > y)"Arguments
the value to convert the the SQL literal representation
String
a literal representation of the value.
patio.QueryError
If an unsupported object is given.
function (v){ if (isInstanceOf(v, Json, JsonArray)) { return this._literalJson(v); } else if (isInstanceOf(v, LiteralString)) { return "" + v; } else if (isString(v)) { return this._literalString(v); } else if (isNumber(v)) { return this._literalNumber(v); } else if (isInstanceOf(v, Expression)) { return this._literalExpression(v); } else if (isInstanceOf(v, Dataset)) { return this._literalDataset(v); } else if (isArray(v)) { return this._literalArray(v); } else if (isInstanceOf(v, sql.Year)) { return this._literalYear(v); } else if (isInstanceOf(v, sql.TimeStamp, sql.DateTime)) { return this._literalTimestamp(v); } else if (isDate(v)) { return this._literalDate(v); } else if (isInstanceOf(v, sql.Time)) { return this._literalTime(v); } else if (Buffer.isBuffer(v)) { return this._literalBuffer(v); } else if (isNull(v)) { return this._literalNull(); } else if (isBoolean(v)) { return this._literalBoolean(v); } else if (isHash(v)) { return this._literalObject(v); } else { return this._literalOther(v); } }
Returns a cloned dataset with the given lock style. If style is a string, it will be used directly.Currently "update" is respected by most databases, and "share" is supported by some.
ExampleDB.from("items").lockStyle('FOR SHARE') # SELECT * FROM items FOR SHAREArguments
the lock style to use.
patio.Dataset
a cloned datase with the given lock style.
function (style){ return this.mergeOptions({lock: style}); }
Returns a cloned dataset with a less than expression added to the WHERE clause.
ExampleDB.from("test").lt({x : 1}); //=> SELECT * FROM test WHERE (x < 1) DB.from("test").lt({x : 1, y : 10}); //=> SELECT * FROM test WHERE ((x < 1) AND (y < 10))Arguments
object used to create the less than expression.
patio.Dataset
a cloned dataset with the less than expression added to the WHERE clause.
function (obj){ return this.filter(this.__createBoolExpression("lt", obj)); }
Returns a cloned dataset with a less than or equal to expression added to the WHERE clause.
ExampleDB.from("test").gte({x : 1}); //=> SELECT * FROM test WHERE (x <= 1) DB.from("test").gte({x : 1, y : 10}); //=> SELECT * FROM test WHERE ((x <= 1) AND (y <= 10))Arguments
object used to create the less than or equal to expression.
patio.Dataset
a cloned dataset with the less than or equal to expression added to the WHERE clause.
function (obj){ var arr = this.__arrayToConditionSpecifier(argsToArray(arguments), "lte"); return this.filter(this.__createBoolExpression("lte", obj)); }
Maps column values for each record in the dataset (if a column name is given).
Example// SELECT * FROM table DB.from("table").map("id").chain(function(ids){ // e.g. ids === [1, 2, 3, ...] }); // SELECT * FROM table DB.from("table").map(function(r){ return r.id * 2; }).chain(function(ids){ // e.g. ids === [2, 4, 6, ...] });Arguments
if a string is provided then then it is assumed to be the name of a column in that table and the value of the column for each row will be returned. If column is a function then the return value of the function will be used.
a function to be called when the aciton is complete
comb.Promise
a promise resolved with the array of mapped values.
function (column,cb){ var ret = this.forEach(); column && (ret = ret[isFunction(column) ? "map" : "pluck"](column)); return ret.classic(cb).promise(); }
Returns a promise resolved with the maximum value for the given column.
Example// SELECT max(id) FROM table LIMIT 1 DB.from("table").max("id").chain(function(max){ // e.g. max === 10. });Arguments
the column to find the maximum value for.
callback to invoke when action is done
*
the maximum value for the column.
function (column,cb){ return this.__aggregateDataset().get(sql.max(this.stringToIdentifier(column)), cb); }
Returns a new clone of the dataset with with the given options merged into the current datasets options. If the options changed include options in patio.dataset.Query#COLUMN_CHANGE_OPTS, the cached columns are deleted. This method should generally not be called directly by user code.
Argumentsoptions to merge into the curred datasets options and applied to the returned dataset.
[patio.Dataset] a cloned dataset with the merged options
function (opts){ opts = isUndefined(opts) ? {} : opts; var ds = new this._static(this.db, {}); ds.rowCb = this.rowCb; this._static.FEATURES.forEach(function (f) { ds[f] = this[f]; }, this); var dsOpts = ds.__opts = merge({}, this.__opts, opts); ds.identifierInputMethod = this.identifierInputMethod; ds.identifierOutputMethod = this.identifierOutputMethod; var columnChangeOpts = this._static.COLUMN_CHANGE_OPTS; if (Object.keys(opts).some(function (o) { return columnChangeOpts.indexOf(o) !== -1; })) { dsOpts.columns = null; } return ds; }
Returns a promise resolved with the minimum value for the given column.
Example// SELECT min(id) FROM table LIMIT 1 DB.from("table").min("id").chain(function(min){ // e.g. max === 0. });Arguments
the column to find the minimum value for.
callback to invoke when action is done
*
the minimum value for the column.
function (column,cb){ return this.__aggregateDataset().get(sql.min(this.stringToIdentifier(column)), cb); }
This is the recommended function to do the insert of multiple items into the database. This acts as a proxy to the patio.Dataset#import method so one can use an array of hashes rather than an array of columns and an array of values. See patio.Dataset#import for more information regarding the method of inserting.
NOTE:All hashes should have the same keys other wise some values could be missed
Example// INSERT INTO table (x) VALUES (1) // INSERT INTO table (x) VALUES (2) DB.from("table").multiInsert([{x : 1}, {x : 2}]).chain(function(){ //...do something }) //commit every 50 inserts DB.from("table").multiInsert([{x : 1}, {x : 2},....], {commitEvery : 50}).chain(function(){ //...do something });Arguments
an array of objects to insert into the database. The keys of the first item in the array will be used to look up columns in all subsequent objects. If the array is empty then the promise is resolved immediatly.
See patio.Dataset#import.
the callback to invoke when the action is done.
comb.Promise
See patio.Dataset#import for return functionality.
function (hashes,opts,cb){ if (isFunction(opts)) { cb = opts; opts = null; } opts = opts || {}; hashes = hashes || []; var ret = new Promise(); if (!hashes.length) { ret.callback(); } else { var columns = Object.keys(hashes[0]); ret = this["import"](columns, hashes.map(function (h) { return columns.map(function (c) { return h[c]; }); }), opts, cb); } return ret.classic(cb).promise(); }
Returns an array of insert statements for inserting multiple records. This method is used by patio.dataset._Actions#multiInsert to format insert statements. This method is not typically used directly.
Note:This method should be overridden by descendants if there is support for inserting multiple records in a single SQL statement.
ArgumentsThe columns to insert values for. This array will be used as the base for each values item in the values array.
Array of arrays of values to insert into the columns.
String[]
array of insert statements.
function (columns,values){ return values.map(function (r) { return this.insertSql(columns, r); }, this); }
Returns the a cloned dataset with out the patio.Dataset#rowCb
Examplevar ds = DB.from("test"); ds.rowCb = function(r){ r.a = r.a * 2; } ds.all().chain(function(ret){ //ret === [{a : 4}, {a : 6}] }); ds.naked().all().chain(function(ret){ //ret === [{a : 2}, {a : 3}]; });Returns
patio.Dataset
a cloned dataset with out the patio.Dataset#rowCb
function (){ var ds = this.mergeOptions({}); ds.rowCb = null; return ds; }
function (constant){ return string.format("NOT %s", this.booleanConstantSql(constant)); }
Returns a cloned dataset with a not equal expression added to the WHERE clause.
ExampleDB.from("test").neq({x : 1}); //=> SELECT * FROM test WHERE (x != 1) DB.from("test").neq({x : 1, y : 10}); //=> SELECT * FROM test WHERE ((x != 1) AND (y != 10))Arguments
object used to create the not equal expression
patio.Dataset
a cloned dataset with the not equal expression added to the WHERE clause.
function (obj){ return this.filter(this.__createBoolExpression("neq", obj)); }
Returns a cloned dataset with a not between clause added to the where clause.
Exampleds.notBetween({x:[1, 2]}).sql; //=> SELECT * FROM test WHERE ((x < 1) OR (x > 2)) ds.find({x:{notBetween:[1, 2]}}).sql; //=> SELECT * FROM test WHERE ((x < 1) OR (x > 2))Arguments
object where the key is the column and the value is an array where the first element is the item to be less than and the second item is greater than.
patio.Dataset
a cloned dataset with a not between clause added to the where clause.
function (obj){ return this.filter(this.__createBetweenExpression(obj, true)); }
Same as patio.Dataset#singleRecord but accepts arguments to filter the dataset. See patio.Dataset#filter for argument types.
NOTE If the last argument is a function that accepts arguments it is not assumed to be a filter function but instead a callback.
Returnscomb.Promise
a promise resolved with a single row from the database that matched the filter.
function (){ var args = comb(arguments).toArray(), cb; var last = args[args.length - 1]; if (isFunction(last) && last.length > 0) { cb = args.pop(); } var ret = this; if (args.length) { ret = ret.filter.apply(ret, args); } return ret.singleRecord(cb); }
Adds an alternate filter to an existing WHERE/HAVING using OR.
For parameter types see patio.Dataset#filter.
ExampleDB.from("items").filter("a").or("b") //=> SELECT * FROM items WHERE a OR bReturns
patio.Dataset
a cloned dataset with the condition added to the WHERE/HAVING clause added.
patio.QueryError
If no WHERE?HAVING clause exists.
function (){ var tOpts = this.__opts; var clause = (tOpts.having ? "having" : "where"), clauseObj = tOpts[clause]; if (clauseObj) { var args = argsToArray(arguments); args = args.length === 1 ? args[0] : args; var opts = {}; opts[clause] = new BooleanExpression("OR", clauseObj, this._filterExpr(args)); return this.mergeOptions(opts); } else { throw new QueryError("No existing filter found"); } }
Adds a group of ANDed conditions wrapped in parens to an existing where/having clause by an OR. If there isn't a where/having clause, a where clause is created with the ANDed conditions.
For parameter types see patio.Dataset#filter.
ExampleDB.from("items").filter({id, [1,2,3]}).orGroupedAnd([{price: {lt : 0}}, {price: {gt: 10}]).sql; //=> SELECT * FROM items WHERE ((id IN (1, 2, 3)) OR ((price > 0) AND (price < 10))) DB.from("items").orGroupedAnd([{price: {gt : 0}}, {price: {gt: 10}]).sql; //=> SELECT * FROM items WHERE ((price > 0) AND (price < 10))Returns
patio.Dataset
a cloned dataset with the condition 'and group' added to the WHERE/HAVING clause.
function (){ var tOpts = this.__opts, clause = (tOpts.having ? "having" : "where"), clauseObj = tOpts[clause]; if (clauseObj) { return this.or.apply(this, arguments); } else { var args = argsToArray(arguments); args = args.length === 1 ? args[0] : args; var opts = {}; opts[clause] = this._filterExpr(args, null, "AND"); return this.mergeOptions(opts); } }
Adds a group of ORed conditions wrapped in parens to an existing having/where clause with an OR. If there isn't already a clause, a where clause is created with the ORed group.
For parameter types see patio.Dataset#filter.
ExampleDB.from("items").filter({id, [1,2,3]}).andGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql; //=> SELECT * FROM items WHERE ((id IN (1, 2, 3)) AND ((price < 0) OR (price > 10))) DB.from("items").orGroupedOr([{price: {lt : 0}}, {price: {gt: 10}]).sql; //=> SELECT * FROM items WHERE ((price < 0) OR (price > 10))Arguments
patio.Dataset
a cloned dataset with the condition 'or group' added to the WHERE/HAVING clause.
function (filterExp){ return this._addGroupedCondition("OR", "OR", filterExp); }
Returns a copy of the dataset with the order changed. If the dataset has an existing order, it is ignored and overwritten with this order. If null is given the returned dataset has no order. This can accept multiple arguments of varying kinds, such as SQL functions. This also takes a function similar to patio.Dataset#filter
ExampleDB.from("items").order("name") //=> SELECT * FROM items ORDER BY name DB.from("items").order("a", "b") //=> SELECT * FROM items ORDER BY a, b DB.from("items").order(sql.literal('a + b')) //=> SELECT * FROM items ORDER BY a + b DB.from("items").order(sql.identifier("a").plus("b")) //=> SELECT * FROM items ORDER BY (a + b) DB.from("items").order(sql.identifier("name").desc()) //=> SELECT * FROM items ORDER BY name DESC DB.from("items").order(sql.identifier("name").asc({nulls : "last")) //=> SELECT * FROM items ORDER BY name ASC NULLS LAST DB.from("items").order(function(){ return this.sum("name").desc(); }); //=> SELECT * FROM items ORDER BY sum(name) DESC DB.from("items").order(null) //=>SELECT * FROM itemsArguments
variable number of arguments similar to patio.Dataset#filter
patio.Dataset
a cloned dataset with the order changed.
function (args){ args = argsToArray(arguments); var order = []; args = compact(args).length ? args : null; if (args) { args.forEach(function (a) { if (isString(a)) { order.push(this.stringToIdentifier(a)); } else if (isFunction(a)) { var res = a.apply(sql, [sql]); order = order.concat(isArray(res) ? res : [res]); } else { order.push(a); } }, this); } else { order = null; } return this.mergeOptions({order: order}); }
Alias of patio.Dataset#orderMore;
Sourcefunction (){ return this.orderMore.apply(this, arguments); }
function (){ return this.order.apply(this, arguments); }
Returns a copy of the dataset with the order columns added to the end of the existing order. For more detail
ExampleDB.from("items").order("a").order("b"); //=> SELECT * FROM items ORDER BY b DB.from("items").order("a").orderMore("b"); //=>SELECT * FROM items ORDER BY a, bSource
function (){ var args = argsToArray(arguments); if (this.__opts.order) { args = this.__opts.order.concat(args); } return this.order.apply(this, args); }
Returns a copy of the dataset with the order columns added to the beginning of the existing order. For more detail
ExampleDB.from("items").order("a").order("b"); //=> SELECT * FROM items ORDER BY b DB.from("items").order("a").orderPrepend("b"); //=>SELECT * FROM items ORDER BY b, aSource
function (){ var ds = this.order.apply(this, arguments); return this.__opts.order ? ds.orderMore.apply(ds, this.__opts.order) : ds; }
function (oe){ var s = string.format("%s %s", this.literal(oe.expression), oe.descending ? "DESC" : "ASC"); if (oe.nulls) { s = string.format("%s NULLS %s", s, oe.nulls === "first" ? "FIRST" : "LAST"); } return s; }
function (v){ (v === '' && (v = 'untitled')); var i = this.__identifierOutputMethod; return !isUndefinedOrNull(i) ? isFunction(v[i]) ? v[i]() : isFunction(comb[i]) ? comb[i](v) : v : v; }
function (pls){ var args = pls.args; var s; if (isHash(args)) { for (var i in args) { args[i] = this.literal(args[i]); } s = string.format(pls.str, args); } else { s = pls.str.replace(this._static.QUESTION_MARK, "%s"); args = toArray(args).map(this.literal, this); s = string.format(s, args); } if (pls.parens) { s = string.format("(%s)", s); } return s; }
This is run inside patio.Dataset#all, after all of the records have been loaded via patio.Dataset#forEach, but before any block passed to all is called. It is called with a single argument, an array of all returned records. Does nothing by default.
Argumentsfunction (allRecords){ return allRecords; }
Returns a qualified column name (including a table name) if the column name isn't already qualified.
Exampledataset.qualifiedColumnName("b1", "items"); //=> items.b1 dataset.qualifiedColumnName("ccc__b")); //=> 'ccc.b' dataset.qualifiedColumnName("ccc__b", "items")); //=> 'ccc.b'Arguments
the column to qualify. If the column is already qualified (e.g. ccc__b) then the table name (e.g. ccc) will override the provided table.
the name of the table to qualify the column to.
String
the qualified column name..
function (column,table){ if (isString(column)) { var parts = this._splitString(column); var columnTable = parts[0], alias = parts[2], schema, tableAlias; column = parts[1]; if (!columnTable) { if (isInstanceOf(table, Identifier)) { table = table.value; } if (isInstanceOf(table, AliasedExpression)) { tableAlias = table.alias; } else if (isInstanceOf(table, QualifiedIdentifier)) { tableAlias = table; } else { parts = this._splitString(table); schema = parts[0]; tableAlias = parts[2]; table = parts[1]; if (schema) { tableAlias = new Identifier(tableAlias) || new QualifiedIdentifier(schema, table); } } columnTable = tableAlias || table; } return new QualifiedIdentifier(columnTable, column); } else if (isInstanceOf(column, Identifier)) { return column.qualify(table); } else { return column; } }
function (qcr){ return [qcr.table, qcr.column].map(function (x) { var isLiteral = [QualifiedIdentifier, Identifier, String].some(function (c) { return x instanceof c; }), ret; if (isLiteral) { ret = this.literal(x); } else { ret = this.quoteIdentifier(x); } return ret; }, this).join('.'); }
Qualify to the given table, or patio.Dataset#firstSourceAlias if not table is given.
ExampleDB.from("items").filter({id : 1}).qualify(); //=> SELECT items.* FROM items WHERE (items.id = 1) DB.from("items").filter({id : 1}).qualify("i"); //=> SELECT i.* FROM items WHERE (i.id = 1)Arguments
{@link patio.Dataset#firstSourceAlias}
] : the table name to qualify to.
patio.Dataset
a cloned dataset qualified to the table or patio.Dataset#firstSourceAlias
function (table){ table = table || this.firstSourceAlias; return this.qualifyTo(table); }
Return a copy of the dataset with unqualified identifiers in the SELECT, WHERE, GROUP, HAVING, and ORDER clauses qualified by the given table. If no columns are currently selected, select all columns of the given table.
ExampleDB.from("items").filter({id : 1}).qualifyTo("i"); //=> SELECT i.* FROM items WHERE (i.id = 1)Arguments
the name to qualify identifier to.
patio.Dataset
a cloned dataset with unqualified identifiers qualified.
function (table){ var o = this.__opts; if (o.sql) { return this.mergeOptions(); } var h = {}; array.intersect(Object.keys(o), this._static.QUALIFY_KEYS).forEach(function (k) { h[k] = this._qualifiedExpression(o[k], table); }, this); if (!o.select || isEmpty(o.select)) { h.select = [new ColumnAll(table)]; } return this.mergeOptions(h); }
Qualify the dataset to its current first source(first from clause). This is useful if you have unqualified identifiers in the query that all refer to the first source, and you want to join to another table which has columns with the same name as columns in the current dataset. See patio.Dataset#qualifyTo
ExampleDB.from("items").filter({id : 1}).qualifyToFirstSource(); //=> SELECT items.* FROM items WHERE (items.id = 1)Returns
patio.Dataset
a cloned dataset that is qualified with the first source.
function (){ return this.qualifyTo(this.firstSourceAlias); }
function (name){ if (isInstanceOf(name, LiteralString)) { return name; } else { if (isInstanceOf(name, Identifier)) { name = name.value; } name = this.inputIdentifier(name); if (this.quoteIdentifiers) { name = this._quotedIdentifier(name); } } return name; }
function (table){ var parts = this.schemaAndTable(table); var schema = parts[0]; table = parts[1]; return string.format("%s%s", schema ? this.quoteIdentifier(schema) + "." : "", this.quoteIdentifier(table)); }
Returns a promise resolved with a range from the minimum and maximum values for the given column.
Example// SELECT max(id) AS v1, min(id) AS v2 FROM table LIMIT 1 DB.from("table").range("id").chain(function(min, max){ //e.g min === 1 AND max === 10 });Arguments
the column to find the min and max value for.
the callback to invoke when the action is done.
comb.Promise
a promise that is resolved with the min and max value, as the first and second args respectively.
function (column,cb){ var ret = new Promise(); this.__aggregateDataset() .select(sql.min(this.stringToIdentifier(column)).as("v1"), sql.max(this.stringToIdentifier(column)).as("v2")) .first() .chain(function (r) { ret.callback(r.v1, r.v2); }, ret.errback); return ret.classic(cb).promise(); }
Deletes the records in the dataset. The returned Promise should be resolved with the number of records deleted, but that is adapter dependent.
Example// DELETE * FROM table DB.from("table").remove().chain(function(numDeleted){ //numDeleted === 3 });Arguments
the callback to invoke when the action is done.
comb.Promise
a promise resolved with the number of records deleted, but that is adapter dependent.
function (cb){ return this.executeDui(this.deleteSql).classic(cb).promise(); }
Adds a RETURNING clause, which is not supported by all databases. If returning is used instead of returning the autogenerated primary key or update/delete returning the number of rows modified.
Exampleds.from("items").returning() //"RETURNING *" ds.from("items").returning(null) //"RETURNING NULL" ds.from("items").returning("id", "name") //"RETURNING id, name" ds.from("items").returning(["id", "name"]) //"RETURNING id, name"Arguments
columns to return. If values is an array then the array is assumed to contain the columns to return. Otherwise the arguments will be used.
patio.Dataset
a new dataset with the retuning option added.
function (values){ var args; if (Array.isArray(values)) { args = values; } else { args = argsToArray(arguments); } return this.mergeOptions({returning: args.map(function (v) { return isString(v) ? sql.stringToIdentifier(v) : v; })}); }
Returns a copy of the dataset with the order reversed. If no order is given, the existing order is inverted.
ExampleDB.from("items").reverse("id"); //=> SELECT * FROM items ORDER BY id DESC DB.from("items").order("id").reverse(); //=> SELECT * FROM items ORDER BY id DESC DB.from("items").order("id").reverse(sql.identifier("name").asc); //=> SELECT * FROM items ORDER BY name ASCArguments
variable number of columns add to order before reversing.
patio.Dataset
a cloned dataset with the order reversed.
function (args){ args = argsToArray(arguments); return this.order.apply(this, this._invertOrder(args.length ? args : this.__opts.order)); }
function (){ return this.reverse.apply(this, arguments); }
function (){ return this.insert.apply(this, arguments); }
function (){ return this.insertMultiple.apply(this, arguments); }
function (tableName){ var sch = this.db ? this.db.defaultSchema || null : null; if (isString(tableName)) { var parts = this._splitString(tableName); var s = parts[0], table = parts[1]; return [s || sch, table]; } else if (isInstanceOf(tableName, QualifiedIdentifier)) { return [tableName.table, tableName.column]; } else if (isInstanceOf(tableName, Identifier)) { return [null, tableName.value]; } else if (isInstanceOf(tableName, LiteralString)) { return [null, tableName]; } else { throw new QueryError("table should be a QualifiedIdentifier, Identifier, or String"); } }
Returns a copy of the dataset with the columns selected changed to the given columns. This also takes a function similar to patio.Dataset#filter
ExampleDB.from("items").select("a"); //=> SELECT a FROM items DB.from("items").select("a", "b"); //=> SELECT a, b FROM items DB.from("items").select("a", function(){ return this.sum("b") }).sql; //=> SELECT a, sum(b) FROM itemsArguments
variable number of colums to select
patio.Dataset
a cloned dataset with the columns selected changed.
function (args){ args = flatten(argsToArray(arguments)); var columns = []; args.forEach(function (c) { if (isFunction(c)) { var res = c.apply(sql, [sql]); columns = columns.concat(isArray(res) ? res : [res]); } else { columns.push(c); } }); var select = []; columns.forEach(function (c) { if (isHash(c)) { for (var i in c) { select.push(new AliasedExpression(this.stringToIdentifier(i), c[i])); } } else if (isString(c)) { select.push(this.stringToIdentifier(c)); } else { select.push(c); } }, this); return this.mergeOptions({select: select}); }
Returns a cloned dataset that selects *.
Returnspatio.Dataset
a cloned dataset that selects *.
function (){ return this.mergeOptions({select: null}); }
Returns a copy of the dataset with the given columns added to the existing selected columns. If no columns are currently selected, it will select the columns given in addition to *.
ExampleDB.from("items").select("a").selectAppend("b").sql; //=> SELECT b FROM items DB.from("items").select("a").selectAppend("b", "c", "d").sql //=> SELECT a, b, c, d FROM items DB.from("items").selectAppend("b").sql //=> SELECT *, b FROM itemsArguments
cols variable number of columns to add to the select statement
patio.Dataset
returns a cloned dataset with the new select columns appended.
function (cols){ cols = argsToArray(arguments); var currentSelect = this.__opts.select; if (!currentSelect || !currentSelect.length) { currentSelect = [this._static.WILDCARD]; } return this.select.apply(this, currentSelect.concat(cols)); }
Returns a promise resolved with a hash with keyColumn values as keys and valueColumn values as values. Similar to patio.Dataset#toHash, but only selects the two columns.
Example// SELECT id, name FROM table DB.from("table").selectHash("id", "name").chain(function(hash){ // e.g {1 : 'a', 2 : 'b', ...} });Arguments
the column to use as the key in the hash.
the column to use as the value in the hash.
the callback to invoke when the action is done.
comb.Promise
a promise that is resolved with an array of hashes, that have the keyColumn as the key and the valueColumn as the value.
function (keyColumn,valueColumn,cb){ var map = {}, args = comb.argsToArray(arguments); cb = isFunction(args[args.length - 1]) ? args.pop() : null; var k = this.__hashIdentifierToName(keyColumn), v = this.__hashIdentifierToName(valueColumn); return this.select.apply(this, args).map(function (r) { map[r[k]] = v ? r[v] : r; }).chain(function () { return map; }).classic(cb).promise(); }
Selects the columns if only if there is not already select sources.
Examplevar ds = DB.from("items"); //SELECT * FROM items ds.select("a"); //SELECT a FROM items; ds.select("a").selectIfNoSource("a", "b"). //SELECT a FROM items; ds.selectIfNoSource("a", "b"). //SELECT a, b FROM items;Arguments
columns to select if there is not already select sources.
patio.Dataset
a cloned dataset with the appropriate select sources.
function (cols){ var ret; if (!this.hasSelectSource) { ret = this.select.apply(this, arguments); } else { ret = this.mergeOptions(); } return ret; }
Selects the column given (either as an argument or as a callback), and returns an array of all values of that column in the dataset. If you give a block argument that returns an array with multiple entries, the contents of the resulting array are undefined.
Example// SELECT id FROM table DB.from("table").selectMap("id").chain(function(selectMap){ // e,g. selectMap === [3, 5, 8, 1, ...] }); // SELECT abs(id) FROM table DB.from("table").selectMap(function(){ return this.abs("id"); }).chain(function(selectMap){ //e.g selectMap === [3, 5, 8, 1, ...] });Arguments
The column to return the values for. See patio.Dataset#select for valid column values.
a function to be called when the aciton is complete
comb.Promise
a promise resolved with the array of mapped values.
function (column,cb){ var ds = this.naked().ungraphed().select(column), col; return ds.map(function (r) { return r[col || (col = Object.keys(r)[0])]; }, cb); }
Returns a copy of the dataset with the given columns added to the existing selected columns. If no columns are currently selected it will just select the columns given.
ExampleDB.from("items").select("a").select("b").sql; //=> SELECT b FROM items DB.from("items").select("a").selectMore("b", "c", "d").sql //=> SELECT a, b, c, d FROM items DB.from("items").selectMore("b").sql //=> SELECT b FROM itemsArguments
cols variable number of columns to add to the select statement
patio.Dataset
returns a cloned dataset with the new select columns appended.
function (cols){ cols = argsToArray(arguments); var currentSelect = this.__opts.select; return this.select.apply(this, (currentSelect || []).concat(cols)); }
The same as patio.Dataset#selectMap, but in addition orders the array by the column.
Example// SELECT id FROM table ORDER BY id DB.from("table").selectOrderMap("id").chain(function(mappedIds){ //e.g. [1, 2, 3, 4, ...] }); // SELECT abs(id) FROM table ORDER BY abs(id) DB.from("table").selectOrderMap(function(){ return this.abs("id"); }).chain(function(mappedIds){ //e.g. [1, 2, 3, 4, ...] });Arguments
The column to return the values for. See patio.Dataset#select for valid column values.
comb.Promise
a promise resolved with the array of mapped values.
function (column,cb){ var col, ds = this.naked() .ungraphed() .select(column) .order(isFunction(column) ? column : this._unaliasedIdentifier(column)); return ds.map(function (r) { return r[col || (col = Object.keys(r)[0])]; }, cb); }
function (){ this.update.apply(this, arguments); }
Set the default values for insert and update statements. The values hash passed to insert or update are merged into this hash, so any values in the hash passed to insert or update will override values passed to this method.
ExampleDB.from("items").setDefaults({a : 'a', c : 'c'}).insert({a : 'd', b : 'b'}).insertSql(); //=> INSERT INTO items (a, c, b) VALUES ('d', 'c', 'b')Arguments
object with key value pairs to use as override values
patio.Dataset
a cloned dataset with the defaults added to the current datasets defaults.
function (hash){ return this.mergeOptions({defaults: merge({}, this.__opts.defaults || {}, hash)}); }
This allows you to manually specify the graph aliases to use when using graph. You can use it to only select certain columns, and have those columns mapped to specific aliases in the result set. This is the equivalent of patio.Dataset#select for a graphed dataset, and must be used instead of patio.Dataset#select whenever graphing is used.
Examplevar DB = patio.defaultDatabase, ds = DB.from("artists"); var p = comb.executeInOrder(ds, function(ds){ var graphedDs = ds.graph("albums", {artist_id : id}); //SELECT artists.name AS artist_name, albums.name AS album_name, 42 AS forty_two FROM table return graphedDs.setGraphAliases({artist_name : ["artists", "name"], album_name : ["albums", "name"], forty_two : ["albums", "fourtwo", 42]).first(); }); p.chain(function(obj){ //obj == {artists : {name : artists.name}, albums : {name : albums.name, fourtwo : 42}} });Arguments
Should be a hash with keys being column aliases, and values being arrays with two or three elements. The first element of the array should be the table alias, and the second should be the actual column name. If the array has a third element, it is used as the value returned, instead of tableAlias.columnName.
function (graphAliases){ var ds = this.select.apply(this, this.__graphAliasColumns(graphAliases)); ds.__opts.graphAliases = graphAliases; return ds; }
Set values that override hash arguments given to insert and update statements. This hash is merged into the hash provided to insert or update, so values will override any values given in the insert/update hashes.
ExampleDB.from("items").setOverrides({a : 'a', c : 'c'}).insert({a : 'd', b : 'b'}).insertSql(); //=> INSERT INTO items (a, c, b) VALUES ('a', 'c', 'b')Arguments
object with key value pairs to use as override values
patio.Dataset
a cloned dataset with the overrides added to the current datasets overrides.
function (hash){ return this.mergeOptions({overrides: merge({}, this.__opts.overrides || {}, hash)}); }
Returns a promise resolved with the first record in the dataset, or null if the dataset has no records. Users should probably use patio.Dataset#first instead of this method.
Example//'SELECT * FROM test LIMIT 1' DB.from("test").singleRecord().chain(function(r) { //e.g r === {id : 1, name : "firstName"} });Arguments
a function to be called when the aciton is complete
comb.Promise
a promise resolved with the first record returned from the query.
function (cb){ return this.mergeOptions({limit: 1}).all().chain(function (r) { return r && r.length ? r[0] : null; }).classic(cb).promise(); }
Returns a promise resolved with the first value of the first record in the dataset. Returns null if dataset is empty. Users should generally use patio.Dataset#get instead of this method.
Example//'SELECT * FROM test LIMIT 1' DB.from("test").singleValue().chain(function(r) { //e.g r === 1 });Arguments
the callback to invoke when the action is done.
comb.Promise
a promise that will be resolved with the first value of the first row returned from the dataset.
function (cb){ return this.naked().ungraphed().singleRecord().chain(function (r) { return r ? r[Object.keys(r)[0]] : null; }).classic(cb).promise(); }
Returns a Stream for streaming data from the database.
User
.stream()
.on("data", function(record){
console.log(record);
})
.on("error", errorHandler)
.on("end", function(){
console.log("all done")
});
//postgres options
User
.stream({batchSize: 100, highWaterMark: 1000})
.on("data", function(record){
console.log(record);
})
.on("error", errorHandler)
.on("end", function(){
console.log("all done")
});
Arguments
an object to pass to the adapters connection stream implementation
Stream
function (opts){ var queryStream = this.fetchRows(this.selectSql, merge(opts || {}, {stream: true})), rowCb, ret; if ((rowCb = this.rowCb)) { ret = new TransformStream({objectMode: true}); ret._transform = function (data, encoding, done) { when(rowCb(data)).chain(function (data) { ret.push(data); done(); }, done); }; pipeAll(queryStream, ret); } else { ret = queryStream; } return ret; }
Converts a string to an patio.sql.Identifier, patio.sql.QualifiedIdentifier, or patio.sql.AliasedExpression, depending on the format:
ds.stringToIdentifier("a") //= > new patio.sql.Identifier("a"); ds.stringToIdentifier("table__column"); //=> new patio.sql.QualifiedIdentifier(table, column); ds.stringToIdentifier("table__column___alias"); //=> new patio.sql.AliasedExpression(new patio.sql.QualifiedIdentifier(table, column), alias);Arguments
the name to covert to an an patio.sql.Identifier, patio.sql.QualifiedIdentifier, or patio.sql.AliasedExpression.
patio.sql.Identifier|patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression
an identifier generated based on the name string.
function (name){ if (isString(name)) { var parts = this._splitString(name), schema = parts[0], table = parts[1], alias = parts[2], identifier; if (schema && table && alias) { identifier = new AliasedExpression(new QualifiedIdentifier(schema, table), alias); } else if (schema && table) { identifier = new QualifiedIdentifier(schema, table); } else if (table && alias) { identifier = new AliasedExpression(new Identifier(table), alias); } else { identifier = new Identifier(table); } return identifier; } else { return name; } }
function (s){ return string.format("%s[%s]", this.literal(s.f), this.__expressionList(s.sub)); }
Returns a promise resolved the sum for the given column.
Example// SELECT sum(id) FROM table LIMIT 1 DB.from("table").sum("id").chain(function(sum){ // e.g sum === 55 });Arguments
the column to find the sum of.
the callback to invoke when the action is done.
comb.Promise
a promise resolved with the sum of the column.
function (column,cb){ return this.__aggregateDataset().get(sql.sum(this.stringToIdentifier(column)), cb); }
Returns a promise resolved with a string in CSV format containing the dataset records. By default the CSV representation includes the column titles in the first line. You can turn that off by passing false as the includeColumnTitles argument.
NOTE: This does not use a CSV library or handle quoting of values in any way. If any values in any of the rows could include commas or line endings, you shouldn't use this.
Example// SELECT * FROM table DB.from("table").toCsv().chain(function(csv){ console.log(csv); //outputs id,name 1,Jim 2,Bob }); // SELECT * FROM table DB.from("table").toCsv(false).chain(function(csv){ console.log(csv); //outputs 1,Jim 2,Bob });Arguments
true
] : Set to false to prevent the printing of the column titles as the first line.
the callback to invoke when the action is done.
comb.Promise
a promise that will be resolved with the CSV string of the results of the query.
function (includeColumnTitles,cb){ var n = this.naked(); if (isFunction(includeColumnTitles)) { cb = includeColumnTitles; includeColumnTitles = true; } includeColumnTitles = isBoolean(includeColumnTitles) ? includeColumnTitles : true; return n.columns.chain(function (cols) { var vals = []; if (includeColumnTitles) { vals.push(cols.join(", ")); } return n.forEach(function (r) { vals.push(cols.map(function (c) { return r[c] || ""; }).join(", ")); }).chain(function () { return vals.join("\r\n") + "\r\n"; }); }.bind(this)).classic(cb).promise(); }
Returns a promise resolved with a hash with one column used as key and another used as value. If rows have duplicate values for the key column, the latter row(s) will overwrite the value of the previous row(s). If the valueColumn is not given or null, uses the entire hash as the value.
Example// SELECT * FROM table DB.from("table").toHash("id", "name").chain(function(hash){ // {1 : 'Jim', 2 : 'Bob', ...} }); // SELECT * FROM table DB.from("table").toHash("id").chain(function(hash){ // {1 : {id : 1, name : 'Jim'}, 2 : {id : 2, name : 'Bob'}, ...} });Arguments
the column to use as the key in the returned hash.
the callback to invoke when the action is done.
null
] String|patio.sql.Identifier\patio.sql.QualifiedIdentifier|patio.sql.AliasedExpression
: the key of the column to use as the value in the hash
comb.Promise
a promise that will be resolved with the resulting hash.
function (keyColumn,valueColumn,cb){ var ret = new Promise(), map = {}; if (isFunction(valueColumn)) { cb = valueColumn; valueColumn = null; } var k = this.__hashIdentifierToName(keyColumn), v = this.__hashIdentifierToName(valueColumn); return this.map(function (r) { map[r[k]] = v ? r[v] : r; }).chain(function () { return map; }).classic(cb).promise(); }
Truncates the dataset. Returns a promise that is resolved once truncation is complete.
Example// TRUNCATE table DB.from("table").truncate().chain(function(){ //...do something });Arguments
the callback to invoke when the action is done.
comb.Promise
a promise that is resolved once truncation is complete.
function (cb){ return this.executeDdl(this.truncateSql).classic(cb); }
Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
ExampleDB.from("items").group("a").having({a : 1}).where("b").unfiltered().sql; //=> SELECT * FROM items GROUP BY aReturns
patio.Dataset
a cloned dataset with no HAVING or WHERE clause.
function (){ return this.mergeOptions({where: null, having: null}); }
Remove the splitting of results into subhashes, and all metadata related to the current graph (if any).
Sourcefunction (){ return this.mergeOptions({graph: null}); }
Returns a copy of the dataset with no GROUP or HAVING clause.
ExampleDB.from("t").group("a").having({a : 1}).where("b").ungrouped().sql; //=> SELECT * FROM t WHERE bReturns
patio.Dataset
a cloned dataset with no GROUP or HAVING clause.
function (){ return this.mergeOptions({group: null, having: null}); }
Adds a UNION clause using a second dataset object. A UNION compound dataset returns all rows in either the current dataset or the given dataset. Options: :alias :: Use the given value as the from_self alias :all :: Set to true to use UNION ALL instead of UNION, so duplicate rows can occur :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
ExampleDB.from("items").union(DB.from("otherItems")).sql; //=> SELECT * FROM items UNION SELECT * FROM other_items DB.from("items").union(DB.from("otherItems"), {all : true, fromSelf : false}).sql; //=> SELECT * FROM items UNION ALL SELECT * FROM other_items DB.from("items").union(DB.from("otherItems"), {alias : "i"}) //=> SELECT * FROM (SELECT * FROM items UNION SELECT * FROM other_items) AS iArguments
dataset to union with
addional options
String|patio.sql.Identifier
: Alias to use as the fromSelf alias.
false
] Boolean
: Set to true to use UNION ALL instead of UNION so duplicate rows can occur
true
] Boolean
: Set to false to not wrap the returned dataset in a fromSelf.
patio.Dataset
a cloned dataset with the union.
function (dataset,opts){ opts = isUndefined(opts) ? {} : opts; if (!isHash(opts)) { opts = {all: opts}; } return this.compoundClone("union", dataset, opts); }
Returns a copy of the dataset with no limit or offset.
ExampleDB.from("t").limit(10, 20).unlimited().sql; //=> SELECT * FROM tReturns
patio.Dataset
a cloned dataset with no limit or offset.
function (){ return this.mergeOptions({limit: null, offset: null}); }
Returns a copy of the dataset with no order.
ExampleDB.from("t").order("a", sql.identifier("b").desc()).unordered().sql; //=> SELECT * FROM tReturns
patio.Dataset
a cloned dataset with no order.
function (){ return this.order(null); }
Creates a unique table alias that hasn't already been used in this dataset.
ExampleDB.from("table").unusedTableAlias("t"); //=> "t" DB.from("table").unusedTableAlias("table"); //=> "table0" DB.from("table", "table0"]).unusedTableAlias("table"); //=> "table1"Arguments
the table to get an unused alias for.
String
the implicit alias that is in tableAlias with a possible "N" if the alias has already been used, where N is an integer starting at 0.
function (tableAlias){ tableAlias = this._toTableName(tableAlias); var usedAliases = [], from, join; if ((from = this.__opts.from) != null) { usedAliases = usedAliases.concat(from.map(function (n) { return this._toTableName(n); }, this)); } if ((join = this.__opts.join) != null) { usedAliases = usedAliases.concat(join.map(function (join) { if (join.tableAlias) { return this.__toAliasedTableName(join.tableAlias); } else { return this._toTableName(join.table); } }, this)); } if (usedAliases.indexOf(tableAlias) !== -1) { var base = tableAlias, i = 0; do { tableAlias = string.format("%s%d", base, i++); } while (usedAliases.indexOf(tableAlias) !== -1); } return tableAlias; }
Updates values for the dataset. The returned promise is resolved with a value that is generally the number of rows updated, but that is adapter dependent.
Example// UPDATE table SET x = NULL DB.from("table").update({x : null}).chain(function(numRowsUpdated){ //e.g. numRowsUpdated === 10 }); // UPDATE table SET x = (x + 1), y = 0 DB.from("table").update({ x : sql.x.plus(1), y : 0}).chain(function(numRowsUpdated){ // e.g. numRowsUpdated === 10 });Arguments
See patio.Dataset#updateSql for parameter types.
the callback to invoke when the action is done.
comb.Promise
a promise that is generally resolved with the number of rows updated, but that is adapter dependent.
function (values,cb){ return this.executeDui(this.updateSql(values)).classic(cb); }
Formats an UPDATE statement using the given values. See patio.dataset._Actions#update.
ExampleDB.from("items").updateSql({price : 100, category : 'software'}); //=> "UPDATE items SET price = 100, category = 'software'Arguments
*...
: number of values to update the table with. The UPDATE statement created depends on the values passed in.
String
the UPDATE statement.
QueryError
If the dataset is grouped or includes more than one table.
function (values){ values = argsToArray(arguments); var update; if (this.__opts.sql) { update = this._staticSql(this.__opts.sql); } else { this.__checkModificationAllowed(); update = this.mergeOptions({values: values})._updateSql(); } return update; }
Add a condition to the WHERE clause. See patio.Dataset#filter for argument types.
ExampleDB.from("test").where('price < ? AND id in ?', 100, [1, 2, 3]).sql; //=> "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))" DB.from("test").where('price < {price} AND id in {ids}', {price:100, ids:[1, 2, 3]}).sql; //=> "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))")Source
function (){ return this._filter.apply(this, ["where"].concat(argsToArray(arguments))); }
Add a common table expression (CTE) with the given name and a dataset that defines the CTE. A common table expression acts as an inline view for the query.
ExampleDB.from("t")["with"]("t", db.from("x"))["with"]("j", db.from("y")).sql; //=> 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y) SELECT * FROM t' DB.from("t")["with"]("t", db.from("x")).withRecursive("j", db.from("y"), db.from("j")).sql; //=> 'WITH t AS (SELECT * FROM x), j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t' DB.from("t")["with"]("t", db.from("x"), {args:["b"]}).sql; //=> 'WITH t(b) AS (SELECT * FROM x) SELECT * FROM t'Arguments
the name of the to assign to the CTE.
the dataset to use for the CTE.
extra options.
String[]
: colums/args for the CTE.
Boolean
: set to true that the CTE is recursive.
patio.Dataset
a cloned dataset with the CTE.
function (name,dataset,opts){ if (!this.supportsCte) { throw new QueryError("this dataset does not support common table expressions"); } return this.mergeOptions({ "with": (this.__opts["with"] || []).concat([merge(opts || {}, {name: this.stringToIdentifier(name), dataset: dataset})]) }); }
Add a recursive common table expression (CTE) with the given name, a dataset that defines the nonrecursive part of the CTE, and a dataset that defines the recursive part of the CTE.
Example//Sing withRecursive call. DB.from("t").withRecursive("t", db.from("x"), db.from("t")).sql; //=> 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t' //Multiple withRecursive calls. DB.from("t").withRecursive("t", db.from("x"), db.from("t")) .withRecursive("j", db.from("y"), db.from("j")).sql; //=> 'WITH t AS (SELECT * FROM x UNION ALL SELECT * FROM t), j AS (SELECT * FROM y UNION ALL SELECT * FROM j) SELECT * FROM t'; //Adding args DB.from("t").withRecursive("t", db.from("x"), db.from("t"), {args:["b", "c"]}).sql; //=> 'WITH t(b, c) AS (SELECT * FROM x UNION ALL SELECT * FROM t) SELECT * FROM t' //Setting union all to false DB.from("t").withRecursive("t", db.from("x"), db.from("t"), {unionAll:false}).sql; //=> 'WITH t AS (SELECT * FROM x UNION SELECT * FROM t) SELECT * FROM t');Arguments
the name to assign to the CTE
the non-recursive part of the CTE
the recursive part of the CTE
{}
] : extra options
String[]
: columns to include with the CTE
Boolena
: set to false to use UNION instead of UNION ALL when combining non recursive with recursive.
patio.Dataset
a cloned dataset with the CTE.
function (name,nonRecursive,recursive,opts){ if (!this.supportsCte) { throw new QueryError("This dataset does not support common table expressions"); } opts = opts || {}; var wit = (this.__opts["with"] || []).concat([merge(opts, {recursive: true, name: this.stringToIdentifier(name), dataset: nonRecursive.union(recursive, {all: opts.unionAll !== false, fromSelf: false})})]); return this.mergeOptions({"with": wit}); }
Returns a copy of the dataset with the static SQL used. This is useful if you want to keep the same patio.Dataset#rowCb/ patio.Dataset#graph, but change the SQL used to custom SQL.
ExampleDB.from("items").withSql('SELECT * FROM foo') //=> SELECT * FROM fooArguments
sql for the dataset to use.
patio.Dataset
a cloned dataset with the static sql set.
function (sql){ var args = argsToArray(arguments).slice(1); if (args.length) { sql = new PlaceHolderLiteralString(sql, args); } return this.mergeOptions({sql: sql}); }