@uwdata/mosaic-sql 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.md +1 -1
- package/dist/mosaic-sql.js +103 -48
- package/dist/mosaic-sql.min.js +1 -1
- package/package.json +3 -2
- package/src/Query.js +2 -1
- package/src/datetime.js +1 -1
- package/src/expression.js +68 -0
- package/src/index.js +11 -3
- package/src/ref.js +0 -27
- package/src/sql-tag.js +34 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023, UW Interactive Data Lab
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
CHANGED
package/dist/mosaic-sql.js
CHANGED
|
@@ -37,34 +37,6 @@ function column(table, column2) {
|
|
|
37
37
|
function all(table) {
|
|
38
38
|
return new Ref(table, "*");
|
|
39
39
|
}
|
|
40
|
-
function desc(expr2) {
|
|
41
|
-
expr2 = asColumn(expr2);
|
|
42
|
-
return {
|
|
43
|
-
expr: expr2,
|
|
44
|
-
desc: true,
|
|
45
|
-
toString: () => `${expr2} DESC NULLS LAST`,
|
|
46
|
-
get columns() {
|
|
47
|
-
return expr2.columns?.() || [];
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
function expr(sql, columns, label) {
|
|
52
|
-
return {
|
|
53
|
-
expr: sql,
|
|
54
|
-
label,
|
|
55
|
-
toString: () => `${sql}`,
|
|
56
|
-
get columns() {
|
|
57
|
-
return columns || [];
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
function transform(func2, label) {
|
|
62
|
-
return (value) => expr(
|
|
63
|
-
func2(value),
|
|
64
|
-
asColumn(value).columns,
|
|
65
|
-
label
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
40
|
|
|
69
41
|
// src/to-sql.js
|
|
70
42
|
function toSQL(value) {
|
|
@@ -89,6 +61,87 @@ function literalToSQL(value) {
|
|
|
89
61
|
}
|
|
90
62
|
}
|
|
91
63
|
|
|
64
|
+
// src/expression.js
|
|
65
|
+
var isParamLike = (e) => typeof e?.addEventListener === "function";
|
|
66
|
+
function isExpression(e) {
|
|
67
|
+
return e instanceof SQLExpression;
|
|
68
|
+
}
|
|
69
|
+
var SQLExpression = class {
|
|
70
|
+
constructor(sql2, columns, label) {
|
|
71
|
+
this.expr = sql2;
|
|
72
|
+
this.label = label;
|
|
73
|
+
this.columns = columns || [];
|
|
74
|
+
}
|
|
75
|
+
toString() {
|
|
76
|
+
return `${this.expr}`;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var ParameterizedSQLExpression = class extends SQLExpression {
|
|
80
|
+
constructor(parts, columns, label) {
|
|
81
|
+
const paramSet = /* @__PURE__ */ new Set();
|
|
82
|
+
for (const part of parts) {
|
|
83
|
+
if (isParamLike(part))
|
|
84
|
+
paramSet.add(part);
|
|
85
|
+
}
|
|
86
|
+
paramSet.forEach((param) => {
|
|
87
|
+
param.addEventListener("value", () => this.update());
|
|
88
|
+
});
|
|
89
|
+
super(parts, columns, label);
|
|
90
|
+
}
|
|
91
|
+
toString() {
|
|
92
|
+
return this.expr.map((p) => isParamLike(p) ? literalToSQL(p.value) : p).join("");
|
|
93
|
+
}
|
|
94
|
+
addEventListener(type, callback) {
|
|
95
|
+
const map = this.map || (this.map = /* @__PURE__ */ new Map());
|
|
96
|
+
const set = map.get(type) || (map.set(type, /* @__PURE__ */ new Set()), map.get(type));
|
|
97
|
+
set.add(callback);
|
|
98
|
+
}
|
|
99
|
+
update() {
|
|
100
|
+
this.map?.get("value")?.forEach((callback) => callback(this));
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
function exprParams(parts, columns, label) {
|
|
104
|
+
return new ParameterizedSQLExpression(parts, columns, label);
|
|
105
|
+
}
|
|
106
|
+
function expr(sql2, columns, label) {
|
|
107
|
+
return new SQLExpression(sql2, columns, label);
|
|
108
|
+
}
|
|
109
|
+
function desc(e) {
|
|
110
|
+
return Object.assign(
|
|
111
|
+
expr(`${asColumn(e)} DESC NULLS LAST`, e?.columns, e?.label),
|
|
112
|
+
{ desc: true }
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
function transform(func2, label) {
|
|
116
|
+
return (value) => expr(func2(value), asColumn(value).columns, label);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/sql-tag.js
|
|
120
|
+
function sql(strings, ...exprs) {
|
|
121
|
+
const spans = [strings[0]];
|
|
122
|
+
const colset = /* @__PURE__ */ new Set();
|
|
123
|
+
const n = exprs.length;
|
|
124
|
+
for (let i = 0, k = 0; i < n; ) {
|
|
125
|
+
const e = exprs[i];
|
|
126
|
+
if (isParamLike(e)) {
|
|
127
|
+
spans[++k] = e;
|
|
128
|
+
} else {
|
|
129
|
+
if (Array.isArray(e.columns)) {
|
|
130
|
+
e.columns.forEach((col) => colset.add(col));
|
|
131
|
+
}
|
|
132
|
+
spans[k] += String(e);
|
|
133
|
+
}
|
|
134
|
+
const s = strings[++i];
|
|
135
|
+
if (isParamLike(spans[k])) {
|
|
136
|
+
spans[++k] = s;
|
|
137
|
+
} else {
|
|
138
|
+
spans[k] += s;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const columns = Array.from(colset);
|
|
142
|
+
return spans.length > 1 ? exprParams(spans, columns) : expr(spans[0], columns);
|
|
143
|
+
}
|
|
144
|
+
|
|
92
145
|
// src/literal.js
|
|
93
146
|
var Literal = class {
|
|
94
147
|
constructor(value) {
|
|
@@ -431,7 +484,7 @@ var Query = class {
|
|
|
431
484
|
list.push({ as: e, from: asRelation(e) });
|
|
432
485
|
} else if (e instanceof Ref) {
|
|
433
486
|
list.push({ as: e.table, from: e });
|
|
434
|
-
} else if (isQuery(e) ||
|
|
487
|
+
} else if (isQuery(e) || isExpression(e)) {
|
|
435
488
|
list.push({ from: e });
|
|
436
489
|
} else if (Array.isArray(e)) {
|
|
437
490
|
list.push({ as: unquote(e[0]), from: asRelation(e[1]) });
|
|
@@ -588,60 +641,60 @@ var Query = class {
|
|
|
588
641
|
offset,
|
|
589
642
|
with: cte
|
|
590
643
|
} = this.query;
|
|
591
|
-
const
|
|
644
|
+
const sql2 = [];
|
|
592
645
|
if (cte.length) {
|
|
593
646
|
const list = cte.map(({ as, query }) => `"${as}" AS (${query})`);
|
|
594
|
-
|
|
647
|
+
sql2.push(`WITH ${list.join(", ")}`);
|
|
595
648
|
}
|
|
596
649
|
const sels = select.map(
|
|
597
650
|
({ as, expr: expr2 }) => isColumnRefFor(expr2, as) && !expr2.table ? `${expr2}` : `${expr2} AS "${as}"`
|
|
598
651
|
);
|
|
599
|
-
|
|
652
|
+
sql2.push(`SELECT${distinct ? " DISTINCT" : ""} ${sels.join(", ")}`);
|
|
600
653
|
if (from.length) {
|
|
601
654
|
const rels = from.map(({ as, from: from2 }) => {
|
|
602
655
|
const rel = isQuery(from2) ? `(${from2})` : `${from2}`;
|
|
603
656
|
return !as || as === from2.table ? rel : `${rel} AS "${as}"`;
|
|
604
657
|
});
|
|
605
|
-
|
|
658
|
+
sql2.push(`FROM ${rels.join(", ")}`);
|
|
606
659
|
}
|
|
607
660
|
if (sample) {
|
|
608
661
|
const { rows, perc, method, seed } = sample;
|
|
609
662
|
const size = rows ? `${rows} ROWS` : `${perc} PERCENT`;
|
|
610
663
|
const how = method ? ` (${method}${seed != null ? `, ${seed}` : ""})` : "";
|
|
611
|
-
|
|
664
|
+
sql2.push(`USING SAMPLE ${size}${how}`);
|
|
612
665
|
}
|
|
613
666
|
if (where.length) {
|
|
614
667
|
const clauses = where.map(String).filter((x) => x).join(" AND ");
|
|
615
668
|
if (clauses)
|
|
616
|
-
|
|
669
|
+
sql2.push(`WHERE ${clauses}`);
|
|
617
670
|
}
|
|
618
671
|
if (groupby.length) {
|
|
619
|
-
|
|
672
|
+
sql2.push(`GROUP BY ${groupby.join(", ")}`);
|
|
620
673
|
}
|
|
621
674
|
if (having.length) {
|
|
622
675
|
const clauses = having.map(String).filter((x) => x).join(" AND ");
|
|
623
676
|
if (clauses)
|
|
624
|
-
|
|
677
|
+
sql2.push(`HAVING ${clauses}`);
|
|
625
678
|
}
|
|
626
679
|
if (window.length) {
|
|
627
680
|
const windows = window.map(({ as, expr: expr2 }) => `"${as}" AS (${expr2})`);
|
|
628
|
-
|
|
681
|
+
sql2.push(`WINDOW ${windows.join(", ")}`);
|
|
629
682
|
}
|
|
630
683
|
if (qualify.length) {
|
|
631
684
|
const clauses = qualify.map(String).filter((x) => x).join(" AND ");
|
|
632
685
|
if (clauses)
|
|
633
|
-
|
|
686
|
+
sql2.push(`QUALIFY ${clauses}`);
|
|
634
687
|
}
|
|
635
688
|
if (orderby.length) {
|
|
636
|
-
|
|
689
|
+
sql2.push(`ORDER BY ${orderby.join(", ")}`);
|
|
637
690
|
}
|
|
638
691
|
if (Number.isFinite(limit)) {
|
|
639
|
-
|
|
692
|
+
sql2.push(`LIMIT ${limit}`);
|
|
640
693
|
}
|
|
641
694
|
if (Number.isFinite(offset)) {
|
|
642
|
-
|
|
695
|
+
sql2.push(`OFFSET ${offset}`);
|
|
643
696
|
}
|
|
644
|
-
return
|
|
697
|
+
return sql2.join(" ");
|
|
645
698
|
}
|
|
646
699
|
};
|
|
647
700
|
var SetOperation = class {
|
|
@@ -692,17 +745,17 @@ var SetOperation = class {
|
|
|
692
745
|
}
|
|
693
746
|
toString() {
|
|
694
747
|
const { op, queries, query: { orderby, limit, offset } } = this;
|
|
695
|
-
const
|
|
748
|
+
const sql2 = [queries.join(` ${op} `)];
|
|
696
749
|
if (orderby.length) {
|
|
697
|
-
|
|
750
|
+
sql2.push(`ORDER BY ${orderby.join(", ")}`);
|
|
698
751
|
}
|
|
699
752
|
if (Number.isFinite(limit)) {
|
|
700
|
-
|
|
753
|
+
sql2.push(`LIMIT ${limit}`);
|
|
701
754
|
}
|
|
702
755
|
if (Number.isFinite(offset)) {
|
|
703
|
-
|
|
756
|
+
sql2.push(`OFFSET ${offset}`);
|
|
704
757
|
}
|
|
705
|
-
return
|
|
758
|
+
return sql2.join(" ");
|
|
706
759
|
}
|
|
707
760
|
};
|
|
708
761
|
function isQuery(value) {
|
|
@@ -735,6 +788,7 @@ export {
|
|
|
735
788
|
epoch_ms,
|
|
736
789
|
eq,
|
|
737
790
|
expr,
|
|
791
|
+
exprParams,
|
|
738
792
|
first,
|
|
739
793
|
gt,
|
|
740
794
|
gte,
|
|
@@ -780,6 +834,7 @@ export {
|
|
|
780
834
|
regrSlope,
|
|
781
835
|
relation,
|
|
782
836
|
skewness,
|
|
837
|
+
sql,
|
|
783
838
|
stddev,
|
|
784
839
|
stddevPop,
|
|
785
840
|
stringAgg,
|
package/dist/mosaic-sql.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var m=class{constructor(t,r){t&&(this.table=String(t)),r&&(this.column=r)}get columns(){return this.column?[this.column]:[]}toString(){let{table:t,column:r}=this;if(r){let n=r==="*"?r:`"${r}"`;return(t?`"${t}".`:"")+n}else return`"${t}"`}};function _(e,t){return e instanceof m&&e.column===t}function f(e){return typeof e=="string"?B(e):e}function E(e){return typeof e=="string"?X(e):e}function X(e){return new m(e)}function B(e,t){return arguments.length===1?new m(null,e):new m(e,t)}function k(e){return new m(e,"*")}function K(e){return e=f(e),{expr:e,desc:!0,toString:()=>`${e} DESC NULLS LAST`,get columns(){return e.columns?.()||[]}}}function V(e,t,r){return{expr:e,label:r,toString:()=>`${e}`,get columns(){return t||[]}}}function b(e,t){return r=>V(e(r),f(r).columns,t)}function h(e){return typeof e=="string"?`"${e}"`:d(e)}function d(e){switch(typeof e){case"boolean":return e?"TRUE":"FALSE";case"string":return`'${e}'`;default:return e==null?"NULL":e instanceof Date?`MAKE_DATE(${e.getUTCFullYear()}, ${e.getUTCMonth()+1}, ${e.getUTCDate()})`:e instanceof RegExp?`'${e.source}'`:String(e)}}var I=class{constructor(t){this.value=t}toString(){return d(this.value)}},z=e=>new I(e);function q(...e){return e.flat().flatMap(t=>t?.columns||[])}var D=class{constructor(t,r){this.op=t,this.a=f(r)}get columns(){return q(this.a)}visit(t){t(this.op,this)}toString(){let{op:t,a:r}=this;return`(${h(r)} ${t})`}};function C(e){return t=>new D(e,t)}var J=C("NOT"),Z=C("IS NULL"),tt=C("IS NOT NULL"),O=class{constructor(t,r,n){this.op=t,this.a=f(r),this.b=f(n)}get columns(){return q(this.a,this.b)}visit(t){t(this.op,this)}toString(){let{op:t,a:r,b:n}=this;return`(${h(r)} ${t} ${h(n)})`}};function w(e){return(t,r)=>new O(e,t,r)}var rt=w("="),et=w("<>"),nt=w("<"),st=w(">"),ot=w("<="),it=w(">="),ct=w("IS DISTINCT FROM"),ut=w("IS NOT DISTINCT FROM"),L=class{constructor(t,r,n){this.op=t,this.expr=f(r),this.value=n?.map(f)}get columns(){return q(this.expr,this.value)}visit(t){t(this.op,this)}toString(){let{op:t,expr:r,value:n}=this;if(!n)return"";let[s,i]=n;return`(${h(r)} ${t} ${h(s)} AND ${h(i)})`}};function v(e){return(t,r)=>new L(e,t,r)}var lt=v("BETWEEN"),ft=v("NOT BETWEEN"),R=class{constructor(t,r){this.op=t,this.value=r.map(f)}get columns(){return q(this.value)}visit(t){t(this.op,this),this.value?.forEach(r=>r.visit(t))}toString(){let{op:t,value:r}=this;return!r||r.length===0?"":r.length===1?h(r[0]):`(${r.map(h).filter(n=>n).join(` ${t} `)})`}};function ht(...e){return new R("AND",e.flat())}function pt(...e){return new R("OR",e.flat())}var F=class{constructor(t,r){this.func=t,this.args=(r||[]).map(f)}get column(){return this.columns[0]}get columns(){return this.args.flatMap(t=>t.columns||[])}toString(){let{func:t,args:r}=this;return`${t}(${r.map(h).join(", ")})`}};function x(e){return(...t)=>new F(e,t)}var at=x("regexp_matches"),gt=x("contains"),mt=x("prefix"),xt=x("suffix"),$t=x("lower"),St=x("upper"),yt=x("length"),wt=x("isnan"),Nt=x("isfinite"),Et=x("isinf");var G=class{constructor(t,r){this.aggregate=t,this.args=(r||[]).map(f)}get label(){return this.aggregate.toLowerCase()+(this.args.length?` ${this.columns.join(", ")}`:"")}get column(){return this.columns[0]}get columns(){return this.args.flatMap(t=>t.columns||[])}distinct(){return this.isDistinct=!0,this}where(t){return this.filter=t,this}toString(){let{aggregate:t,args:r,isDistinct:n,filter:s}=this,i=r.length===0?"*":r.map(h).join(", "),c=n?"DISTINCT ":"",p=s?` FILTER (WHERE ${h(s)})`:"",y=t==="COUNT"?"::INTEGER":"";return p&&y?`(${t}(${c}${i})${p})${y}`:`${t}(${c}${i})${p}${y}`}};function o(e){return(...t)=>new G(e,t)}var dt=o("COUNT"),Rt=o("AVG"),qt=o("AVG"),At=o("MAD"),Tt=o("MAX"),bt=o("MIN"),It=o("SUM"),Dt=o("PRODUCT"),Ot=o("MEDIAN"),Lt=o("QUANTILE"),Ct=o("MODE"),Ft=o("VARIANCE"),Gt=o("STDDEV"),Ut=o("SKEWNESS"),Mt=o("KURTOSIS"),Pt=o("ENTROPY"),jt=o("VAR_POP"),Yt=o("STDDEV_POP"),_t=o("CORR"),Xt=o("COVAR_POP"),Bt=o("REGR_INTERCEPT"),Vt=o("REGR_SLOPE"),vt=o("REGR_COUNT"),Qt=o("REGR_R2"),Wt=o("REGR_SYY"),Ht=o("REGR_SXX"),kt=o("REGR_SXY"),Kt=o("REGR_AVGX"),zt=o("REGR_AVGY"),Jt=o("FIRST"),Zt=o("LAST"),tr=o("ARG_MIN"),rr=o("ARG_MAX"),er=o("STRING_AGG"),nr=o("ARRAY_AGG");var sr=b(e=>`(1000 * (epoch(${e}) - second(${e})) + millisecond(${e}))::DOUBLE`);function or(e){return{op:"UNNEST",arg:e,toString(){return`UNNEST(${Array.isArray(e)?`[${e.join(", ")}]`:e})`}}}var $=class{static select(...t){return new $().select(...t)}static from(...t){return new $().from(...t)}static with(...t){return new $().with(...t)}static union(...t){return new S("UNION",t.flat())}static unionAll(...t){return new S("UNION ALL",t.flat())}static intersect(...t){return new S("INTERSECT",t.flat())}static except(...t){return new S("EXCEPT",t.flat())}constructor(){this.query={with:[],select:[],from:[],where:[],groupby:[],having:[],window:[],qualify:[],orderby:[]}}clone(){let t=new $;return t.query={...this.query},t}with(...t){let{query:r}=this;if(t.length===0)return r.with;{let n=[],s=(i,c)=>{let p=c.clone();p.cteFor=this,n.push({as:i,query:p})};return t.flat().forEach(i=>{if(i!=null)if(i.as&&i.query)s(i.as,i.query);else for(let c in i)s(c,i[c])}),r.with=r.with.concat(n),this}}select(...t){let{query:r}=this;if(t.length===0)return r.select;{let n=[];return t.flat().forEach(s=>{if(s!=null)if(typeof s=="string")n.push({as:s,expr:f(s)});else if(s instanceof m)n.push({as:s.column,expr:s});else if(Array.isArray(s))n.push({as:s[0],expr:s[1]});else for(let i in s)n.push({as:A(i),expr:f(s[i])})}),r.select=r.select.concat(n),this}}$select(...t){return this.query.select=[],this.select(...t)}distinct(t=!0){return this.query.distinct=!!t,this}from(...t){let{query:r}=this;if(t.length===0)return r.from;{let n=[];return t.flat().forEach(s=>{if(s!=null)if(typeof s=="string")n.push({as:s,from:E(s)});else if(s instanceof m)n.push({as:s.table,from:s});else if(T(s)||Object.hasOwn(s,"toString"))n.push({from:s});else if(Array.isArray(s))n.push({as:A(s[0]),from:E(s[1])});else for(let i in s)n.push({as:A(i),from:E(s[i])})}),r.from=r.from.concat(n),this}}$from(...t){return this.query.from=[],this.from(...t)}sample(t){let{query:r}=this;if(arguments.length===0)return r.sample;{let n=t;return typeof t=="number"&&(n=t>0&&t<1?{perc:100*t}:{rows:Math.round(t)}),r.sample=n,this}}where(...t){let{query:r}=this;return t.length===0?r.where:(r.where=r.where.concat(t.flat().filter(n=>n)),this)}$where(...t){return this.query.where=[],this.where(...t)}groupby(...t){let{query:r}=this;return t.length===0?r.groupby:(r.groupby=r.groupby.concat(t.flat().filter(n=>n).map(f)),this)}having(...t){let{query:r}=this;return t.length===0?r.having:(r.having=r.having.concat(t.flat().filter(n=>n)),this)}window(...t){let{query:r}=this;if(t.length===0)return r.window;{let n=[];return t.flat().forEach(s=>{if(s!=null)for(let i in s)n.push({as:A(i),expr:s[i]})}),r.window=r.window.concat(n),this}}qualify(...t){let{query:r}=this;return t.length===0?r.qualify:(r.qualify=r.qualify.concat(t.flat().filter(n=>n)),this)}orderby(...t){let{query:r}=this;return t.length===0?r.orderby:(r.orderby=r.orderby.concat(t.flat().filter(n=>n).map(f)),this)}limit(t){let{query:r}=this;return arguments.length===0?r.limit:(r.limit=Number.isFinite(t)?t:void 0,this)}offset(t){let{query:r}=this;return arguments.length===0?r.offset:(r.offset=Number.isFinite(t)?t:void 0,this)}get subqueries(){let{query:t,cteFor:r}=this,s=(r?.query||t).with?.reduce((c,{as:p,query:y})=>(c[p]=y,c),{}),i=[];return t.from.forEach(({from:c})=>{if(T(c))i.push(c);else if(s[c.table]){let p=s[c.table];i.push(p)}}),i}toString(){let{select:t,distinct:r,from:n,sample:s,where:i,groupby:c,having:p,window:y,qualify:U,orderby:M,limit:P,offset:j,with:Y}=this.query,a=[];if(Y.length){let u=Y.map(({as:l,query:g})=>`"${l}" AS (${g})`);a.push(`WITH ${u.join(", ")}`)}let Q=t.map(({as:u,expr:l})=>_(l,u)&&!l.table?`${l}`:`${l} AS "${u}"`);if(a.push(`SELECT${r?" DISTINCT":""} ${Q.join(", ")}`),n.length){let u=n.map(({as:l,from:g})=>{let N=T(g)?`(${g})`:`${g}`;return!l||l===g.table?N:`${N} AS "${l}"`});a.push(`FROM ${u.join(", ")}`)}if(s){let{rows:u,perc:l,method:g,seed:N}=s,W=u?`${u} ROWS`:`${l} PERCENT`,H=g?` (${g}${N!=null?`, ${N}`:""})`:"";a.push(`USING SAMPLE ${W}${H}`)}if(i.length){let u=i.map(String).filter(l=>l).join(" AND ");u&&a.push(`WHERE ${u}`)}if(c.length&&a.push(`GROUP BY ${c.join(", ")}`),p.length){let u=p.map(String).filter(l=>l).join(" AND ");u&&a.push(`HAVING ${u}`)}if(y.length){let u=y.map(({as:l,expr:g})=>`"${l}" AS (${g})`);a.push(`WINDOW ${u.join(", ")}`)}if(U.length){let u=U.map(String).filter(l=>l).join(" AND ");u&&a.push(`QUALIFY ${u}`)}return M.length&&a.push(`ORDER BY ${M.join(", ")}`),Number.isFinite(P)&&a.push(`LIMIT ${P}`),Number.isFinite(j)&&a.push(`OFFSET ${j}`),a.join(" ")}},S=class{constructor(t,r){this.op=t,this.queries=r.map(n=>n.clone()),this.query={orderby:[]}}clone(){let t=new S(this.op,this.queries);return t.query={...this.query},t}orderby(...t){let{query:r}=this;return t.length===0?r.orderby:(r.orderby=r.orderby.concat(t.flat().filter(n=>n).map(f)),this)}limit(t){let{query:r}=this;return arguments.length===0?r.limit:(r.limit=Number.isFinite(t)?t:void 0,this)}offset(t){let{query:r}=this;return arguments.length===0?r.offset:(r.offset=Number.isFinite(t)?t:void 0,this)}get subqueries(){let{queries:t,cteFor:r}=this;return r&&t.forEach(n=>n.cteFor=r),t}toString(){let{op:t,queries:r,query:{orderby:n,limit:s,offset:i}}=this,c=[r.join(` ${t} `)];return n.length&&c.push(`ORDER BY ${n.join(", ")}`),Number.isFinite(s)&&c.push(`LIMIT ${s}`),Number.isFinite(i)&&c.push(`OFFSET ${i}`),c.join(" ")}};function T(e){return e instanceof $||e instanceof S}function A(e){return ir(e)?e.slice(1,-1):e}function ir(e){return e[0]==='"'&&e[e.length-1]==='"'}export{$ as Query,m as Ref,k as all,ht as and,rr as argmax,tr as argmin,nr as arrayAgg,f as asColumn,E as asRelation,Rt as avg,B as column,gt as contains,_t as corr,dt as count,Xt as covarPop,K as desc,Pt as entropy,sr as epoch_ms,rt as eq,V as expr,Jt as first,st as gt,it as gte,lt as isBetween,ct as isDistinct,Nt as isFinite,Et as isInfinite,wt as isNaN,ft as isNotBetween,ut as isNotDistinct,tt as isNotNull,Z as isNull,T as isQuery,Mt as kurtosis,Zt as last,yt as length,z as literal,d as literalToSQL,$t as lower,nt as lt,ot as lte,At as mad,Tt as max,qt as mean,Ot as median,bt as min,Ct as mode,et as neq,J as not,pt as or,mt as prefix,Dt as product,Lt as quantile,at as regexp_matches,Kt as regrAvgX,zt as regrAvgY,vt as regrCount,Bt as regrIntercept,Qt as regrR2,Ht as regrSXX,kt as regrSXY,Wt as regrSYY,Vt as regrSlope,X as relation,Ut as skewness,Gt as stddev,Yt as stddevPop,er as stringAgg,xt as suffix,It as sum,h as toSQL,b as transform,or as unnest,St as upper,jt as varPop,Ft as variance};
|
|
1
|
+
var x=class{constructor(t,r){t&&(this.table=String(t)),r&&(this.column=r)}get columns(){return this.column?[this.column]:[]}toString(){let{table:t,column:r}=this;if(r){let n=r==="*"?r:`"${r}"`;return(t?`"${t}".`:"")+n}else return`"${t}"`}};function W(e,t){return e instanceof x&&e.column===t}function u(e){return typeof e=="string"?k(e):e}function R(e){return typeof e=="string"?Q(e):e}function Q(e){return new x(e)}function k(e,t){return arguments.length===1?new x(null,e):new x(e,t)}function tt(e){return new x(e,"*")}function f(e){return typeof e=="string"?`"${e}"`:y(e)}function y(e){switch(typeof e){case"boolean":return e?"TRUE":"FALSE";case"string":return`'${e}'`;default:return e==null?"NULL":e instanceof Date?`MAKE_DATE(${e.getUTCFullYear()}, ${e.getUTCMonth()+1}, ${e.getUTCDate()})`:e instanceof RegExp?`'${e.source}'`:String(e)}}var q=e=>typeof e?.addEventListener=="function";function H(e){return e instanceof A}var A=class{constructor(t,r,n){this.expr=t,this.label=n,this.columns=r||[]}toString(){return`${this.expr}`}},C=class extends A{constructor(t,r,n){let s=new Set;for(let o of t)q(o)&&s.add(o);s.forEach(o=>{o.addEventListener("value",()=>this.update())}),super(t,r,n)}toString(){return this.expr.map(t=>q(t)?y(t.value):t).join("")}addEventListener(t,r){let n=this.map||(this.map=new Map);(n.get(t)||(n.set(t,new Set),n.get(t))).add(r)}update(){this.map?.get("value")?.forEach(t=>t(this))}};function F(e,t,r){return new C(e,t,r)}function T(e,t,r){return new A(e,t,r)}function rt(e){return Object.assign(T(`${u(e)} DESC NULLS LAST`,e?.columns,e?.label),{desc:!0})}function G(e,t){return r=>T(e(r),u(r).columns,t)}function et(e,...t){let r=[e[0]],n=new Set,s=t.length;for(let c=0,p=0;c<s;){let h=t[c];q(h)?r[++p]=h:(Array.isArray(h.columns)&&h.columns.forEach(b=>n.add(b)),r[p]+=String(h));let E=e[++c];q(r[p])?r[++p]=E:r[p]+=E}let o=Array.from(n);return r.length>1?F(r,o):T(r[0],o)}var P=class{constructor(t){this.value=t}toString(){return y(this.value)}},nt=e=>new P(e);function L(...e){return e.flat().flatMap(t=>t?.columns||[])}var U=class{constructor(t,r){this.op=t,this.a=u(r)}get columns(){return L(this.a)}visit(t){t(this.op,this)}toString(){let{op:t,a:r}=this;return`(${f(r)} ${t})`}};function Y(e){return t=>new U(e,t)}var st=Y("NOT"),ot=Y("IS NULL"),it=Y("IS NOT NULL"),M=class{constructor(t,r,n){this.op=t,this.a=u(r),this.b=u(n)}get columns(){return L(this.a,this.b)}visit(t){t(this.op,this)}toString(){let{op:t,a:r,b:n}=this;return`(${f(r)} ${t} ${f(n)})`}};function w(e){return(t,r)=>new M(e,t,r)}var ct=w("="),ut=w("<>"),lt=w("<"),at=w(">"),pt=w("<="),ft=w(">="),ht=w("IS DISTINCT FROM"),mt=w("IS NOT DISTINCT FROM"),j=class{constructor(t,r,n){this.op=t,this.expr=u(r),this.value=n?.map(u)}get columns(){return L(this.expr,this.value)}visit(t){t(this.op,this)}toString(){let{op:t,expr:r,value:n}=this;if(!n)return"";let[s,o]=n;return`(${f(r)} ${t} ${f(s)} AND ${f(o)})`}};function K(e){return(t,r)=>new j(e,t,r)}var gt=K("BETWEEN"),xt=K("NOT BETWEEN"),I=class{constructor(t,r){this.op=t,this.value=r.map(u)}get columns(){return L(this.value)}visit(t){t(this.op,this),this.value?.forEach(r=>r.visit(t))}toString(){let{op:t,value:r}=this;return!r||r.length===0?"":r.length===1?f(r[0]):`(${r.map(f).filter(n=>n).join(` ${t} `)})`}};function $t(...e){return new I("AND",e.flat())}function St(...e){return new I("OR",e.flat())}var _=class{constructor(t,r){this.func=t,this.args=(r||[]).map(u)}get column(){return this.columns[0]}get columns(){return this.args.flatMap(t=>t.columns||[])}toString(){let{func:t,args:r}=this;return`${t}(${r.map(f).join(", ")})`}};function $(e){return(...t)=>new _(e,t)}var dt=$("regexp_matches"),wt=$("contains"),yt=$("prefix"),Et=$("suffix"),Nt=$("lower"),Rt=$("upper"),qt=$("length"),At=$("isnan"),Tt=$("isfinite"),bt=$("isinf");var v=class{constructor(t,r){this.aggregate=t,this.args=(r||[]).map(u)}get label(){return this.aggregate.toLowerCase()+(this.args.length?` ${this.columns.join(", ")}`:"")}get column(){return this.columns[0]}get columns(){return this.args.flatMap(t=>t.columns||[])}distinct(){return this.isDistinct=!0,this}where(t){return this.filter=t,this}toString(){let{aggregate:t,args:r,isDistinct:n,filter:s}=this,o=r.length===0?"*":r.map(f).join(", "),c=n?"DISTINCT ":"",p=s?` FILTER (WHERE ${f(s)})`:"",h=t==="COUNT"?"::INTEGER":"";return p&&h?`(${t}(${c}${o})${p})${h}`:`${t}(${c}${o})${p}${h}`}};function i(e){return(...t)=>new v(e,t)}var It=i("COUNT"),Lt=i("AVG"),Dt=i("AVG"),Ot=i("MAD"),Ct=i("MAX"),Ft=i("MIN"),Gt=i("SUM"),Pt=i("PRODUCT"),Ut=i("MEDIAN"),Mt=i("QUANTILE"),jt=i("MODE"),Yt=i("VARIANCE"),_t=i("STDDEV"),vt=i("SKEWNESS"),Xt=i("KURTOSIS"),Bt=i("ENTROPY"),Vt=i("VAR_POP"),Wt=i("STDDEV_POP"),Qt=i("CORR"),kt=i("COVAR_POP"),Ht=i("REGR_INTERCEPT"),Kt=i("REGR_SLOPE"),zt=i("REGR_COUNT"),Jt=i("REGR_R2"),Zt=i("REGR_SYY"),tr=i("REGR_SXX"),rr=i("REGR_SXY"),er=i("REGR_AVGX"),nr=i("REGR_AVGY"),sr=i("FIRST"),or=i("LAST"),ir=i("ARG_MIN"),cr=i("ARG_MAX"),ur=i("STRING_AGG"),lr=i("ARRAY_AGG");var ar=G(e=>`(1000 * (epoch(${e}) - second(${e})) + millisecond(${e}))::DOUBLE`);function pr(e){return{op:"UNNEST",arg:e,toString(){return`UNNEST(${Array.isArray(e)?`[${e.join(", ")}]`:e})`}}}var S=class{static select(...t){return new S().select(...t)}static from(...t){return new S().from(...t)}static with(...t){return new S().with(...t)}static union(...t){return new d("UNION",t.flat())}static unionAll(...t){return new d("UNION ALL",t.flat())}static intersect(...t){return new d("INTERSECT",t.flat())}static except(...t){return new d("EXCEPT",t.flat())}constructor(){this.query={with:[],select:[],from:[],where:[],groupby:[],having:[],window:[],qualify:[],orderby:[]}}clone(){let t=new S;return t.query={...this.query},t}with(...t){let{query:r}=this;if(t.length===0)return r.with;{let n=[],s=(o,c)=>{let p=c.clone();p.cteFor=this,n.push({as:o,query:p})};return t.flat().forEach(o=>{if(o!=null)if(o.as&&o.query)s(o.as,o.query);else for(let c in o)s(c,o[c])}),r.with=r.with.concat(n),this}}select(...t){let{query:r}=this;if(t.length===0)return r.select;{let n=[];return t.flat().forEach(s=>{if(s!=null)if(typeof s=="string")n.push({as:s,expr:u(s)});else if(s instanceof x)n.push({as:s.column,expr:s});else if(Array.isArray(s))n.push({as:s[0],expr:s[1]});else for(let o in s)n.push({as:D(o),expr:u(s[o])})}),r.select=r.select.concat(n),this}}$select(...t){return this.query.select=[],this.select(...t)}distinct(t=!0){return this.query.distinct=!!t,this}from(...t){let{query:r}=this;if(t.length===0)return r.from;{let n=[];return t.flat().forEach(s=>{if(s!=null)if(typeof s=="string")n.push({as:s,from:R(s)});else if(s instanceof x)n.push({as:s.table,from:s});else if(O(s)||H(s))n.push({from:s});else if(Array.isArray(s))n.push({as:D(s[0]),from:R(s[1])});else for(let o in s)n.push({as:D(o),from:R(s[o])})}),r.from=r.from.concat(n),this}}$from(...t){return this.query.from=[],this.from(...t)}sample(t){let{query:r}=this;if(arguments.length===0)return r.sample;{let n=t;return typeof t=="number"&&(n=t>0&&t<1?{perc:100*t}:{rows:Math.round(t)}),r.sample=n,this}}where(...t){let{query:r}=this;return t.length===0?r.where:(r.where=r.where.concat(t.flat().filter(n=>n)),this)}$where(...t){return this.query.where=[],this.where(...t)}groupby(...t){let{query:r}=this;return t.length===0?r.groupby:(r.groupby=r.groupby.concat(t.flat().filter(n=>n).map(u)),this)}having(...t){let{query:r}=this;return t.length===0?r.having:(r.having=r.having.concat(t.flat().filter(n=>n)),this)}window(...t){let{query:r}=this;if(t.length===0)return r.window;{let n=[];return t.flat().forEach(s=>{if(s!=null)for(let o in s)n.push({as:D(o),expr:s[o]})}),r.window=r.window.concat(n),this}}qualify(...t){let{query:r}=this;return t.length===0?r.qualify:(r.qualify=r.qualify.concat(t.flat().filter(n=>n)),this)}orderby(...t){let{query:r}=this;return t.length===0?r.orderby:(r.orderby=r.orderby.concat(t.flat().filter(n=>n).map(u)),this)}limit(t){let{query:r}=this;return arguments.length===0?r.limit:(r.limit=Number.isFinite(t)?t:void 0,this)}offset(t){let{query:r}=this;return arguments.length===0?r.offset:(r.offset=Number.isFinite(t)?t:void 0,this)}get subqueries(){let{query:t,cteFor:r}=this,s=(r?.query||t).with?.reduce((c,{as:p,query:h})=>(c[p]=h,c),{}),o=[];return t.from.forEach(({from:c})=>{if(O(c))o.push(c);else if(s[c.table]){let p=s[c.table];o.push(p)}}),o}toString(){let{select:t,distinct:r,from:n,sample:s,where:o,groupby:c,having:p,window:h,qualify:E,orderby:b,limit:X,offset:B,with:V}=this.query,m=[];if(V.length){let l=V.map(({as:a,query:g})=>`"${a}" AS (${g})`);m.push(`WITH ${l.join(", ")}`)}let z=t.map(({as:l,expr:a})=>W(a,l)&&!a.table?`${a}`:`${a} AS "${l}"`);if(m.push(`SELECT${r?" DISTINCT":""} ${z.join(", ")}`),n.length){let l=n.map(({as:a,from:g})=>{let N=O(g)?`(${g})`:`${g}`;return!a||a===g.table?N:`${N} AS "${a}"`});m.push(`FROM ${l.join(", ")}`)}if(s){let{rows:l,perc:a,method:g,seed:N}=s,J=l?`${l} ROWS`:`${a} PERCENT`,Z=g?` (${g}${N!=null?`, ${N}`:""})`:"";m.push(`USING SAMPLE ${J}${Z}`)}if(o.length){let l=o.map(String).filter(a=>a).join(" AND ");l&&m.push(`WHERE ${l}`)}if(c.length&&m.push(`GROUP BY ${c.join(", ")}`),p.length){let l=p.map(String).filter(a=>a).join(" AND ");l&&m.push(`HAVING ${l}`)}if(h.length){let l=h.map(({as:a,expr:g})=>`"${a}" AS (${g})`);m.push(`WINDOW ${l.join(", ")}`)}if(E.length){let l=E.map(String).filter(a=>a).join(" AND ");l&&m.push(`QUALIFY ${l}`)}return b.length&&m.push(`ORDER BY ${b.join(", ")}`),Number.isFinite(X)&&m.push(`LIMIT ${X}`),Number.isFinite(B)&&m.push(`OFFSET ${B}`),m.join(" ")}},d=class{constructor(t,r){this.op=t,this.queries=r.map(n=>n.clone()),this.query={orderby:[]}}clone(){let t=new d(this.op,this.queries);return t.query={...this.query},t}orderby(...t){let{query:r}=this;return t.length===0?r.orderby:(r.orderby=r.orderby.concat(t.flat().filter(n=>n).map(u)),this)}limit(t){let{query:r}=this;return arguments.length===0?r.limit:(r.limit=Number.isFinite(t)?t:void 0,this)}offset(t){let{query:r}=this;return arguments.length===0?r.offset:(r.offset=Number.isFinite(t)?t:void 0,this)}get subqueries(){let{queries:t,cteFor:r}=this;return r&&t.forEach(n=>n.cteFor=r),t}toString(){let{op:t,queries:r,query:{orderby:n,limit:s,offset:o}}=this,c=[r.join(` ${t} `)];return n.length&&c.push(`ORDER BY ${n.join(", ")}`),Number.isFinite(s)&&c.push(`LIMIT ${s}`),Number.isFinite(o)&&c.push(`OFFSET ${o}`),c.join(" ")}};function O(e){return e instanceof S||e instanceof d}function D(e){return fr(e)?e.slice(1,-1):e}function fr(e){return e[0]==='"'&&e[e.length-1]==='"'}export{S as Query,x as Ref,tt as all,$t as and,cr as argmax,ir as argmin,lr as arrayAgg,u as asColumn,R as asRelation,Lt as avg,k as column,wt as contains,Qt as corr,It as count,kt as covarPop,rt as desc,Bt as entropy,ar as epoch_ms,ct as eq,T as expr,F as exprParams,sr as first,at as gt,ft as gte,gt as isBetween,ht as isDistinct,Tt as isFinite,bt as isInfinite,At as isNaN,xt as isNotBetween,mt as isNotDistinct,it as isNotNull,ot as isNull,O as isQuery,Xt as kurtosis,or as last,qt as length,nt as literal,y as literalToSQL,Nt as lower,lt,pt as lte,Ot as mad,Ct as max,Dt as mean,Ut as median,Ft as min,jt as mode,ut as neq,st as not,St as or,yt as prefix,Pt as product,Mt as quantile,dt as regexp_matches,er as regrAvgX,nr as regrAvgY,zt as regrCount,Ht as regrIntercept,Jt as regrR2,tr as regrSXX,rr as regrSXY,Zt as regrSYY,Kt as regrSlope,Q as relation,vt as skewness,et as sql,_t as stddev,Wt as stddevPop,ur as stringAgg,Et as suffix,Gt as sum,f as toSQL,G as transform,pr as unnest,Rt as upper,Vt as varPop,Yt as variance};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uwdata/mosaic-sql",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "SQL query construction and analysis.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sql",
|
|
@@ -24,5 +24,6 @@
|
|
|
24
24
|
"lint": "eslint src test --ext .js",
|
|
25
25
|
"test": "mocha 'test/**/*-test.js'",
|
|
26
26
|
"prepublishOnly": "npm run test && npm run lint && npm run build"
|
|
27
|
-
}
|
|
27
|
+
},
|
|
28
|
+
"gitHead": "a7967c35349bdf7f00abb113ce1dd9abb233cd62"
|
|
28
29
|
}
|
package/src/Query.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isExpression } from './expression.js';
|
|
1
2
|
import { asColumn, asRelation, isColumnRefFor, Ref } from './ref.js';
|
|
2
3
|
|
|
3
4
|
export class Query {
|
|
@@ -126,7 +127,7 @@ export class Query {
|
|
|
126
127
|
list.push({ as: e, from: asRelation(e) });
|
|
127
128
|
} else if (e instanceof Ref) {
|
|
128
129
|
list.push({ as: e.table, from: e });
|
|
129
|
-
} else if (isQuery(e) ||
|
|
130
|
+
} else if (isQuery(e) || isExpression(e)) {
|
|
130
131
|
list.push({ from: e });
|
|
131
132
|
} else if (Array.isArray(e)) {
|
|
132
133
|
list.push({ as: unquote(e[0]), from: asRelation(e[1]) });
|
package/src/datetime.js
CHANGED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { asColumn } from './ref.js';
|
|
2
|
+
import { literalToSQL } from './to-sql.js';
|
|
3
|
+
|
|
4
|
+
export const isParamLike = e => typeof e?.addEventListener === 'function';
|
|
5
|
+
|
|
6
|
+
export function isExpression(e) {
|
|
7
|
+
return e instanceof SQLExpression;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class SQLExpression {
|
|
11
|
+
constructor(sql, columns, label) {
|
|
12
|
+
this.expr = sql;
|
|
13
|
+
this.label = label;
|
|
14
|
+
this.columns = columns || [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
toString() {
|
|
18
|
+
return `${this.expr}`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class ParameterizedSQLExpression extends SQLExpression{
|
|
23
|
+
constructor(parts, columns, label) {
|
|
24
|
+
const paramSet = new Set;
|
|
25
|
+
for (const part of parts) {
|
|
26
|
+
if (isParamLike(part)) paramSet.add(part);
|
|
27
|
+
}
|
|
28
|
+
paramSet.forEach(param => {
|
|
29
|
+
param.addEventListener('value', () => this.update());
|
|
30
|
+
});
|
|
31
|
+
super(parts, columns, label);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
toString() {
|
|
35
|
+
return this.expr
|
|
36
|
+
.map(p => isParamLike(p) ? literalToSQL(p.value) : p)
|
|
37
|
+
.join('');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
addEventListener(type, callback) {
|
|
41
|
+
const map = this.map || (this.map = new Map());
|
|
42
|
+
const set = map.get(type) || (map.set(type, new Set), map.get(type));
|
|
43
|
+
set.add(callback);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
update() {
|
|
47
|
+
this.map?.get('value')?.forEach(callback => callback(this));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function exprParams(parts, columns, label) {
|
|
52
|
+
return new ParameterizedSQLExpression(parts, columns, label);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function expr(sql, columns, label) {
|
|
56
|
+
return new SQLExpression(sql, columns, label);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function desc(e) {
|
|
60
|
+
return Object.assign(
|
|
61
|
+
expr(`${asColumn(e)} DESC NULLS LAST`, e?.columns, e?.label),
|
|
62
|
+
{ desc: true }
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function transform(func, label) {
|
|
67
|
+
return value => expr(func(value), asColumn(value).columns, label);
|
|
68
|
+
}
|
package/src/index.js
CHANGED
|
@@ -4,11 +4,19 @@ export {
|
|
|
4
4
|
asRelation,
|
|
5
5
|
all,
|
|
6
6
|
column,
|
|
7
|
+
relation
|
|
8
|
+
} from './ref.js';
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
transform,
|
|
7
12
|
desc,
|
|
8
13
|
expr,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
exprParams
|
|
15
|
+
} from './expression.js';
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
sql
|
|
19
|
+
} from './sql-tag.js';
|
|
12
20
|
|
|
13
21
|
export {
|
|
14
22
|
toSQL,
|
package/src/ref.js
CHANGED
|
@@ -44,30 +44,3 @@ export function column(table, column) {
|
|
|
44
44
|
export function all(table) {
|
|
45
45
|
return new Ref(table, '*');
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
export function desc(expr) {
|
|
49
|
-
expr = asColumn(expr);
|
|
50
|
-
return {
|
|
51
|
-
expr,
|
|
52
|
-
desc: true,
|
|
53
|
-
toString: () => `${expr} DESC NULLS LAST`,
|
|
54
|
-
get columns() { return expr.columns?.() || []; }
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function expr(sql, columns, label) {
|
|
59
|
-
return {
|
|
60
|
-
expr: sql,
|
|
61
|
-
label,
|
|
62
|
-
toString: () => `${sql}`,
|
|
63
|
-
get columns() { return columns || []; }
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function transform(func, label) {
|
|
68
|
-
return value => expr(
|
|
69
|
-
func(value),
|
|
70
|
-
asColumn(value).columns,
|
|
71
|
-
label
|
|
72
|
-
)
|
|
73
|
-
}
|
package/src/sql-tag.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { expr, exprParams, isParamLike } from './expression.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tag function for SQL expression strings. Interpolated values
|
|
5
|
+
* may be strings, other SQL expression objects (such as column
|
|
6
|
+
* references), or parameterized values.
|
|
7
|
+
*/
|
|
8
|
+
export function sql(strings, ...exprs) {
|
|
9
|
+
const spans = [strings[0]];
|
|
10
|
+
const colset = new Set;
|
|
11
|
+
const n = exprs.length;
|
|
12
|
+
for (let i = 0, k = 0; i < n;) {
|
|
13
|
+
const e = exprs[i];
|
|
14
|
+
if (isParamLike(e)) {
|
|
15
|
+
spans[++k] = e;
|
|
16
|
+
} else {
|
|
17
|
+
if (Array.isArray(e.columns)) {
|
|
18
|
+
e.columns.forEach(col => colset.add(col));
|
|
19
|
+
}
|
|
20
|
+
spans[k] += String(e);
|
|
21
|
+
}
|
|
22
|
+
const s = strings[++i];
|
|
23
|
+
if (isParamLike(spans[k])) {
|
|
24
|
+
spans[++k] = s;
|
|
25
|
+
} else {
|
|
26
|
+
spans[k] += s;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const columns = Array.from(colset);
|
|
31
|
+
return spans.length > 1
|
|
32
|
+
? exprParams(spans, columns)
|
|
33
|
+
: expr(spans[0], columns);
|
|
34
|
+
}
|