diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a8abd0e534..ef2b59abdcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,6 @@ * Make the remote filesystem cache composable, allow not to evict certain files (regarding idx, mrk, ..), delete old cache version. Now it is possible to configure cache over Azure blob storage disk, over Local disk, over StaticWeb disk, etc. This PR is marked backward incompatible because cache configuration changes and in order for cache to work need to update the config file. Old cache will still be used with new configuration. The server will startup fine with the old cache configuration. Closes https://github.com/ClickHouse/ClickHouse/issues/36140. Closes https://github.com/ClickHouse/ClickHouse/issues/37889. ([Kseniia Sumarokova](https://github.com/kssenii)). [#36171](https://github.com/ClickHouse/ClickHouse/pull/36171)) #### New Feature -* Support SQL standard DELETE FROM syntax on merge tree tables and lightweight delete implementation for merge tree families. [#37893](https://github.com/ClickHouse/ClickHouse/pull/37893) ([Jianmei Zhang](https://github.com/zhangjmruc)) ([Alexander Gololobov](https://github.com/davenger)). Note: this new feature does not make ClickHouse an HTAP DBMS. * Query parameters can be set in interactive mode as `SET param_abc = 'def'` and transferred via the native protocol as settings. [#39906](https://github.com/ClickHouse/ClickHouse/pull/39906) ([Nikita Taranov](https://github.com/nickitat)). * Quota key can be set in the native protocol ([Yakov Olkhovsky](https://github.com/ClickHouse/ClickHouse/pull/39874)). * Added a setting `exact_rows_before_limit` (0/1). When enabled, ClickHouse will provide exact value for `rows_before_limit_at_least` statistic, but with the cost that the data before limit will have to be read completely. This closes [#6613](https://github.com/ClickHouse/ClickHouse/issues/6613). [#25333](https://github.com/ClickHouse/ClickHouse/pull/25333) ([kevin wan](https://github.com/MaxWk)). @@ -33,6 +32,8 @@ * Add formats `PrettyMonoBlock`, `PrettyNoEscapesMonoBlock`, `PrettyCompactNoEscapes`, `PrettyCompactNoEscapesMonoBlock`, `PrettySpaceNoEscapes`, `PrettySpaceMonoBlock`, `PrettySpaceNoEscapesMonoBlock`. [#39646](https://github.com/ClickHouse/ClickHouse/pull/39646) ([Kruglov Pavel](https://github.com/Avogar)). * Add new setting schema_inference_hints that allows to specify structure hints in schema inference for specific columns. Closes [#39569](https://github.com/ClickHouse/ClickHouse/issues/39569). [#40068](https://github.com/ClickHouse/ClickHouse/pull/40068) ([Kruglov Pavel](https://github.com/Avogar)). +#### Experimental Feature +* Support SQL standard DELETE FROM syntax on merge tree tables and lightweight delete implementation for merge tree families. [#37893](https://github.com/ClickHouse/ClickHouse/pull/37893) ([Jianmei Zhang](https://github.com/zhangjmruc)) ([Alexander Gololobov](https://github.com/davenger)). Note: this new feature does not make ClickHouse an HTAP DBMS. #### Performance Improvement * Improved memory usage during memory efficient merging of aggregation results. [#39429](https://github.com/ClickHouse/ClickHouse/pull/39429) ([Nikita Taranov](https://github.com/nickitat)). diff --git a/docs/en/operations/named-collections.md b/docs/en/operations/named-collections.md index 7623f7b7203..3dace93fa4b 100644 --- a/docs/en/operations/named-collections.md +++ b/docs/en/operations/named-collections.md @@ -1,6 +1,6 @@ --- sidebar_position: 69 -sidebar_label: "Named connections" +sidebar_label: "Named collections" --- # Storing details for connecting to external sources in configuration files @@ -12,7 +12,7 @@ from users with only SQL access. Parameters can be set in XML `CSV` and overridden in SQL `, format = 'TSV'`. The parameters in SQL can be overridden using format `key` = `value`: `compression_method = 'gzip'`. -Named connections are stored in the `config.xml` file of the ClickHouse server in the `` section and are applied when ClickHouse starts. +Named collections are stored in the `config.xml` file of the ClickHouse server in the `` section and are applied when ClickHouse starts. Example of configuration: ```xml @@ -24,7 +24,7 @@ $ cat /etc/clickhouse-server/config.d/named_collections.xml ``` -## Named connections for accessing S3. +## Named collections for accessing S3. The description of parameters see [s3 Table Function](../sql-reference/table-functions/s3.md). @@ -42,7 +42,7 @@ Example of configuration: ``` -### Example of using named connections with the s3 function +### Example of using named collections with the s3 function ```sql INSERT INTO FUNCTION s3(s3_mydata, filename = 'test_file.tsv.gz', @@ -58,7 +58,7 @@ FROM s3(s3_mydata, filename = 'test_file.tsv.gz') 1 rows in set. Elapsed: 0.279 sec. Processed 10.00 thousand rows, 90.00 KB (35.78 thousand rows/s., 322.02 KB/s.) ``` -### Example of using named connections with an S3 table +### Example of using named collections with an S3 table ```sql CREATE TABLE s3_engine_table (number Int64) @@ -73,7 +73,7 @@ SELECT * FROM s3_engine_table LIMIT 3; └────────┘ ``` -## Named connections for accessing MySQL database +## Named collections for accessing MySQL database The description of parameters see [mysql](../sql-reference/table-functions/mysql.md). @@ -95,7 +95,7 @@ Example of configuration: ``` -### Example of using named connections with the mysql function +### Example of using named collections with the mysql function ```sql SELECT count() FROM mysql(mymysql, table = 'test'); @@ -105,7 +105,7 @@ SELECT count() FROM mysql(mymysql, table = 'test'); └─────────┘ ``` -### Example of using named connections with an MySQL table +### Example of using named collections with an MySQL table ```sql CREATE TABLE mytable(A Int64) ENGINE = MySQL(mymysql, table = 'test', connection_pool_size=3, replace_query=0); @@ -116,7 +116,7 @@ SELECT count() FROM mytable; └─────────┘ ``` -### Example of using named connections with database with engine MySQL +### Example of using named collections with database with engine MySQL ```sql CREATE DATABASE mydatabase ENGINE = MySQL(mymysql); @@ -129,7 +129,7 @@ SHOW TABLES FROM mydatabase; └────────┘ ``` -### Example of using named connections with an external dictionary with source MySQL +### Example of using named collections with an external dictionary with source MySQL ```sql CREATE DICTIONARY dict (A Int64, B String) @@ -145,7 +145,7 @@ SELECT dictGet('dict', 'B', 2); └─────────────────────────┘ ``` -## Named connections for accessing PostgreSQL database +## Named collections for accessing PostgreSQL database The description of parameters see [postgresql](../sql-reference/table-functions/postgresql.md). @@ -166,7 +166,7 @@ Example of configuration: ``` -### Example of using named connections with the postgresql function +### Example of using named collections with the postgresql function ```sql SELECT * FROM postgresql(mypg, table = 'test'); @@ -186,8 +186,7 @@ SELECT * FROM postgresql(mypg, table = 'test', schema = 'public'); └───┘ ``` - -### Example of using named connections with database with engine PostgreSQL +### Example of using named collections with database with engine PostgreSQL ```sql CREATE TABLE mypgtable (a Int64) ENGINE = PostgreSQL(mypg, table = 'test', schema = 'public'); @@ -201,7 +200,7 @@ SELECT * FROM mypgtable; └───┘ ``` -### Example of using named connections with database with engine PostgreSQL +### Example of using named collections with database with engine PostgreSQL ```sql CREATE DATABASE mydatabase ENGINE = PostgreSQL(mypg); @@ -213,7 +212,7 @@ SHOW TABLES FROM mydatabase └──────┘ ``` -### Example of using named connections with an external dictionary with source POSTGRESQL +### Example of using named collections with an external dictionary with source POSTGRESQL ```sql CREATE DICTIONARY dict (a Int64, b String) @@ -228,3 +227,59 @@ SELECT dictGet('dict', 'b', 2); │ two │ └─────────────────────────┘ ``` + +## Named collections for accessing remote ClickHouse database + +The description of parameters see [remote](../sql-reference/table-functions/remote.md/#parameters). + +Example of configuration: + +```xml + + + + localhost + 9000 + system + foo + secret + + + +``` + +### Example of using named collections with the `remote`/`remoteSecure` functions + +```sql +SELECT * FROM remote(remote1, table = one); +┌─dummy─┐ +│ 0 │ +└───────┘ + +SELECT * FROM remote(remote1, database = merge(system, '^one')); +┌─dummy─┐ +│ 0 │ +└───────┘ + +INSERT INTO FUNCTION remote(remote1, database = default, table = test) VALUES (1,'a'); + +SELECT * FROM remote(remote1, database = default, table = test); +┌─a─┬─b─┐ +│ 1 │ a │ +└───┴───┘ +``` + +### Example of using named collections with an external dictionary with source ClickHouse + +```sql +CREATE DICTIONARY dict(a Int64, b String) +PRIMARY KEY a +SOURCE(CLICKHOUSE(NAME remote1 TABLE test DB default)) +LIFETIME(MIN 1 MAX 2) +LAYOUT(HASHED()); + +SELECT dictGet('dict', 'b', 1); +┌─dictGet('dict', 'b', 1)─┐ +│ a │ +└─────────────────────────┘ +``` diff --git a/programs/server/CMakeLists.txt b/programs/server/CMakeLists.txt index 643fd2f0ec4..2cfa748d585 100644 --- a/programs/server/CMakeLists.txt +++ b/programs/server/CMakeLists.txt @@ -34,6 +34,6 @@ install(FILES config.xml users.xml DESTINATION "${CLICKHOUSE_ETC_DIR}/clickhouse clickhouse_embed_binaries( TARGET clickhouse_server_configs - RESOURCES config.xml users.xml embedded.xml play.html + RESOURCES config.xml users.xml embedded.xml play.html dashboard.html js/uplot.js ) add_dependencies(clickhouse-server-lib clickhouse_server_configs) diff --git a/programs/server/dashboard.html b/programs/server/dashboard.html new file mode 100644 index 00000000000..e63a277497a --- /dev/null +++ b/programs/server/dashboard.html @@ -0,0 +1,905 @@ + + + + + ClickHouse Dashboard + + + + + +
+
+
+ + + +
+
+ + 🌚🌞 +
+
+
+
+
+ + + diff --git a/programs/server/js/uplot.js b/programs/server/js/uplot.js new file mode 100644 index 00000000000..657479be7e1 --- /dev/null +++ b/programs/server/js/uplot.js @@ -0,0 +1,2 @@ +/*! https://github.com/leeoniya/uPlot (v1.6.21) */ +var uPlot=function(){"use strict";const e="u-off",t="u-label",l="width",n="height",i="top",o="bottom",s="left",r="right",u="#000",a="mousemove",f="mousedown",c="mouseup",h="mouseenter",d="mouseleave",p="dblclick",m="change",g="dppxchange",x="undefined"!=typeof window,w=x?document:null,_=x?window:null,k=x?navigator:null;let b,v;function y(e,t){if(null!=t){let l=e.classList;!l.contains(t)&&l.add(t)}}function M(e,t){let l=e.classList;l.contains(t)&&l.remove(t)}function S(e,t,l){e.style[t]=l+"px"}function E(e,t,l,n){let i=w.createElement(e);return null!=t&&y(i,t),null!=l&&l.insertBefore(i,n),i}function D(e,t){return E("div",e,t)}const z=new WeakMap;function T(t,l,n,i,o){let s="translate("+l+"px,"+n+"px)";s!=z.get(t)&&(t.style.transform=s,z.set(t,s),0>l||0>n||l>i||n>o?y(t,e):M(t,e))}const P=new WeakMap;function A(e,t,l){let n=t+l;n!=P.get(e)&&(P.set(e,n),e.style.background=t,e.style.borderColor=l)}const W=new WeakMap;function Y(e,t,l,n){let i=t+""+l;i!=W.get(e)&&(W.set(e,i),e.style.height=l+"px",e.style.width=t+"px",e.style.marginLeft=n?-t/2+"px":0,e.style.marginTop=n?-l/2+"px":0)}const C={passive:!0},F={...C,capture:!0};function H(e,t,l,n){t.addEventListener(e,l,n?F:C)}function R(e,t,l,n){t.removeEventListener(e,l,n?F:C)}function G(e,t,l,n){let i;l=l||0;let o=2147483647>=(n=n||t.length-1);for(;n-l>1;)i=o?l+n>>1:te((l+n)/2),e>t[i]?l=i:n=i;return e-t[l]>t[n]-e?n:l}function L(e,t,l,n){for(let i=1==n?t:l;i>=t&&l>=i;i+=n)if(null!=e[i])return i;return-1}x&&function e(){let t=devicePixelRatio;b!=t&&(b=t,v&&R(m,v,e),v=matchMedia(`(min-resolution: ${b-.001}dppx) and (max-resolution: ${b+.001}dppx)`),H(m,v,e),_.dispatchEvent(new CustomEvent(g)))}();const I=[0,0];function O(e,t,l,n){return I[0]=0>l?ye(e,-l):e,I[1]=0>n?ye(t,-n):t,I}function N(e,t,l,n){let i,o,s,r=re(e),u=10==l?ue:ae;return e==t&&(-1==r?(e*=l,t/=l):(e/=l,t*=l)),n?(i=te(u(e)),o=ne(u(t)),s=O(se(l,i),se(l,o),i,o),e=s[0],t=s[1]):(i=te(u(ee(e))),o=te(u(ee(t))),s=O(se(l,i),se(l,o),i,o),e=ve(e,s[0]),t=be(t,s[1])),[e,t]}function j(e,t,l,n){let i=N(e,t,l,n);return 0==e&&(i[0]=0),0==t&&(i[1]=0),i}const B={mode:3,pad:.1},U={pad:0,soft:null,mode:0},V={min:U,max:U};function J(e,t,l,n){return We(l)?K(e,t,l):(U.pad=l,U.soft=n?0:null,U.mode=n?3:0,K(e,t,V))}function q(e,t){return null==e?t:e}function K(e,t,l){let n=l.min,i=l.max,o=q(n.pad,0),s=q(i.pad,0),r=q(n.hard,-ce),u=q(i.hard,ce),a=q(n.soft,ce),f=q(i.soft,-ce),c=q(n.mode,0),h=q(i.mode,0),d=t-e;1e-9>d&&(d=0,0!=e&&0!=t||(d=1e-9,2==c&&a!=ce&&(o=0),2==h&&f!=-ce&&(s=0)));let p=d||ee(t)||1e3,m=ue(p),g=se(10,te(m)),x=ye(ve(e-p*(0==d?0==e?.1:1:o),g/10),9),w=a>e||1!=c&&(3!=c||x>a)&&(2!=c||a>x)?ce:a,_=oe(r,w>x&&e>=w?w:ie(w,x)),k=ye(be(t+p*(0==d?0==t?.1:1:s),g/10),9),b=t>f||1!=h&&(3!=h||f>k)&&(2!=h||k>f)?-ce:f,v=ie(u,k>b&&b>=t?b:oe(b,k));return _==v&&0==_&&(v=100),[_,v]}const Z=new Intl.NumberFormat(x?k.language:"en-US"),$=e=>Z.format(e),X=Math,Q=X.PI,ee=X.abs,te=X.floor,le=X.round,ne=X.ceil,ie=X.min,oe=X.max,se=X.pow,re=X.sign,ue=X.log10,ae=X.log2,fe=(e,t=1)=>X.asinh(e/t),ce=1/0;function he(e){return 1+(0|ue((e^e>>31)-(e>>31)))}function de(e,t){return le(e/t)*t}function pe(e,t,l){return ie(oe(e,t),l)}function me(e){return"function"==typeof e?e:()=>e}const ge=e=>e,xe=(e,t)=>t,we=()=>null,_e=()=>!0,ke=(e,t)=>e==t;function be(e,t){return ne(e/t)*t}function ve(e,t){return te(e/t)*t}function ye(e,t){return le(e*(t=10**t))/t}const Me=new Map;function Se(e){return((""+e).split(".")[1]||"").length}function Ee(e,t,l,n){let i=[],o=n.map(Se);for(let s=t;l>s;s++){let t=ee(s),l=ye(se(e,s),t);for(let e=0;n.length>e;e++){let r=n[e]*l,u=(0>r||0>s?t:0)+(o[e]>s?o[e]:0),a=ye(r,u);i.push(a),Me.set(a,u)}}return i}const De={},ze=[],Te=[null,null],Pe=Array.isArray;function Ae(e){return"string"==typeof e}function We(e){let t=!1;if(null!=e){let l=e.constructor;t=null==l||l==Object}return t}function Ye(e){return null!=e&&"object"==typeof e}function Ce(e,t=We){let l;if(Pe(e)){let n=e.find((e=>null!=e));if(Pe(n)||t(n)){l=Array(e.length);for(let n=0;e.length>n;n++)l[n]=Ce(e[n],t)}else l=e.slice()}else if(t(e)){l={};for(let n in e)l[n]=Ce(e[n],t)}else l=e;return l}function Fe(e){let t=arguments;for(let l=1;t.length>l;l++){let n=t[l];for(let t in n)We(e[t])?Fe(e[t],Ce(n[t])):e[t]=Ce(n[t])}return e}function He(e,t,l){for(let n,i=0,o=-1;t.length>i;i++){let s=t[i];if(s>o){for(n=s-1;n>=0&&null==e[n];)e[n--]=null;for(n=s+1;l>n&&null==e[n];)e[o=n++]=null}}}const Re="undefined"==typeof queueMicrotask?e=>Promise.resolve().then(e):queueMicrotask,Ge=["January","February","March","April","May","June","July","August","September","October","November","December"],Le=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];function Ie(e){return e.slice(0,3)}const Oe=Le.map(Ie),Ne=Ge.map(Ie),je={MMMM:Ge,MMM:Ne,WWWW:Le,WWW:Oe};function Be(e){return(10>e?"0":"")+e}const Ue={YYYY:e=>e.getFullYear(),YY:e=>(e.getFullYear()+"").slice(2),MMMM:(e,t)=>t.MMMM[e.getMonth()],MMM:(e,t)=>t.MMM[e.getMonth()],MM:e=>Be(e.getMonth()+1),M:e=>e.getMonth()+1,DD:e=>Be(e.getDate()),D:e=>e.getDate(),WWWW:(e,t)=>t.WWWW[e.getDay()],WWW:(e,t)=>t.WWW[e.getDay()],HH:e=>Be(e.getHours()),H:e=>e.getHours(),h:e=>{let t=e.getHours();return 0==t?12:t>12?t-12:t},AA:e=>12>e.getHours()?"AM":"PM",aa:e=>12>e.getHours()?"am":"pm",a:e=>12>e.getHours()?"a":"p",mm:e=>Be(e.getMinutes()),m:e=>e.getMinutes(),ss:e=>Be(e.getSeconds()),s:e=>e.getSeconds(),fff:e=>function(e){return(10>e?"00":100>e?"0":"")+e}(e.getMilliseconds())};function Ve(e,t){t=t||je;let l,n=[],i=/\{([a-z]+)\}|[^{]+/gi;for(;l=i.exec(e);)n.push("{"==l[0][0]?Ue[l[1]]:l[0]);return e=>{let l="";for(let i=0;n.length>i;i++)l+="string"==typeof n[i]?n[i]:n[i](e,t);return l}}const Je=(new Intl.DateTimeFormat).resolvedOptions().timeZone,qe=e=>e%1==0,Ke=[1,2,2.5,5],Ze=Ee(10,-16,0,Ke),$e=Ee(10,0,16,Ke),Xe=$e.filter(qe),Qe=Ze.concat($e),et="{YYYY}",tt="\n"+et,lt="{M}/{D}",nt="\n"+lt,it=nt+"/{YY}",ot="{aa}",st="{h}:{mm}"+ot,rt="\n"+st,ut=":{ss}",at=null;function ft(e){let t=1e3*e,l=60*t,n=60*l,i=24*n,o=30*i,s=365*i;return[(1==e?Ee(10,0,3,Ke).filter(qe):Ee(10,-3,0,Ke)).concat([t,5*t,10*t,15*t,30*t,l,5*l,10*l,15*l,30*l,n,2*n,3*n,4*n,6*n,8*n,12*n,i,2*i,3*i,4*i,5*i,6*i,7*i,8*i,9*i,10*i,15*i,o,2*o,3*o,4*o,6*o,s,2*s,5*s,10*s,25*s,50*s,100*s]),[[s,et,at,at,at,at,at,at,1],[28*i,"{MMM}",tt,at,at,at,at,at,1],[i,lt,tt,at,at,at,at,at,1],[n,"{h}"+ot,it,at,nt,at,at,at,1],[l,st,it,at,nt,at,at,at,1],[t,ut,it+" "+st,at,nt+" "+st,at,rt,at,1],[e,ut+".{fff}",it+" "+st,at,nt+" "+st,at,rt,at,1]],function(t){return(r,u,a,f,c,h)=>{let d=[],p=c>=s,m=c>=o&&s>c,g=t(a),x=ye(g*e,3),w=_t(g.getFullYear(),p?0:g.getMonth(),m||p?1:g.getDate()),_=ye(w*e,3);if(m||p){let l=m?c/o:0,n=p?c/s:0,i=x==_?x:ye(_t(w.getFullYear()+n,w.getMonth()+l,1)*e,3),r=new Date(le(i/e)),u=r.getFullYear(),a=r.getMonth();for(let o=0;f>=i;o++){let s=_t(u+n*o,a+l*o,1),r=s-t(ye(s*e,3));i=ye((+s+r)*e,3),i>f||d.push(i)}}else{let o=i>c?c:i,s=_+(te(a)-te(x))+be(x-_,o);d.push(s);let p=t(s),m=p.getHours()+p.getMinutes()/l+p.getSeconds()/n,g=c/n,w=h/r.axes[u]._space;for(;s=ye(s+c,1==e?0:3),f>=s;)if(g>1){let e=te(ye(m+g,6))%24,l=t(s).getHours()-e;l>1&&(l=-1),s-=l*n,m=(m+g)%24,.7>ye((s-d[d.length-1])/c,3)*w||d.push(s)}else d.push(s)}return d}}]}const[ct,ht,dt]=ft(1),[pt,mt,gt]=ft(.001);function xt(e,t){return e.map((e=>e.map(((l,n)=>0==n||8==n||null==l?l:t(1==n||0==e[8]?l:e[1]+l)))))}function wt(e,t){return(l,n,i,o,s)=>{let r,u,a,f,c,h,d=t.find((e=>s>=e[0]))||t[t.length-1];return n.map((t=>{let l=e(t),n=l.getFullYear(),i=l.getMonth(),o=l.getDate(),s=l.getHours(),p=l.getMinutes(),m=l.getSeconds(),g=n!=r&&d[2]||i!=u&&d[3]||o!=a&&d[4]||s!=f&&d[5]||p!=c&&d[6]||m!=h&&d[7]||d[1];return r=n,u=i,a=o,f=s,c=p,h=m,g(l)}))}}function _t(e,t,l){return new Date(e,t,l)}function kt(e,t){return t(e)}function bt(e,t){return(l,n)=>t(e(n))}Ee(2,-53,53,[1]);const vt={show:!0,live:!0,isolate:!1,markers:{show:!0,width:2,stroke:function(e,t){let l=e.series[t];return l.width?l.stroke(e,t):l.points.width?l.points.stroke(e,t):null},fill:function(e,t){return e.series[t].fill(e,t)},dash:"solid"},idx:null,idxs:null,values:[]},yt=[0,0];function Mt(e,t,l){return e=>{0==e.button&&l(e)}}function St(e,t,l){return l}const Et={show:!0,x:!0,y:!0,lock:!1,move:function(e,t,l){return yt[0]=t,yt[1]=l,yt},points:{show:function(e,t){let i=e.cursor.points,o=D(),s=i.size(e,t);S(o,l,s),S(o,n,s);let r=s/-2;S(o,"marginLeft",r),S(o,"marginTop",r);let u=i.width(e,t,s);return u&&S(o,"borderWidth",u),o},size:function(e,t){return Vt(e.series[t].points.width,1)},width:0,stroke:function(e,t){let l=e.series[t].points;return l._stroke||l._fill},fill:function(e,t){let l=e.series[t].points;return l._fill||l._stroke}},bind:{mousedown:Mt,mouseup:Mt,click:Mt,dblclick:Mt,mousemove:St,mouseleave:St,mouseenter:St},drag:{setScale:!0,x:!0,y:!1,dist:0,uni:null,_x:!1,_y:!1},focus:{prox:-1},left:-10,top:-10,idx:null,dataIdx:function(e,t,l){return l},idxs:null},Dt={show:!0,stroke:"rgba(0,0,0,0.07)",width:2},zt=Fe({},Dt,{filter:xe}),Tt=Fe({},zt,{size:10}),Pt=Fe({},Dt,{show:!1}),At='12px system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',Wt="bold "+At,Yt={show:!0,scale:"x",stroke:u,space:50,gap:5,size:50,labelGap:0,labelSize:30,labelFont:Wt,side:2,grid:zt,ticks:Tt,border:Pt,font:At,rotate:0},Ct={show:!0,scale:"x",auto:!1,sorted:1,min:ce,max:-ce,idxs:[]};function Ft(e,t){return t.map((e=>null==e?"":$(e)))}function Ht(e,t,l,n,i,o,s){let r=[],u=Me.get(i)||0;for(let e=l=s?l:ye(be(l,i),u);n>=e;e=ye(e+i,u))r.push(Object.is(e,-0)?0:e);return r}function Rt(e,t,l,n,i){const o=[],s=e.scales[e.axes[t].scale].log,r=te((10==s?ue:ae)(l));i=se(s,r),0>r&&(i=ye(i,-r));let u=l;do{o.push(u),u=ye(u+i,Me.get(i)),i*s>u||(i=u)}while(n>=u);return o}function Gt(e,t,l,n,i){let o=e.scales[e.axes[t].scale].asinh,s=n>o?Rt(e,t,oe(o,l),n,i):[o],r=0>n||l>0?[]:[0];return(-o>l?Rt(e,t,oe(o,-n),-l,i):[o]).reverse().map((e=>-e)).concat(r,s)}const Lt=/./,It=/[12357]/,Ot=/[125]/,Nt=/1/;function jt(e,t,l){let n=e.axes[l],i=n.scale,o=e.scales[i];if(3==o.distr&&2==o.log)return t;let s=e.valToPos,r=n._space,u=s(10,i),a=s(9,i)-u4==o.distr&&0==e||a.test(e)?e:null))}function Bt(e,t){return null==t?"":$(t)}const Ut={show:!0,scale:"y",stroke:u,space:30,gap:5,size:50,labelGap:0,labelSize:30,labelFont:Wt,side:3,grid:zt,ticks:Tt,border:Pt,font:At,rotate:0};function Vt(e,t){return ye((3+2*(e||1))*t,3)}const Jt={scale:null,auto:!0,sorted:0,min:ce,max:-ce},qt={show:!0,auto:!0,sorted:0,alpha:1,facets:[Fe({},Jt,{scale:"x"}),Fe({},Jt,{scale:"y"})]},Kt={scale:"y",auto:!0,sorted:0,show:!0,spanGaps:!1,gaps:(e,t,l,n,i)=>i,alpha:1,points:{show:function(e,t){let{scale:l,idxs:n}=e.series[0],i=e._data[0],o=e.valToPos(i[n[0]],l,!0),s=e.valToPos(i[n[1]],l,!0);return ee(s-o)/(e.series[t].points.space*b)>=n[1]-n[0]},filter:null},values:null,min:ce,max:-ce,idxs:[],path:null,clip:null};function Zt(e,t,l){return l/10}const $t={time:!0,auto:!0,distr:1,log:10,asinh:1,min:null,max:null,dir:1,ori:0},Xt=Fe({},$t,{time:!1,ori:1}),Qt={};function el(e){let t=Qt[e];return t||(t={key:e,plots:[],sub(e){t.plots.push(e)},unsub(e){t.plots=t.plots.filter((t=>t!=e))},pub(e,l,n,i,o,s,r){for(let u=0;t.plots.length>u;u++)t.plots[u]!=l&&t.plots[u].pub(e,l,n,i,o,s,r)}},null!=e&&(Qt[e]=t)),t}function tl(e,t,l){const n=e.series[t],i=e.scales,o=e.bbox;let s=e._data[0],r=e._data[t],u=2==e.mode?i[n.facets[0].scale]:i[e.series[0].scale],a=2==e.mode?i[n.facets[1].scale]:i[n.scale],f=o.left,c=o.top,h=o.width,d=o.height,p=e.valToPosH,m=e.valToPosV;return 0==u.ori?l(n,s,r,u,a,p,m,f,c,h,d,al,cl,dl,ml,xl):l(n,s,r,u,a,m,p,c,f,d,h,fl,hl,pl,gl,wl)}function ll(e,t){let l=0,n=0,i=q(e.bands,ze);for(let e=0;i.length>e;e++){let o=i[e];o.series[0]==t?l=o.dir:o.series[1]==t&&(n|=1==o.dir?1:2)}return[l,1==n?-1:2==n?1:3==n?2:0]}function nl(e,t,l,n,i){let o=e.scales[e.series[t].scale];return-1==i?o.min:1==i?o.max:3==o.distr?1==o.dir?o.min:o.max:0}function il(e,t,l,n,i,o){return tl(e,t,((e,t,s,r,u,a,f,c,h,d,p)=>{let m=e.pxRound;const g=0==r.ori?cl:hl;let x,w;1==r.dir*(0==r.ori?1:-1)?(x=l,w=n):(x=n,w=l);let _=m(a(t[x],r,d,c)),k=m(f(s[x],u,p,h)),b=m(a(t[w],r,d,c)),v=m(f(1==o?u.max:u.min,u,p,h)),y=new Path2D(i);return g(y,b,v),g(y,_,v),g(y,_,k),y}))}function ol(e,t,l,n,i,o){let s=null;if(e.length>0){s=new Path2D;const r=0==t?dl:pl;let u=l;for(let t=0;e.length>t;t++){let l=e[t];if(l[1]>l[0]){let e=l[0]-u;e>0&&r(s,u,n,e,n+o),u=l[1]}}let a=l+i-u;a>0&&r(s,u,n,a,n+o)}return s}function sl(e,t,l,n,i,o,s){let r=[];for(let u=1==i?l:n;u>=l&&n>=u;u+=i)if(null===t[u]){let a=u,f=u;if(1==i)for(;++u<=n&&null===t[u];)f=u;else for(;--u>=l&&null===t[u];)f=u;let c=o(e[a]),h=f==a?c:o(e[f]);c=s>0?c:o(e[a-i]),h=0>s?h:o(e[f+i]),c>h||r.push([c,h])}return r}function rl(e){return 0==e?ge:1==e?le:t=>de(t,e)}function ul(e){let t=0==e?al:fl,l=0==e?(e,t,l,n,i,o)=>{e.arcTo(t,l,n,i,o)}:(e,t,l,n,i,o)=>{e.arcTo(l,t,i,n,o)},n=0==e?(e,t,l,n,i)=>{e.rect(t,l,n,i)}:(e,t,l,n,i)=>{e.rect(l,t,i,n)};return(e,i,o,s,r,u=0)=>{0==u?n(e,i,o,s,r):(u=ie(u,s/2,r/2),t(e,i+u,o),l(e,i+s,o,i+s,o+r,u),l(e,i+s,o+r,i,o+r,u),l(e,i,o+r,i,o,u),l(e,i,o,i+s,o,u),e.closePath())}}const al=(e,t,l)=>{e.moveTo(t,l)},fl=(e,t,l)=>{e.moveTo(l,t)},cl=(e,t,l)=>{e.lineTo(t,l)},hl=(e,t,l)=>{e.lineTo(l,t)},dl=ul(0),pl=ul(1),ml=(e,t,l,n,i,o)=>{e.arc(t,l,n,i,o)},gl=(e,t,l,n,i,o)=>{e.arc(l,t,n,i,o)},xl=(e,t,l,n,i,o,s)=>{e.bezierCurveTo(t,l,n,i,o,s)},wl=(e,t,l,n,i,o,s)=>{e.bezierCurveTo(l,t,i,n,s,o)};function _l(){return(e,t,l,n,i)=>tl(e,t,((t,o,s,r,u,a,f,c,h,d,p)=>{let m,g,{pxRound:x,points:w}=t;0==r.ori?(m=al,g=ml):(m=fl,g=gl);const _=ye(w.width*b,3);let k=(w.size-w.width)/2*b,v=ye(2*k,3),y=new Path2D,M=new Path2D,{left:S,top:E,width:D,height:z}=e.bbox;dl(M,S-v,E-v,D+2*v,z+2*v);const T=e=>{if(null!=s[e]){let t=x(a(o[e],r,d,c)),l=x(f(s[e],u,p,h));m(y,t+k,l),g(y,t,l,k,0,2*Q)}};if(i)i.forEach(T);else for(let e=l;n>=e;e++)T(e);return{stroke:_>0?y:null,fill:y,clip:M,flags:3}}))}function kl(e){return(t,l,n,i,o,s)=>{n!=i&&(o!=n&&s!=n&&e(t,l,n),o!=i&&s!=i&&e(t,l,i),e(t,l,s))}}const bl=kl(cl),vl=kl(hl);function yl(e){const t=q(e?.alignGaps,0);return(e,l,n,i)=>tl(e,l,((o,s,r,u,a,f,c,h,d,p,m)=>{let g,x,w=o.pxRound,_=e=>w(f(e,u,p,h)),k=e=>w(c(e,a,m,d));0==u.ori?(g=cl,x=bl):(g=hl,x=vl);const b=u.dir*(0==u.ori?1:-1),v={stroke:new Path2D,fill:null,clip:null,band:null,gaps:null,flags:1},y=v.stroke;let M,S,E,D=ce,z=-ce,T=_(s[1==b?n:i]),P=L(r,n,i,1*b),A=L(r,n,i,-1*b),W=_(s[P]),Y=_(s[A]);for(let e=1==b?n:i;e>=n&&i>=e;e+=b){let t=_(s[e]);t==T?null!=r[e]&&(S=k(r[e]),D==ce&&(g(y,t,S),M=S),D=ie(S,D),z=oe(S,z)):(D!=ce&&(x(y,T,D,z,M,S),E=T),null!=r[e]?(S=k(r[e]),g(y,t,S),D=z=M=S):(D=ce,z=-ce),T=t)}D!=ce&&D!=z&&E!=T&&x(y,T,D,z,M,S);let[C,F]=ll(e,l);if(null!=o.fill||0!=C){let t=v.fill=new Path2D(y),n=k(o.fillTo(e,l,o.min,o.max,C));g(t,Y,n),g(t,W,n)}if(!o.spanGaps){let a=[];a.push(...sl(s,r,n,i,b,_,t)),v.gaps=a=o.gaps(e,l,n,i,a),v.clip=ol(a,u.ori,h,d,p,m)}return 0!=F&&(v.band=2==F?[il(e,l,n,i,y,-1),il(e,l,n,i,y,1)]:il(e,l,n,i,y,F)),v}))}function Ml(e,t,l,n,i){const o=e.length;if(2>o)return null;const s=new Path2D;if(l(s,e[0],t[0]),2==o)n(s,e[1],t[1]);else{let l=Array(o),n=Array(o-1),r=Array(o-1),u=Array(o-1);for(let l=0;o-1>l;l++)r[l]=t[l+1]-t[l],u[l]=e[l+1]-e[l],n[l]=r[l]/u[l];l[0]=n[0];for(let e=1;o-1>e;e++)0===n[e]||0===n[e-1]||n[e-1]>0!=n[e]>0?l[e]=0:(l[e]=3*(u[e-1]+u[e])/((2*u[e]+u[e-1])/n[e-1]+(u[e]+2*u[e-1])/n[e]),isFinite(l[e])||(l[e]=0));l[o-1]=n[o-2];for(let n=0;o-1>n;n++)i(s,e[n]+u[n]/3,t[n]+l[n]*u[n]/3,e[n+1]-u[n]/3,t[n+1]-l[n+1]*u[n]/3,e[n+1],t[n+1])}return s}const Sl=new Set;function El(){Sl.forEach((e=>{e.syncRect(!0)}))}x&&(H("resize",_,El),H("scroll",_,El,!0),H(g,_,(()=>{Ol.pxRatio=b})));const Dl=yl(),zl=_l();function Tl(e,t,l,n){return(n?[e[0],e[1]].concat(e.slice(2)):[e[0]].concat(e.slice(1))).map(((e,n)=>Pl(e,n,t,l)))}function Pl(e,t,l,n){return Fe({},0==t?l:n,e)}function Al(e,t,l){return null==t?Te:[t,l]}const Wl=Al;function Yl(e,t,l){return null==t?Te:J(t,l,.1,!0)}function Cl(e,t,l,n){return null==t?Te:N(t,l,e.scales[n].log,!1)}const Fl=Cl;function Hl(e,t,l,n){return null==t?Te:j(t,l,e.scales[n].log,!1)}const Rl=Hl;function Gl(e,t,l,n,i){let o=oe(he(e),he(t)),s=t-e,r=G(i/n*s,l);do{let e=l[r],t=n*e/s;if(t>=i&&17>=o+(5>e?Me.get(e):0))return[e,t]}while(++r(t=le((l=+n)*b))+"px")),t,l]}function Il(e){e.show&&[e.font,e.labelFont].forEach((e=>{let t=ye(e[2]*b,1);e[0]=e[0].replace(/[0-9.]+px/,t+"px"),e[1]=t}))}function Ol(u,m,x){const k={mode:q(u.mode,1)},v=k.mode;function z(e,t){return((3==t.distr?ue(e>0?e:t.clamp(k,e,t.min,t.max,t.key)):4==t.distr?fe(e,t.asinh):e)-t._min)/(t._max-t._min)}function P(e,t,l,n){let i=z(e,t);return n+l*(-1==t.dir?1-i:i)}function W(e,t,l,n){let i=z(e,t);return n+l*(-1==t.dir?i:1-i)}function C(e,t,l,n){return 0==t.ori?P(e,t,l,n):W(e,t,l,n)}k.valToPosH=P,k.valToPosV=W;let F=!1;k.status=0;const L=k.root=D("uplot");null!=u.id&&(L.id=u.id),y(L,u.class),u.title&&(D("u-title",L).textContent=u.title);const I=E("canvas"),O=k.ctx=I.getContext("2d"),U=D("u-wrap",L),V=k.under=D("u-under",U);U.appendChild(I);const K=k.over=D("u-over",U),Z=+q((u=Ce(u)).pxAlign,1),$=rl(Z);(u.plugins||[]).forEach((e=>{e.opts&&(u=e.opts(k,u)||u)}));const te=u.ms||.001,re=k.series=1==v?Tl(u.series||[],Ct,Kt,!1):function(e,t){return e.map(((e,l)=>0==l?null:Fe({},t,e)))}(u.series||[null],qt),ae=k.axes=Tl(u.axes||[],Yt,Ut,!0),he=k.scales={},ge=k.bands=u.bands||[];ge.forEach((e=>{e.fill=me(e.fill||null),e.dir=q(e.dir,-1)}));const ve=2==v?re[1].facets[0].scale:re[0].scale,Me={axes:function(){for(let e=0;ae.length>e;e++){let t=ae[e];if(!t.show||!t._show)continue;let l,n,u=t.side,a=u%2,f=t.stroke(k,e),c=0==u||3==u?-1:1;if(t.label){let e=le((t._lpos+t.labelGap*c)*b);ql(t.labelFont[0],f,"center",2==u?i:o),O.save(),1==a?(l=n=0,O.translate(e,le(At+Lt/2)),O.rotate((3==u?-Q:Q)/2)):(l=le(Pt+Wt/2),n=e),O.fillText(t.label,l,n),O.restore()}let[h,d]=t._found;if(0==d)continue;let p=he[t.scale],m=0==a?Wt:Lt,g=0==a?Pt:At,x=le(t.gap*b),w=t._splits,_=2==p.distr?w.map((e=>jl[e])):w,v=2==p.distr?jl[w[1]]-jl[w[0]]:h,y=t.ticks,M=t.border,S=y.show?le(y.size*b):0,E=t._rotate*-Q/180,D=$(t._pos*b),z=D+(S+x)*c;n=0==a?z:0,l=1==a?z:0,ql(t.font[0],f,1==t.align?s:2==t.align?r:E>0?s:0>E?r:0==a?"center":3==u?r:s,E||1==a?"middle":2==u?i:o);let T=1.5*t.font[1],P=w.map((e=>$(C(e,p,m,g)))),A=t._values;for(let e=0;A.length>e;e++){let t=A[e];if(null!=t){0==a?l=P[e]:n=P[e],t=""+t;let i=-1==t.indexOf("\n")?[t]:t.split(/\n/gm);for(let e=0;i.length>e;e++){let t=i[e];E?(O.save(),O.translate(l,n+e*T),O.rotate(E),O.fillText(t,0,0),O.restore()):O.fillText(t,l,n+e*T)}}}y.show&&tn(P,y.filter(k,_,e,d,v),a,u,D,S,ye(y.width*b,3),y.stroke(k,e),y.dash,y.cap);let W=t.grid;W.show&&tn(P,W.filter(k,_,e,d,v),a,0==a?2:1,0==a?At:Pt,0==a?Lt:Wt,ye(W.width*b,3),W.stroke(k,e),W.dash,W.cap),M.show&&tn([D],[1],0==a?1:0,0==a?1:2,1==a?At:Pt,1==a?Lt:Wt,ye(M.width*b,3),M.stroke(k,e),M.dash,M.cap)}ti("drawAxes")},series:function(){pl>0&&(re.forEach(((e,t)=>{if(t>0&&e.show&&null==e._paths){let l=function(e){let t=pe(ml-1,0,pl-1),l=pe(gl+1,0,pl-1);for(;null==e[t]&&t>0;)t--;for(;null==e[l]&&pl-1>l;)l++;return[t,l]}(m[t]);e._paths=e.paths(k,t,l[0],l[1])}})),re.forEach(((e,t)=>{if(t>0&&e.show){Nl!=e.alpha&&(O.globalAlpha=Nl=e.alpha),Zl(t,!1),e._paths&&$l(t,!1);{Zl(t,!0);let l=e.points.show(k,t,ml,gl),n=e.points.filter(k,t,l,e._paths?e._paths.gaps:null);(l||n)&&(e.points._paths=e.points.paths(k,t,ml,gl,n),$l(t,!0))}1!=Nl&&(O.globalAlpha=Nl=1),ti("drawSeries",t)}})))}},Se=(u.drawOrder||["axes","series"]).map((e=>Me[e]));function Ee(e){let t=he[e];if(null==t){let l=(u.scales||De)[e]||De;if(null!=l.from)Ee(l.from),he[e]=Fe({},he[l.from],l,{key:e});else{t=he[e]=Fe({},e==ve?$t:Xt,l),t.key=e;let n=t.time,i=t.range,o=Pe(i);if((e!=ve||2==v&&!n)&&(!o||null!=i[0]&&null!=i[1]||(i={min:null==i[0]?B:{mode:1,hard:i[0],soft:i[0]},max:null==i[1]?B:{mode:1,hard:i[1],soft:i[1]}},o=!1),!o&&We(i))){let e=i;i=(t,l,n)=>null==l?Te:J(l,n,e)}t.range=me(i||(n?Wl:e==ve?3==t.distr?Fl:4==t.distr?Rl:Al:3==t.distr?Cl:4==t.distr?Hl:Yl)),t.auto=me(!o&&t.auto),t.clamp=me(t.clamp||Zt),t._min=t._max=null}}}Ee("x"),Ee("y"),1==v&&re.forEach((e=>{Ee(e.scale)})),ae.forEach((e=>{Ee(e.scale)}));for(let e in u.scales)Ee(e);const He=he[ve],Ge=He.distr;let Le,Ie;0==He.ori?(y(L,"u-hz"),Le=P,Ie=W):(y(L,"u-vt"),Le=W,Ie=P);const Oe={};for(let e in he){let t=he[e];null==t.min&&null==t.max||(Oe[e]={min:t.min,max:t.max},t.min=t.max=null)}const Ne=u.tzDate||(e=>new Date(le(e/te))),je=u.fmtDate||Ve,Be=1==te?dt(Ne):gt(Ne),Ue=wt(Ne,xt(1==te?ht:mt,je)),Je=bt(Ne,kt("{YYYY}-{MM}-{DD} {h}:{mm}{aa}",je)),qe=[],Ke=k.legend=Fe({},vt,u.legend),Ze=Ke.show,$e=Ke.markers;let et;Ke.idxs=qe,$e.width=me($e.width),$e.dash=me($e.dash),$e.stroke=me($e.stroke),$e.fill=me($e.fill);let tt,lt=[],nt=[],it=!1,ot={};if(Ke.live){const e=re[1]?re[1].values:null;it=null!=e,tt=it?e(k,1,0):{_:0};for(let e in tt)ot[e]="--"}if(Ze)if(et=E("table","u-legend",L),it){let e=E("tr","u-thead",et);for(var st in E("th",null,e),tt)E("th",t,e).textContent=st}else y(et,"u-inline"),Ke.live&&y(et,"u-live");const rt={show:!0},ut={show:!1},at=new Map;function ft(e,t,l){const n=at.get(t)||{},i=il.bind[e](k,t,l);i&&(H(e,t,n[e]=i),at.set(t,n))}function _t(e,t){const l=at.get(t)||{};for(let n in l)null!=e&&n!=e||(R(n,t,l[n]),delete l[n]);null==e&&at.delete(t)}let yt=0,Mt=0,St=0,Dt=0,zt=0,Tt=0,Pt=0,At=0,Wt=0,Lt=0;k.bbox={};let It=!1,Ot=!1,Nt=!1,Jt=!1,Qt=!1;function tl(e,t,l){(l||e!=k.width||t!=k.height)&&ll(e,t),on(!1),Nt=!0,Ot=!0,Jt=Qt=il.left>=0,_n()}function ll(e,t){k.width=yt=St=e,k.height=Mt=Dt=t,zt=Tt=0,function(){let e=!1,t=!1,l=!1,n=!1;ae.forEach((i=>{if(i.show&&i._show){let{side:o,_size:s}=i,r=o%2,u=s+(null!=i.label?i.labelSize:0);u>0&&(r?(St-=u,3==o?(zt+=u,n=!0):l=!0):(Dt-=u,0==o?(Tt+=u,e=!0):t=!0))}})),fl[0]=e,fl[1]=l,fl[2]=t,fl[3]=n,St-=dl[1]+dl[3],zt+=dl[3],Dt-=dl[2]+dl[0],Tt+=dl[0]}(),function(){let e=zt+St,t=Tt+Dt,l=zt,n=Tt;function i(i,o){switch(i){case 1:return e+=o,e-o;case 2:return t+=o,t-o;case 3:return l-=o,l+o;case 0:return n-=o,n+o}}ae.forEach((e=>{if(e.show&&e._show){let t=e.side;e._pos=i(t,e._size),null!=e.label&&(e._lpos=i(t,e.labelSize))}}))}();let l=k.bbox;Pt=l.left=de(zt*b,.5),At=l.top=de(Tt*b,.5),Wt=l.width=de(St*b,.5),Lt=l.height=de(Dt*b,.5)}k.setSize=function({width:e,height:t}){tl(e,t)};const il=k.cursor=Fe({},Et,{drag:{y:2==v}},u.cursor);{il.idxs=qe,il._lock=!1;let e=il.points;e.show=me(e.show),e.size=me(e.size),e.stroke=me(e.stroke),e.width=me(e.width),e.fill=me(e.fill)}const ol=k.focus=Fe({},u.focus||{alpha:.3},il.focus),sl=ol.prox>=0;let ul=[null];function al(l,n){if(1==v||n>0){let e=1==v&&he[l.scale].time,t=l.value;l.value=e?Ae(t)?bt(Ne,kt(t,je)):t||Je:t||Bt,l.label=l.label||(e?"Time":"Value")}if(n>0){l.width=null==l.width?1:l.width,l.paths=l.paths||Dl||we,l.fillTo=me(l.fillTo||nl),l.pxAlign=+q(l.pxAlign,Z),l.pxRound=rl(l.pxAlign),l.stroke=me(l.stroke||null),l.fill=me(l.fill||null),l._stroke=l._fill=l._paths=l._focus=null;let e=Vt(l.width,1),t=l.points=Fe({},{size:e,width:oe(1,.2*e),stroke:l.stroke,space:2*e,paths:zl,_stroke:null,_fill:null},l.points);t.show=me(t.show),t.filter=me(t.filter),t.fill=me(t.fill),t.stroke=me(t.stroke),t.paths=me(t.paths),t.pxAlign=l.pxAlign}if(Ze){let i=function(l,n){if(0==n&&(it||!Ke.live||2==v))return Te;let i=[],o=E("tr","u-series",et,et.childNodes[n]);y(o,l.class),l.show||y(o,e);let s=E("th",null,o);if($e.show){let e=D("u-marker",s);if(n>0){let t=$e.width(k,n);t&&(e.style.border=t+"px "+$e.dash(k,n)+" "+$e.stroke(k,n)),e.style.background=$e.fill(k,n)}}let r=D(t,s);for(var u in r.textContent=l.label,n>0&&($e.show||(r.style.color=l.width>0?$e.stroke(k,n):$e.fill(k,n)),ft("click",s,(e=>{if(il._lock)return;let t=re.indexOf(l);if((e.ctrlKey||e.metaKey)!=Ke.isolate){let e=re.some(((e,l)=>l>0&&l!=t&&e.show));re.forEach(((l,n)=>{n>0&&Pn(n,e?n==t?rt:ut:rt,!0,li.setSeries)}))}else Pn(t,{show:!l.show},!0,li.setSeries)})),sl&&ft(h,s,(()=>{il._lock||Pn(re.indexOf(l),Cn,!0,li.setSeries)}))),tt){let e=E("td","u-value",o);e.textContent="--",i.push(e)}return[o,i]}(l,n);lt.splice(n,0,i[0]),nt.splice(n,0,i[1]),Ke.values.push(null)}if(il.show){qe.splice(n,0,null);let e=function(e,t){if(t>0){let l=il.points.show(k,t);if(l)return y(l,"u-cursor-pt"),y(l,e.class),T(l,-10,-10,St,Dt),K.insertBefore(l,ul[t]),l}}(l,n);e&&ul.splice(n,0,e)}ti("addSeries",n)}k.addSeries=function(e,t){e=Pl(e,t=null==t?re.length:t,Ct,Kt),re.splice(t,0,e),al(re[t],t)},k.delSeries=function(e){if(re.splice(e,1),Ze){Ke.values.splice(e,1),nt.splice(e,1);let t=lt.splice(e,1)[0];_t(null,t.firstChild),t.remove()}il.show&&(qe.splice(e,1),ul.length>1&&ul.splice(e,1)[0].remove()),ti("delSeries",e)};const fl=[!1,!1,!1,!1];function cl(e,t,l){let[n,i,o,s]=l,r=t%2,u=0;return 0==r&&(s||i)&&(u=0==t&&!n||2==t&&!o?le(Yt.size/3):0),1==r&&(n||o)&&(u=1==t&&!i||3==t&&!s?le(Ut.size/2):0),u}const hl=k.padding=(u.padding||[cl,cl,cl,cl]).map((e=>me(q(e,cl)))),dl=k._padding=hl.map(((e,t)=>e(k,t,fl,0)));let pl,ml=null,gl=null;const xl=1==v?re[0].idxs:null;let wl,_l,kl,bl,vl,yl,Ml,El,Ol,Nl,jl=null,Bl=!1;function Ul(e,t){if(m=null==e?[]:Ce(e,Ye),2==v){pl=0;for(let e=1;re.length>e;e++)pl+=m[e][0].length;k.data=m=e}else if(null==m[0]&&(m[0]=[]),k.data=m.slice(),jl=m[0],pl=jl.length,2==Ge){m[0]=Array(pl);for(let e=0;pl>e;e++)m[0][e]=e}if(k._data=m,on(!0),ti("setData"),!1!==t){let e=He;e.auto(k,Bl)?Vl():Tn(ve,e.min,e.max),Jt=il.left>=0,Qt=!0,_n()}}function Vl(){let e,t;Bl=!0,1==v&&(pl>0?(ml=xl[0]=0,gl=xl[1]=pl-1,e=m[0][ml],t=m[0][gl],2==Ge?(e=ml,t=gl):1==pl&&(3==Ge?[e,t]=N(e,e,He.log,!1):4==Ge?[e,t]=j(e,e,He.log,!1):He.time?t=e+le(86400/te):[e,t]=J(e,t,.1,!0))):(ml=xl[0]=e=null,gl=xl[1]=t=null)),Tn(ve,e,t)}function Jl(e="#0000",t,l=ze,n="butt",i="#0000",o="round"){e!=wl&&(O.strokeStyle=wl=e),i!=_l&&(O.fillStyle=_l=i),t!=kl&&(O.lineWidth=kl=t),o!=vl&&(O.lineJoin=vl=o),n!=yl&&(O.lineCap=yl=n),l!=bl&&O.setLineDash(bl=l)}function ql(e,t,l,n){t!=_l&&(O.fillStyle=_l=t),e!=Ml&&(O.font=Ml=e),l!=El&&(O.textAlign=El=l),n!=Ol&&(O.textBaseline=Ol=n)}function Kl(e,t,l,n,i=0){if(n.length>0&&e.auto(k,Bl)&&(null==t||null==t.min)){let t=q(ml,0),o=q(gl,n.length-1),s=null==l.min?3==e.distr?function(e,t,l){let n=ce,i=-ce;for(let o=t;l>=o;o++)e[o]>0&&(n=ie(n,e[o]),i=oe(i,e[o]));return[n==ce?1:n,i==-ce?10:i]}(n,t,o):function(e,t,l,n){let i=ce,o=-ce;if(1==n)i=e[t],o=e[l];else if(-1==n)i=e[l],o=e[t];else for(let n=t;l>=n;n++)null!=e[n]&&(i=ie(i,e[n]),o=oe(o,e[n]));return[i,o]}(n,t,o,i):[l.min,l.max];e.min=ie(e.min,l.min=s[0]),e.max=oe(e.max,l.max=s[1])}}function Zl(e,t){let l=t?re[e].points:re[e];l._stroke=l.stroke(k,e),l._fill=l.fill(k,e)}function $l(e,t){let l=t?re[e].points:re[e],n=l._stroke,i=l._fill,{stroke:o,fill:s,clip:r,flags:u}=l._paths,a=null,f=ye(l.width*b,3),c=f%2/2;t&&null==i&&(i=f>0?"#fff":n);let h=1==l.pxAlign;if(h&&O.translate(c,c),!t){let e=Pt,t=At,n=Wt,i=Lt,o=f*b/2;0==l.min&&(i+=o),0==l.max&&(t-=o,i+=o),a=new Path2D,a.rect(e,t,n,i)}t?Xl(n,f,l.dash,l.cap,i,o,s,u,r):function(e,t,l,n,i,o,s,r,u,a,f){let c=!1;ge.forEach(((h,d)=>{if(h.series[0]==e){let e,p=re[h.series[1]],g=m[h.series[1]],x=(p._paths||De).band;Pe(x)&&(x=1==h.dir?x[0]:x[1]);let w=null;p.show&&x&&function(e,t,l){for(t=q(t,0),l=q(l,e.length-1);l>=t;){if(null!=e[t])return!0;t++}return!1}(g,ml,gl)?(w=h.fill(k,d)||o,e=p._paths.clip):x=null,Xl(t,l,n,i,w,s,r,u,a,f,e,x),c=!0}})),c||Xl(t,l,n,i,o,s,r,u,a,f)}(e,n,f,l.dash,l.cap,i,o,s,u,a,r),h&&O.translate(-c,-c)}function Xl(e,t,l,n,i,o,s,r,u,a,f,c){Jl(e,t,l,n,i),(u||a||c)&&(O.save(),u&&O.clip(u),a&&O.clip(a)),c?3==(3&r)?(O.clip(c),f&&O.clip(f),en(i,s),Ql(e,o,t)):2&r?(en(i,s),O.clip(c),Ql(e,o,t)):1&r&&(O.save(),O.clip(c),f&&O.clip(f),en(i,s),O.restore(),Ql(e,o,t)):(en(i,s),Ql(e,o,t)),(u||a||c)&&O.restore()}function Ql(e,t,l){l>0&&(t instanceof Map?t.forEach(((e,t)=>{O.strokeStyle=wl=t,O.stroke(e)})):null!=t&&e&&O.stroke(t))}function en(e,t){t instanceof Map?t.forEach(((e,t)=>{O.fillStyle=_l=t,O.fill(e)})):null!=t&&e&&O.fill(t)}function tn(e,t,l,n,i,o,s,r,u,a){let f=s%2/2;1==Z&&O.translate(f,f),Jl(r,s,u,a,r),O.beginPath();let c,h,d,p,m=i+(0==n||3==n?-o:o);0==l?(h=i,p=m):(c=i,d=m);for(let n=0;e.length>n;n++)null!=t[n]&&(0==l?c=d=e[n]:h=p=e[n],O.moveTo(c,h),O.lineTo(d,p));O.stroke(),1==Z&&O.translate(-f,-f)}function ln(e){let t=!0;return ae.forEach(((l,n)=>{if(!l.show)return;let i=he[l.scale];if(null==i.min)return void(l._show&&(t=!1,l._show=!1,on(!1)));l._show||(t=!1,l._show=!0,on(!1));let o=l.side,s=o%2,{min:r,max:u}=i,[a,f]=function(e,t,l,n){let i,o=ae[e];if(n>0){let s=o._space=o.space(k,e,t,l,n);i=Gl(t,l,o._incrs=o.incrs(k,e,t,l,n,s),n,s)}else i=[0,0];return o._found=i}(n,r,u,0==s?St:Dt);if(0==f)return;let c=l._splits=l.splits(k,n,r,u,a,f,2==i.distr),h=2==i.distr?c.map((e=>jl[e])):c,d=2==i.distr?jl[c[1]]-jl[c[0]]:a,p=l._values=l.values(k,l.filter(k,h,n,f,d),n,f,d);l._rotate=2==o?l.rotate(k,p,n,f):0;let m=l._size;l._size=ne(l.size(k,p,n,e)),null!=m&&l._size!=m&&(t=!1)})),t}function nn(e){let t=!0;return hl.forEach(((l,n)=>{let i=l(k,n,fl,e);i!=dl[n]&&(t=!1),dl[n]=i})),t}function on(e){re.forEach(((t,l)=>{l>0&&(t._paths=null,e&&(1==v?(t.min=null,t.max=null):t.facets.forEach((e=>{e.min=null,e.max=null}))))}))}k.setData=Ul;let sn,rn,un,an,fn,cn,hn,dn,pn,mn,gn,xn,wn=!1;function _n(){wn||(Re(kn),wn=!0)}function kn(){It&&(function(){let e=Ce(he,Ye);for(let t in e){let l=e[t],n=Oe[t];if(null!=n&&null!=n.min)Fe(l,n),t==ve&&on(!0);else if(t!=ve||2==v)if(0==pl&&null==l.from){let e=l.range(k,null,null,t);l.min=e[0],l.max=e[1]}else l.min=ce,l.max=-ce}if(pl>0){re.forEach(((t,l)=>{if(1==v){let n=t.scale,i=e[n],o=Oe[n];if(0==l){let e=i.range(k,i.min,i.max,n);i.min=e[0],i.max=e[1],ml=G(i.min,m[0]),gl=G(i.max,m[0]),i.min>m[0][ml]&&ml++,m[0][gl]>i.max&&gl--,t.min=jl[ml],t.max=jl[gl]}else t.show&&t.auto&&Kl(i,o,t,m[l],t.sorted);t.idxs[0]=ml,t.idxs[1]=gl}else if(l>0&&t.show&&t.auto){let[n,i]=t.facets,o=n.scale,s=i.scale,[r,u]=m[l];Kl(e[o],Oe[o],n,r,n.sorted),Kl(e[s],Oe[s],i,u,i.sorted),t.min=i.min,t.max=i.max}}));for(let t in e){let l=e[t],n=Oe[t];if(null==l.from&&(null==n||null==n.min)){let e=l.range(k,l.min==ce?null:l.min,l.max==-ce?null:l.max,t);l.min=e[0],l.max=e[1]}}}for(let t in e){let l=e[t];if(null!=l.from){let n=e[l.from];if(null==n.min)l.min=l.max=null;else{let e=l.range(k,n.min,n.max,t);l.min=e[0],l.max=e[1]}}}let t={},l=!1;for(let n in e){let i=e[n],o=he[n];if(o.min!=i.min||o.max!=i.max){o.min=i.min,o.max=i.max;let e=o.distr;o._min=3==e?ue(o.min):4==e?fe(o.min,o.asinh):o.min,o._max=3==e?ue(o.max):4==e?fe(o.max,o.asinh):o.max,t[n]=l=!0}}if(l){re.forEach(((e,l)=>{2==v?l>0&&t.y&&(e._paths=null):t[e.scale]&&(e._paths=null)}));for(let e in t)Nt=!0,ti("setScale",e);il.show&&(Jt=Qt=il.left>=0)}for(let e in Oe)Oe[e]=null}(),It=!1),Nt&&(function(){let e=!1,t=0;for(;!e;){t++;let l=ln(t),n=nn(t);e=3==t||l&&n,e||(ll(k.width,k.height),Ot=!0)}}(),Nt=!1),Ot&&(S(V,s,zt),S(V,i,Tt),S(V,l,St),S(V,n,Dt),S(K,s,zt),S(K,i,Tt),S(K,l,St),S(K,n,Dt),S(U,l,yt),S(U,n,Mt),I.width=le(yt*b),I.height=le(Mt*b),ae.forEach((({_el:t,_show:l,_size:n,_pos:i,side:o})=>{if(null!=t)if(l){let l=o%2==1;S(t,l?"left":"top",i-(3===o||0===o?n:0)),S(t,l?"width":"height",n),S(t,l?"top":"left",l?Tt:zt),S(t,l?"height":"width",l?Dt:St),M(t,e)}else y(t,e)})),wl=_l=kl=vl=yl=Ml=El=Ol=bl=null,Nl=1,Bn(!0),ti("setSize"),Ot=!1),yt>0&&Mt>0&&(O.clearRect(0,0,I.width,I.height),ti("drawClear"),Se.forEach((e=>e())),ti("draw")),il.show&&Jt&&(Nn(null,!0,!1),Jt=!1),F||(F=!0,k.status=1,ti("ready")),Bl=!1,wn=!1}function bn(e,t){let l=he[e];if(null==l.from){if(0==pl){let n=l.range(k,t.min,t.max,e);t.min=n[0],t.max=n[1]}if(t.min>t.max){let e=t.min;t.min=t.max,t.max=e}if(pl>1&&null!=t.min&&null!=t.max&&1e-16>t.max-t.min)return;e==ve&&2==l.distr&&pl>0&&(t.min=G(t.min,m[0]),t.max=G(t.max,m[0]),t.min==t.max&&t.max++),Oe[e]=t,It=!0,_n()}}k.redraw=(e,t)=>{Nt=t||!1,!1!==e?Tn(ve,He.min,He.max):_n()},k.setScale=bn;let vn=!1;const yn=il.drag;let Mn=yn.x,Sn=yn.y;il.show&&(il.x&&(sn=D("u-cursor-x",K)),il.y&&(rn=D("u-cursor-y",K)),0==He.ori?(un=sn,an=rn):(un=rn,an=sn),gn=il.left,xn=il.top);const En=k.select=Fe({show:!0,over:!0,left:0,width:0,top:0,height:0},u.select),Dn=En.show?D("u-select",En.over?K:V):null;function zn(e,t){if(En.show){for(let t in e)S(Dn,t,En[t]=e[t]);!1!==t&&ti("setSelect")}}function Tn(e,t,l){bn(e,{min:t,max:l})}function Pn(t,l,n,i){null!=l.focus&&function(e){if(e!=Yn){let t=null==e,l=1!=ol.alpha;re.forEach(((n,i)=>{let o=t||0==i||i==e;n._focus=t?null:o,l&&function(e,t){re[e].alpha=t,il.show&&ul[e]&&(ul[e].style.opacity=t),Ze&<[e]&&(lt[e].style.opacity=t)}(i,o?1:ol.alpha)})),Yn=e,l&&_n()}}(t),null!=l.show&&re.forEach(((n,i)=>{0>=i||t!=i&&null!=t||(n.show=l.show,function(t){let l=Ze?lt[t]:null;re[t].show?l&&M(l,e):(l&&y(l,e),ul.length>1&&T(ul[t],-10,-10,St,Dt))}(i),Tn(2==v?n.facets[1].scale:n.scale,null,null),_n())})),!1!==n&&ti("setSeries",t,l),i&&oi("setSeries",k,t,l)}let An,Wn,Yn;k.setSelect=zn,k.setSeries=Pn,k.addBand=function(e,t){e.fill=me(e.fill||null),e.dir=q(e.dir,-1),ge.splice(t=null==t?ge.length:t,0,e)},k.setBand=function(e,t){Fe(ge[e],t)},k.delBand=function(e){null==e?ge.length=0:ge.splice(e,1)};const Cn={focus:!0};function Fn(e,t,l){let n=he[t];l&&(e=e/b-(1==n.ori?Tt:zt));let i=St;1==n.ori&&(i=Dt,e=i-e),-1==n.dir&&(e=i-e);let o=n._min,s=o+e/i*(n._max-o),r=n.distr;return 3==r?se(10,s):4==r?((e,t=1)=>X.sinh(e)*t)(s,n.asinh):s}function Hn(e,t){S(Dn,s,En.left=e),S(Dn,l,En.width=t)}function Rn(e,t){S(Dn,i,En.top=e),S(Dn,n,En.height=t)}Ze&&sl&&H(d,et,(()=>{il._lock||null!=Yn&&Pn(null,Cn,!0,li.setSeries)})),k.valToIdx=e=>G(e,m[0]),k.posToIdx=function(e,t){return G(Fn(e,ve,t),m[0],ml,gl)},k.posToVal=Fn,k.valToPos=(e,t,l)=>0==he[t].ori?P(e,he[t],l?Wt:St,l?Pt:0):W(e,he[t],l?Lt:Dt,l?At:0),k.batch=function(e){e(k),_n()},k.setCursor=(e,t,l)=>{gn=e.left,xn=e.top,Nn(null,t,l)};let Gn=0==He.ori?Hn:Rn,Ln=1==He.ori?Hn:Rn;function In(e,t){if(null!=e){let t=e.idx;Ke.idx=t,re.forEach(((e,l)=>{(l>0||!it)&&On(l,t)}))}Ze&&Ke.live&&function(){if(Ze&&Ke.live)for(let e=2==v?1:0;re.length>e;e++){if(0==e&&it)continue;let t=Ke.values[e],l=0;for(let n in t)nt[e][l++].firstChild.nodeValue=t[n]}}(),Qt=!1,!1!==t&&ti("setLegend")}function On(e,t){let l;if(null==t)l=ot;else{let n=re[e],i=0==e&&2==Ge?jl:m[e];l=it?n.values(k,e,t):{_:n.value(k,i[t],e,t)}}Ke.values[e]=l}function Nn(e,t,l){let n;pn=gn,mn=xn,[gn,xn]=il.move(k,gn,xn),il.show&&(un&&T(un,le(gn),0,St,Dt),an&&T(an,0,le(xn),St,Dt)),An=ce;let i=0==He.ori?St:Dt,o=1==He.ori?St:Dt;if(0>gn||0==pl||ml>gl){n=null;for(let e=0;re.length>e;e++)e>0&&ul.length>1&&T(ul[e],-10,-10,St,Dt);if(sl&&Pn(null,Cn,!0,null==e&&li.setSeries),Ke.live){qe.fill(null),Qt=!0;for(let e=0;re.length>e;e++)Ke.values[e]=ot}}else{let e,t,l;1==v&&(e=0==He.ori?gn:xn,t=Fn(e,ve),n=G(t,m[0],ml,gl),l=be(Le(m[0][n],He,i,0),.5));for(let e=2==v?1:0;re.length>e;e++){let s=re[e],r=qe[e],u=1==v?m[e][r]:m[e][1][r],a=il.dataIdx(k,e,n,t),f=1==v?m[e][a]:m[e][1][a];Qt=Qt||f!=u||a!=r,qe[e]=a;let c=a==n?l:be(Le(1==v?m[0][a]:m[e][0][a],He,i,0),.5);if(e>0&&s.show){let t,l,n=null==f?-10:be(Ie(f,1==v?he[s.scale]:he[s.facets[1].scale],o,0),.5);if(n>0&&1==v){let t=ee(n-xn);t>An||(An=t,Wn=e)}if(0==He.ori?(t=c,l=n):(t=n,l=c),Qt&&ul.length>1){A(ul[e],il.points.fill(k,e),il.points.stroke(k,e));let n,i,o,s,r=!0,u=il.points.bbox;if(null!=u){r=!1;let t=u(k,e);o=t.left,s=t.top,n=t.width,i=t.height}else o=t,s=l,n=i=il.points.size(k,e);Y(ul[e],n,i,r),T(ul[e],o,s,St,Dt)}}if(Ke.live){if(!Qt||0==e&&it)continue;On(e,a)}}}if(il.idx=n,il.left=gn,il.top=xn,Qt&&(Ke.idx=n,In()),En.show&&vn)if(null!=e){let[t,l]=li.scales,[n,s]=li.match,[r,u]=e.cursor.sync.scales,a=e.cursor.drag;if(Mn=a._x,Sn=a._y,Mn||Sn){let a,f,c,h,d,{left:p,top:m,width:g,height:x}=e.select,w=e.scales[t].ori,_=e.posToVal,k=null!=t&&n(t,r),b=null!=l&&s(l,u);k&&Mn?(0==w?(a=p,f=g):(a=m,f=x),c=he[t],h=Le(_(a,r),c,i,0),d=Le(_(a+f,r),c,i,0),Gn(ie(h,d),ee(d-h))):Gn(0,i),b&&Sn?(1==w?(a=p,f=g):(a=m,f=x),c=he[l],h=Ie(_(a,u),c,o,0),d=Ie(_(a+f,u),c,o,0),Ln(ie(h,d),ee(d-h))):Ln(0,o)}else qn()}else{let e=ee(pn-fn),t=ee(mn-cn);if(1==He.ori){let l=e;e=t,t=l}Mn=yn.x&&e>=yn.dist,Sn=yn.y&&t>=yn.dist;let l,n,s=yn.uni;null!=s?Mn&&Sn&&(Mn=e>=s,Sn=t>=s,Mn||Sn||(t>e?Sn=!0:Mn=!0)):yn.x&&yn.y&&(Mn||Sn)&&(Mn=Sn=!0),Mn&&(0==He.ori?(l=hn,n=gn):(l=dn,n=xn),Gn(ie(l,n),ee(n-l)),Sn||Ln(0,o)),Sn&&(1==He.ori?(l=hn,n=gn):(l=dn,n=xn),Ln(ie(l,n),ee(n-l)),Mn||Gn(0,i)),Mn||Sn||(Gn(0,0),Ln(0,0))}if(yn._x=Mn,yn._y=Sn,null==e){if(l){if(null!=ni){let[e,t]=li.scales;li.values[0]=null!=e?Fn(0==He.ori?gn:xn,e):null,li.values[1]=null!=t?Fn(1==He.ori?gn:xn,t):null}oi(a,k,gn,xn,St,Dt,n)}if(sl){let e=l&&li.setSeries,t=ol.prox;null==Yn?An>t||Pn(Wn,Cn,!0,e):An>t?Pn(null,Cn,!0,e):Wn!=Yn&&Pn(Wn,Cn,!0,e)}}F&&!1!==t&&ti("setCursor")}k.setLegend=In;let jn=null;function Bn(e){!0===e?jn=null:(jn=K.getBoundingClientRect(),ti("syncRect",jn))}function Un(e,t,l,n,i,o){il._lock||(Vn(e,t,l,n,i,o,0,!1,null!=e),null!=e?Nn(null,!0,!0):Nn(t,!0,!1))}function Vn(e,t,l,n,i,o,s,r,u){if(null==jn&&Bn(!1),null!=e)l=e.clientX-jn.left,n=e.clientY-jn.top;else{if(0>l||0>n)return gn=-10,void(xn=-10);let[e,s]=li.scales,r=t.cursor.sync,[u,a]=r.values,[f,c]=r.scales,[h,d]=li.match,p=t.axes[0].side%2==1,m=0==He.ori?St:Dt,g=1==He.ori?St:Dt,x=p?o:i,w=p?i:o,_=p?n:l,k=p?l:n;if(l=null!=f?h(e,f)?C(u,he[e],m,0):-10:m*(_/x),n=null!=c?d(s,c)?C(a,he[s],g,0):-10:g*(k/w),1==He.ori){let e=l;l=n,n=e}}u&&(l>1&&St-1>l||(l=de(l,St)),n>1&&Dt-1>n||(n=de(n,Dt))),r?(fn=l,cn=n,[hn,dn]=il.move(k,l,n)):(gn=l,xn=n)}const Jn={width:0,height:0};function qn(){zn(Jn,!1)}function Kn(e,t,l,n,i,o){vn=!0,Mn=Sn=yn._x=yn._y=!1,Vn(e,t,l,n,i,o,0,!0,!1),null!=e&&(ft(c,w,Zn),oi(f,k,hn,dn,St,Dt,null))}function Zn(e,t,l,n,i,o){vn=yn._x=yn._y=!1,Vn(e,t,l,n,i,o,0,!1,!0);let{left:s,top:r,width:u,height:a}=En,f=u>0||a>0;if(f&&zn(En),yn.setScale&&f){let e=s,t=u,l=r,n=a;if(1==He.ori&&(e=r,t=a,l=s,n=u),Mn&&Tn(ve,Fn(e,ve),Fn(e+t,ve)),Sn)for(let e in he){let t=he[e];e!=ve&&null==t.from&&t.min!=ce&&Tn(e,Fn(l+n,e),Fn(l,e))}qn()}else il.lock&&(il._lock=!il._lock,il._lock||Nn(null,!0,!1));null!=e&&(_t(c,w),oi(c,k,gn,xn,St,Dt,null))}function $n(e){Vl(),qn(),null!=e&&oi(p,k,gn,xn,St,Dt,null)}function Xn(){ae.forEach(Il),tl(k.width,k.height,!0)}H(g,_,Xn);const Qn={};Qn.mousedown=Kn,Qn.mousemove=Un,Qn.mouseup=Zn,Qn.dblclick=$n,Qn.setSeries=(e,t,l,n)=>{Pn(l,n,!0,!1)},il.show&&(ft(f,K,Kn),ft(a,K,Un),ft(h,K,Bn),ft(d,K,(function(){if(!il._lock){let e=vn;if(vn){let e,t,l=!0,n=!0,i=10;0==He.ori?(e=Mn,t=Sn):(e=Sn,t=Mn),e&&t&&(l=i>=gn||gn>=St-i,n=i>=xn||xn>=Dt-i),e&&l&&(gn=hn>gn?0:St),t&&n&&(xn=dn>xn?0:Dt),Nn(null,!0,!0),vn=!1}gn=-10,xn=-10,Nn(null,!0,!0),e&&(vn=e)}})),ft(p,K,$n),Sl.add(k),k.syncRect=Bn);const ei=k.hooks=u.hooks||{};function ti(e,t,l){e in ei&&ei[e].forEach((e=>{e.call(null,k,t,l)}))}(u.plugins||[]).forEach((e=>{for(let t in e.hooks)ei[t]=(ei[t]||[]).concat(e.hooks[t])}));const li=Fe({key:null,setSeries:!1,filters:{pub:_e,sub:_e},scales:[ve,re[1]?re[1].scale:null],match:[ke,ke],values:[null,null]},il.sync);il.sync=li;const ni=li.key,ii=el(ni);function oi(e,t,l,n,i,o,s){li.filters.pub(e,t,l,n,i,o,s)&&ii.pub(e,t,l,n,i,o,s)}function si(){ti("init",u,m),Ul(m||u.data,!1),Oe[ve]?bn(ve,Oe[ve]):Vl(),tl(u.width,u.height),Nn(null,!0,!1),zn(En,!1)}return ii.sub(k),k.pub=function(e,t,l,n,i,o,s){li.filters.sub(e,t,l,n,i,o,s)&&Qn[e](null,t,l,n,i,o,s)},k.destroy=function(){ii.unsub(k),Sl.delete(k),at.clear(),R(g,_,Xn),L.remove(),ti("destroy")},re.forEach(al),ae.forEach((function(e,t){if(e._show=e.show,e.show){let l=e.side%2,n=he[e.scale];null==n&&(e.scale=l?re[1].scale:ve,n=he[e.scale]);let i=n.time;e.size=me(e.size),e.space=me(e.space),e.rotate=me(e.rotate),e.incrs=me(e.incrs||(2==n.distr?Xe:i?1==te?ct:pt:Qe)),e.splits=me(e.splits||(i&&1==n.distr?Be:3==n.distr?Rt:4==n.distr?Gt:Ht)),e.stroke=me(e.stroke),e.grid.stroke=me(e.grid.stroke),e.ticks.stroke=me(e.ticks.stroke),e.border.stroke=me(e.border.stroke);let o=e.values;e.values=Pe(o)&&!Pe(o[0])?me(o):i?Pe(o)?wt(Ne,xt(o,je)):Ae(o)?function(e,t){let l=Ve(t);return(t,n)=>n.map((t=>l(e(t))))}(Ne,o):o||Ue:o||Ft,e.filter=me(e.filter||(3>n.distr?xe:jt)),e.font=Ll(e.font),e.labelFont=Ll(e.labelFont),e._size=e.size(k,null,t,0),e._space=e._rotate=e._incrs=e._found=e._splits=e._values=null,e._size>0&&(fl[t]=!0,e._el=D("u-axis",U))}})),x?x instanceof HTMLElement?(x.appendChild(L),si()):x(k,si):si(),k}Ol.assign=Fe,Ol.fmtNum=$,Ol.rangeNum=J,Ol.rangeLog=N,Ol.rangeAsinh=j,Ol.orient=tl,Ol.pxRatio=b,Ol.join=function(e,t){let l=new Set;for(let t=0;e.length>t;t++){let n=e[t][0],i=n.length;for(let e=0;i>e;e++)l.add(n[e])}let n=[Array.from(l).sort(((e,t)=>e-t))],i=n[0].length,o=new Map;for(let e=0;i>e;e++)o.set(n[0][e],e);for(let l=0;e.length>l;l++){let s=e[l],r=s[0];for(let e=1;s.length>e;e++){let u=s[e],a=Array(i).fill(void 0),f=t?t[l][e]:1,c=[];for(let e=0;u.length>e;e++){let t=u[e],l=o.get(r[e]);null===t?0!=f&&(a[l]=t,2==f&&c.push(l)):a[l]=t}He(a,c,i),n.push(a)}}return n},Ol.fmtDate=Ve,Ol.tzDate=function(e,t){let l;return"UTC"==t||"Etc/UTC"==t?l=new Date(+e+6e4*e.getTimezoneOffset()):t==Je?l=e:(l=new Date(e.toLocaleString("en-US",{timeZone:t})),l.setMilliseconds(e.getMilliseconds())),l},Ol.sync=el;{Ol.addGap=function(e,t,l){let n=e[e.length-1];n&&n[0]==t?n[1]=l:e.push([t,l])},Ol.clipGaps=ol;let e=Ol.paths={points:_l};e.linear=yl,e.stepped=function(e){const t=q(e.align,1),l=q(e.ascDesc,!1),n=q(e.alignGaps,0);return(e,i,o,s)=>tl(e,i,((r,u,a,f,c,h,d,p,m,g,x)=>{let w=r.pxRound,_=e=>w(h(e,f,g,p)),k=e=>w(d(e,c,x,m)),v=0==f.ori?cl:hl;const y={stroke:new Path2D,fill:null,clip:null,band:null,gaps:null,flags:1},M=y.stroke,S=f.dir*(0==f.ori?1:-1);o=L(a,o,s,1),s=L(a,o,s,-1);let E=k(a[1==S?o:s]),D=_(u[1==S?o:s]),z=D;v(M,D,E);for(let e=1==S?o:s;e>=o&&s>=e;e+=S){let l=a[e];if(null==l)continue;let n=_(u[e]),i=k(l);1==t?v(M,n,E):v(M,z,i),v(M,n,i),E=i,z=n}let[T,P]=ll(e,i);if(null!=r.fill||0!=T){let t=y.fill=new Path2D(M),l=k(r.fillTo(e,i,r.min,r.max,T));v(t,z,l),v(t,D,l)}if(!r.spanGaps){let c=[];c.push(...sl(u,a,o,s,S,_,n));let h=r.width*b/2,d=l||1==t?h:-h,w=l||-1==t?-h:h;c.forEach((e=>{e[0]+=d,e[1]+=w})),y.gaps=c=r.gaps(e,i,o,s,c),y.clip=ol(c,f.ori,p,m,g,x)}return 0!=P&&(y.band=2==P?[il(e,i,o,s,M,-1),il(e,i,o,s,M,1)]:il(e,i,o,s,M,P)),y}))},e.bars=function(e){const t=q((e=e||De).size,[.6,ce,1]),l=e.align||0,n=(e.gap||0)*b,i=q(e.radius,0),o=1-t[0],s=q(t[1],ce)*b,r=q(t[2],1)*b,u=q(e.disp,De),a=q(e.each,(()=>{})),{fill:f,stroke:c}=u;return(e,t,h,d)=>tl(e,t,((p,m,g,x,w,_,k,v,y,M,S)=>{let E=p.pxRound;const D=x.dir*(0==x.ori?1:-1),z=w.dir*(1==w.ori?1:-1);let T,P,A=0==x.ori?dl:pl,W=0==x.ori?a:(e,t,l,n,i,o,s)=>{a(e,t,l,i,n,s,o)},[Y,C]=ll(e,t),F=3==w.distr?1==Y?w.max:w.min:0,H=k(F,w,S,y),R=E(p.width*b),G=!1,L=null,I=null,O=null,N=null;null==f||0!=R&&null==c||(G=!0,L=f.values(e,t,h,d),I=new Map,new Set(L).forEach((e=>{null!=e&&I.set(e,new Path2D)})),R>0&&(O=c.values(e,t,h,d),N=new Map,new Set(O).forEach((e=>{null!=e&&N.set(e,new Path2D)}))));let{x0:j,size:B}=u;if(null!=j&&null!=B){m=j.values(e,t,h,d),2==j.unit&&(m=m.map((t=>e.posToVal(v+t*M,x.key,!0))));let l=B.values(e,t,h,d);P=2==B.unit?l[0]*M:_(l[0],x,M,v)-_(0,x,M,v),P=E(P-R),T=1==D?-R/2:P+R/2}else{let e=M;if(m.length>1){let t=null;for(let l=0,n=1/0;m.length>l;l++)if(void 0!==g[l]){if(null!=t){let i=ee(m[l]-m[t]);n>i&&(n=i,e=ee(_(m[l],x,M,v)-_(m[t],x,M,v)))}t=l}}P=E(ie(s,oe(r,e-e*o))-R-n),T=(0==l?P/2:l==D?0:P)-l*D*n/2}const U={stroke:null,fill:null,clip:null,band:null,gaps:null,flags:3};let V;0!=C&&(U.band=new Path2D,V=E(k(1==C?w.max:w.min,w,S,y)));const J=G?null:new Path2D,K=U.band;let{y0:Z,y1:$}=u,X=null;null!=Z&&null!=$&&(g=$.values(e,t,h,d),X=Z.values(e,t,h,d));for(let l=1==D?h:d;l>=h&&d>=l;l+=D){let n=g[l],o=_(2!=x.distr||null!=u?m[l]:l,x,M,v),s=k(q(n,F),w,S,y);null!=X&&null!=n&&(H=k(X[l],w,S,y));let r=E(o-T),a=E(oe(s,H)),f=E(ie(s,H)),c=a-f,h=i*P;null!=n&&(G?(R>0&&null!=O[l]&&A(N.get(O[l]),r,f+te(R/2),P,oe(0,c-R),h),null!=L[l]&&A(I.get(L[l]),r,f+te(R/2),P,oe(0,c-R),h)):A(J,r,f+te(R/2),P,oe(0,c-R),h),W(e,t,l,r-R/2,f,P+R,c)),0!=C&&(z*C==1?(a=f,f=V):(f=a,a=V),c=a-f,A(K,r-R/2,f,P+R,oe(0,c),0))}return R>0&&(U.stroke=G?N:J),U.fill=G?I:J,U}))},e.spline=function(e){return function(e,t){const l=q(t?.alignGaps,0);return(t,n,i,o)=>tl(t,n,((s,r,u,a,f,c,h,d,p,m,g)=>{let x,w,_,k=s.pxRound,b=e=>k(c(e,a,m,d)),v=e=>k(h(e,f,g,p));0==a.ori?(x=al,_=cl,w=xl):(x=fl,_=hl,w=wl);const y=a.dir*(0==a.ori?1:-1);i=L(u,i,o,1),o=L(u,i,o,-1);let M=b(r[1==y?i:o]),S=M,E=[],D=[];for(let e=1==y?i:o;e>=i&&o>=e;e+=y)if(null!=u[e]){let t=b(r[e]);E.push(S=t),D.push(v(u[e]))}const z={stroke:e(E,D,x,_,w,k),fill:null,clip:null,band:null,gaps:null,flags:1},T=z.stroke;let[P,A]=ll(t,n);if(null!=s.fill||0!=P){let e=z.fill=new Path2D(T),l=v(s.fillTo(t,n,s.min,s.max,P));_(e,S,l),_(e,M,l)}if(!s.spanGaps){let e=[];e.push(...sl(r,u,i,o,y,b,l)),z.gaps=e=s.gaps(t,n,i,o,e),z.clip=ol(e,a.ori,d,p,m,g)}return 0!=A&&(z.band=2==A?[il(t,n,i,o,T,-1),il(t,n,i,o,T,1)]:il(t,n,i,o,T,A)),z}))}(Ml,e)}}return Ol}(); diff --git a/src/Columns/ColumnConst.h b/src/Columns/ColumnConst.h index 21cfaf7f64c..99a230720a4 100644 --- a/src/Columns/ColumnConst.h +++ b/src/Columns/ColumnConst.h @@ -269,7 +269,7 @@ public: bool isFixedAndContiguous() const override { return data->isFixedAndContiguous(); } bool valuesHaveFixedSize() const override { return data->valuesHaveFixedSize(); } size_t sizeOfValueIfFixed() const override { return data->sizeOfValueIfFixed(); } - StringRef getRawData() const override { return data->getRawData(); } + std::string_view getRawData() const override { return data->getRawData(); } /// Not part of the common interface. diff --git a/src/Columns/ColumnDecimal.h b/src/Columns/ColumnDecimal.h index 03875121637..afae2cd641a 100644 --- a/src/Columns/ColumnDecimal.h +++ b/src/Columns/ColumnDecimal.h @@ -71,9 +71,9 @@ public: data.resize_assume_reserved(data.size() - n); } - StringRef getRawData() const override + std::string_view getRawData() const override { - return StringRef(reinterpret_cast(data.data()), byteSize()); + return {reinterpret_cast(data.data()), byteSize()}; } StringRef getDataAt(size_t n) const override diff --git a/src/Columns/ColumnFixedString.h b/src/Columns/ColumnFixedString.h index 711db056ee6..7c2d9b1a155 100644 --- a/src/Columns/ColumnFixedString.h +++ b/src/Columns/ColumnFixedString.h @@ -209,7 +209,7 @@ public: bool isFixedAndContiguous() const override { return true; } size_t sizeOfValueIfFixed() const override { return n; } - StringRef getRawData() const override { return StringRef(chars.data(), chars.size()); } + std::string_view getRawData() const override { return {reinterpret_cast(chars.data()), chars.size()}; } /// Specialized part of interface, not from IColumn. void insertString(const String & string) { insertData(string.c_str(), string.size()); } diff --git a/src/Columns/ColumnVector.h b/src/Columns/ColumnVector.h index 6ba9abaca32..88e953891cc 100644 --- a/src/Columns/ColumnVector.h +++ b/src/Columns/ColumnVector.h @@ -332,9 +332,9 @@ public: bool isFixedAndContiguous() const override { return true; } size_t sizeOfValueIfFixed() const override { return sizeof(T); } - StringRef getRawData() const override + std::string_view getRawData() const override { - return StringRef(reinterpret_cast(data.data()), byteSize()); + return {reinterpret_cast(data.data()), byteSize()}; } StringRef getDataAt(size_t n) const override diff --git a/src/Columns/IColumn.h b/src/Columns/IColumn.h index 8b693015674..974925d247e 100644 --- a/src/Columns/IColumn.h +++ b/src/Columns/IColumn.h @@ -507,7 +507,7 @@ public: [[nodiscard]] virtual bool isFixedAndContiguous() const { return false; } /// If isFixedAndContiguous, returns the underlying data array, otherwise throws an exception. - [[nodiscard]] virtual StringRef getRawData() const { throw Exception("Column " + getName() + " is not a contiguous block of memory", ErrorCodes::NOT_IMPLEMENTED); } + [[nodiscard]] virtual std::string_view getRawData() const { throw Exception("Column " + getName() + " is not a contiguous block of memory", ErrorCodes::NOT_IMPLEMENTED); } /// If valuesHaveFixedSize, returns size of value, otherwise throw an exception. [[nodiscard]] virtual size_t sizeOfValueIfFixed() const { throw Exception("Values of column " + getName() + " are not fixed size.", ErrorCodes::CANNOT_GET_SIZE_OF_FIELD); } diff --git a/src/Common/ColumnsHashing.h b/src/Common/ColumnsHashing.h index 0e4617345fa..54446950567 100644 --- a/src/Common/ColumnsHashing.h +++ b/src/Common/ColumnsHashing.h @@ -41,12 +41,12 @@ struct HashMethodOneNumber /// If the keys of a fixed length then key_sizes contains their lengths, empty otherwise. HashMethodOneNumber(const ColumnRawPtrs & key_columns, const Sizes & /*key_sizes*/, const HashMethodContextPtr &) { - vec = key_columns[0]->getRawData().data; + vec = key_columns[0]->getRawData().data(); } explicit HashMethodOneNumber(const IColumn * column) { - vec = column->getRawData().data; + vec = column->getRawData().data(); } /// Creates context. Method is called once and result context is used in all threads. @@ -577,7 +577,7 @@ struct HashMethodKeysFixed columns_data.reset(new const char*[keys_size]); for (size_t i = 0; i < keys_size; ++i) - columns_data[i] = Base::getActualColumns()[i]->getRawData().data; + columns_data[i] = Base::getActualColumns()[i]->getRawData().data(); } #endif } diff --git a/src/Common/DNSResolver.cpp b/src/Common/DNSResolver.cpp index 10797b7a809..cf61d2795f0 100644 --- a/src/Common/DNSResolver.cpp +++ b/src/Common/DNSResolver.cpp @@ -86,13 +86,10 @@ static void splitHostAndPort(const std::string & host_and_port, std::string & ou static DNSResolver::IPAddresses hostByName(const std::string & host) { - /// Family: AF_UNSPEC - /// AI_ALL is required for checking if client is allowed to connect from an address - auto flags = Poco::Net::DNS::DNS_HINT_AI_V4MAPPED | Poco::Net::DNS::DNS_HINT_AI_ALL; /// Do not resolve IPv6 (or IPv4) if no local IPv6 (or IPv4) addresses are configured. /// It should not affect client address checking, since client cannot connect from IPv6 address /// if server has no IPv6 addresses. - flags |= Poco::Net::DNS::DNS_HINT_AI_ADDRCONFIG; + auto flags = Poco::Net::DNS::DNS_HINT_AI_ADDRCONFIG; DNSResolver::IPAddresses addresses; diff --git a/src/Common/FileSegment.cpp b/src/Common/FileSegment.cpp index c2a12b38320..0b2e874e9ab 100644 --- a/src/Common/FileSegment.cpp +++ b/src/Common/FileSegment.cpp @@ -430,19 +430,20 @@ void FileSegment::completeBatchAndResetDownloader() cv.notify_all(); } -void FileSegment::completeWithState(State state, bool auto_resize) +void FileSegment::completeWithState(State state) { std::lock_guard cache_lock(cache->mutex); std::lock_guard segment_lock(mutex); assertNotDetached(segment_lock); - bool is_downloader = isDownloaderImpl(segment_lock); - if (!is_downloader) + auto caller_id = getCallerId(); + if (caller_id != downloader_id) { - cv.notify_all(); - throw Exception(ErrorCodes::REMOTE_FS_OBJECT_CACHE_ERROR, - "File segment can be completed only by downloader or downloader's FileSegmentsHodler"); + throw Exception( + ErrorCodes::LOGICAL_ERROR, + "File segment completion can be done only by downloader. (CallerId: {}, downloader id: {}", + caller_id, downloader_id); } if (state != State::DOWNLOADED @@ -450,140 +451,48 @@ void FileSegment::completeWithState(State state, bool auto_resize) && state != State::PARTIALLY_DOWNLOADED_NO_CONTINUATION) { cv.notify_all(); - throw Exception(ErrorCodes::REMOTE_FS_OBJECT_CACHE_ERROR, - "Cannot complete file segment with state: {}", stateToString(state)); - } - - if (state == State::DOWNLOADED) - { - if (auto_resize && downloaded_size != range().size()) - { - LOG_TEST(log, "Resize cell {} to downloaded: {}", range().toString(), downloaded_size); - assert(downloaded_size <= range().size()); - segment_range = Range(segment_range.left, segment_range.left + downloaded_size - 1); - } - - /// Update states and finalize cache write buffer. - setDownloaded(segment_lock); - - if (downloaded_size != range().size()) - throw Exception( - ErrorCodes::REMOTE_FS_OBJECT_CACHE_ERROR, - "Cannot complete file segment as DOWNLOADED, because downloaded size ({}) does not match expected size ({})", - downloaded_size, range().size()); + throw Exception( + ErrorCodes::REMOTE_FS_OBJECT_CACHE_ERROR, + "Cannot complete file segment with state: {}", stateToString(state)); } download_state = state; - - try - { - completeImpl(cache_lock, segment_lock); - } - catch (...) - { - if (!downloader_id.empty() && is_downloader) - downloader_id.clear(); - - cv.notify_all(); - throw; - } - - cv.notify_all(); + completeBasedOnCurrentState(cache_lock, segment_lock); } -void FileSegment::completeBasedOnCurrentState(std::lock_guard & cache_lock) +void FileSegment::completeWithoutState(std::lock_guard & cache_lock) { std::lock_guard segment_lock(mutex); + completeBasedOnCurrentState(cache_lock, segment_lock); +} +void FileSegment::completeBasedOnCurrentState(std::lock_guard & cache_lock, std::lock_guard & segment_lock) +{ if (is_detached) return; - assertNotDetached(segment_lock); - - completeBasedOnCurrentStateUnlocked(cache_lock, segment_lock); -} - -void FileSegment::completeBasedOnCurrentStateUnlocked( - std::lock_guard & cache_lock, std::lock_guard & segment_lock) -{ + bool is_downloader = isDownloaderImpl(segment_lock); bool is_last_holder = cache->isLastFileSegmentHolder(key(), offset(), cache_lock, segment_lock); + bool can_update_segment_state = is_downloader || is_last_holder; + size_t current_downloaded_size = getDownloadedSize(segment_lock); - if (is_last_holder && download_state == State::SKIP_CACHE) - { - cache->remove(key(), offset(), cache_lock, segment_lock); - return; - } - - if (download_state == State::SKIP_CACHE || is_detached) - return; - - if (isDownloaderImpl(segment_lock) - && download_state != State::DOWNLOADED - && getDownloadedSize(segment_lock) == range().size()) - { - setDownloaded(segment_lock); - } - - assertNotDetached(segment_lock); - - if (download_state == State::DOWNLOADING || download_state == State::EMPTY) - { - /// Segment state can be changed from DOWNLOADING or EMPTY only if the caller is the - /// downloader or the only owner of the segment. - - bool can_update_segment_state = isDownloaderImpl(segment_lock) || is_last_holder; - - if (can_update_segment_state) - download_state = State::PARTIALLY_DOWNLOADED; - } - - try - { - completeImpl(cache_lock, segment_lock); - } - catch (...) - { - if (!downloader_id.empty() && isDownloaderImpl(segment_lock)) - downloader_id.clear(); - - cv.notify_all(); - throw; - } - - cv.notify_all(); -} - -void FileSegment::completeImpl(std::lock_guard & cache_lock, std::lock_guard & segment_lock) -{ - bool is_last_holder = cache->isLastFileSegmentHolder(key(), offset(), cache_lock, segment_lock); - - if (is_last_holder - && (download_state == State::PARTIALLY_DOWNLOADED || download_state == State::PARTIALLY_DOWNLOADED_NO_CONTINUATION)) - { - size_t current_downloaded_size = getDownloadedSize(segment_lock); - if (current_downloaded_size == 0) + SCOPE_EXIT({ + if (is_downloader) { - download_state = State::SKIP_CACHE; - LOG_TEST(log, "Remove cell {} (nothing downloaded)", range().toString()); - cache->remove(key(), offset(), cache_lock, segment_lock); + cv.notify_all(); } + }); + + LOG_TEST(log, "Complete without state (is_last_holder: {}). File segment info: {}", is_last_holder, getInfoForLogImpl(segment_lock)); + + if (can_update_segment_state) + { + if (current_downloaded_size == range().size()) + setDownloaded(segment_lock); else - { - /** - * Only last holder of current file segment can resize the cell, - * because there is an invariant that file segments returned to users - * in FileSegmentsHolder represent a contiguous range, so we can resize - * it only when nobody needs it. - */ - download_state = State::PARTIALLY_DOWNLOADED_NO_CONTINUATION; - /// Resize this file segment by creating a copy file segment with DOWNLOADED state, - /// but current file segment should remain PARRTIALLY_DOWNLOADED_NO_CONTINUATION and with detached state, - /// because otherwise an invariant that getOrSet() returns a contiguous range of file segments will be broken - /// (this will be crucial for other file segment holder, not for current one). - cache->reduceSizeToDownloaded(key(), offset(), cache_lock, segment_lock); - } + download_state = State::PARTIALLY_DOWNLOADED; - markAsDetached(segment_lock); + resetDownloaderImpl(segment_lock); if (cache_writer) { @@ -593,10 +502,62 @@ void FileSegment::completeImpl(std::lock_guard & cache_lock, std::lo } } - if (!downloader_id.empty() && (isDownloaderImpl(segment_lock) || is_last_holder)) + switch (download_state) { - LOG_TEST(log, "Clearing downloader id: {}, current state: {}", downloader_id, stateToString(download_state)); - downloader_id.clear(); + case State::SKIP_CACHE: + { + if (is_last_holder) + cache->remove(key(), offset(), cache_lock, segment_lock); + + return; + } + case State::DOWNLOADED: + { + assert(downloaded_size == range().size()); + assert(is_downloaded); + break; + } + case State::DOWNLOADING: + case State::EMPTY: + { + assert(!is_last_holder); + break; + } + case State::PARTIALLY_DOWNLOADED: + case State::PARTIALLY_DOWNLOADED_NO_CONTINUATION: + { + if (is_last_holder) + { + if (current_downloaded_size == 0) + { + LOG_TEST(log, "Remove cell {} (nothing downloaded)", range().toString()); + + download_state = State::SKIP_CACHE; + cache->remove(key(), offset(), cache_lock, segment_lock); + } + else + { + LOG_TEST(log, "Resize cell {} to downloaded: {}", range().toString(), current_downloaded_size); + + /** + * Only last holder of current file segment can resize the cell, + * because there is an invariant that file segments returned to users + * in FileSegmentsHolder represent a contiguous range, so we can resize + * it only when nobody needs it. + */ + download_state = State::PARTIALLY_DOWNLOADED_NO_CONTINUATION; + + /// Resize this file segment by creating a copy file segment with DOWNLOADED state, + /// but current file segment should remain PARRTIALLY_DOWNLOADED_NO_CONTINUATION and with detached state, + /// because otherwise an invariant that getOrSet() returns a contiguous range of file segments will be broken + /// (this will be crucial for other file segment holder, not for current one). + cache->reduceSizeToDownloaded(key(), offset(), cache_lock, segment_lock); + } + + markAsDetached(segment_lock); + } + break; + } } LOG_TEST(log, "Completed file segment: {}", getInfoForLogImpl(segment_lock)); @@ -793,7 +754,7 @@ FileSegmentsHolder::~FileSegmentsHolder() /// under the same mutex, because complete() checks for segment pointers. std::lock_guard cache_lock(cache->mutex); - file_segment->completeBasedOnCurrentState(cache_lock); + file_segment->completeWithoutState(cache_lock); file_segment_it = file_segments.erase(current_file_segment_it); } @@ -859,13 +820,20 @@ void FileSegmentRangeWriter::completeFileSegment(FileSegment & file_segment) /// was initially set with a margin as `max_file_segment_size`. => We need to always /// resize to actual size after download finished. file_segment.getOrSetDownloader(); - file_segment.completeWithState(FileSegment::State::DOWNLOADED, /* auto_resize */true); + + assert(file_segment.downloaded_size <= file_segment.range().size()); + file_segment.segment_range = FileSegment::Range( + file_segment.segment_range.left, file_segment.segment_range.left + file_segment.downloaded_size - 1); + file_segment.reserved_size = file_segment.downloaded_size; + + file_segment.completeWithState(FileSegment::State::DOWNLOADED); + on_complete_file_segment_func(file_segment); } else { std::lock_guard cache_lock(cache->mutex); - file_segment.completeBasedOnCurrentState(cache_lock); + file_segment.completeWithoutState(cache_lock); } } diff --git a/src/Common/FileSegment.h b/src/Common/FileSegment.h index 4168cf8bd03..2b25cfd172e 100644 --- a/src/Common/FileSegment.h +++ b/src/Common/FileSegment.h @@ -142,7 +142,7 @@ public: void completeBatchAndResetDownloader(); - void completeWithState(State state, bool auto_resize = false); + void completeWithState(State state); String getInfoForLog() const; @@ -195,12 +195,8 @@ private: /// FileSegmentsHolder. complete() might check if the caller of the method /// is the last alive holder of the segment. Therefore, complete() and destruction /// of the file segment pointer must be done under the same cache mutex. - void completeBasedOnCurrentState(std::lock_guard & cache_lock); - void completeBasedOnCurrentStateUnlocked(std::lock_guard & cache_lock, std::lock_guard & segment_lock); - - void completeImpl( - std::lock_guard & cache_lock, - std::lock_guard & segment_lock); + void completeBasedOnCurrentState(std::lock_guard & cache_lock, std::lock_guard & segment_lock); + void completeWithoutState(std::lock_guard & cache_lock); void resetDownloaderImpl(std::lock_guard & segment_lock); diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 2b808a1ada7..718c122c2c6 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -589,7 +589,6 @@ static constexpr UInt64 operator""_GiB(unsigned long long value) M(UInt64, remote_fs_read_max_backoff_ms, 10000, "Max wait time when trying to read data for remote disk", 0) \ M(UInt64, remote_fs_read_backoff_max_tries, 5, "Max attempts to read with backoff", 0) \ M(Bool, enable_filesystem_cache, true, "Use cache for remote filesystem. This setting does not turn on/off cache for disks (must be done via disk config), but allows to bypass cache for some queries if intended", 0) \ - M(UInt64, filesystem_cache_max_wait_sec, 5, "Allow to wait at most this number of seconds for download of current remote_fs_buffer_size bytes, and skip cache if exceeded", 0) \ M(Bool, enable_filesystem_cache_on_write_operations, false, "Write into cache on write operations. To actually work this setting requires be added to disk config too", 0) \ M(Bool, enable_filesystem_cache_log, false, "Allows to record the filesystem caching log for each query", 0) \ M(Bool, read_from_filesystem_cache_if_exists_otherwise_bypass_cache, false, "", 0) \ diff --git a/src/Disks/IO/CachedOnDiskReadBufferFromFile.cpp b/src/Disks/IO/CachedOnDiskReadBufferFromFile.cpp index d3777fbcbd4..dac59c596f5 100644 --- a/src/Disks/IO/CachedOnDiskReadBufferFromFile.cpp +++ b/src/Disks/IO/CachedOnDiskReadBufferFromFile.cpp @@ -222,9 +222,6 @@ CachedOnDiskReadBufferFromFile::getReadBufferForFileSegment(FileSegmentPtr & fil { auto range = file_segment->range(); - size_t wait_download_max_tries = settings.filesystem_cache_max_wait_sec; - size_t wait_download_tries = 0; - auto download_state = file_segment->state(); LOG_TEST(log, "getReadBufferForFileSegment: {}", file_segment->getInfoForLog()); @@ -274,16 +271,7 @@ CachedOnDiskReadBufferFromFile::getReadBufferForFileSegment(FileSegmentPtr & fil return getCacheReadBuffer(range.left); } - if (wait_download_tries++ < wait_download_max_tries) - { - download_state = file_segment->wait(); - } - else - { - LOG_DEBUG(log, "Retries to wait for file segment download exceeded ({})", wait_download_tries); - download_state = FileSegment::State::SKIP_CACHE; - } - + download_state = file_segment->wait(); continue; } case FileSegment::State::DOWNLOADED: diff --git a/src/Functions/reinterpretAs.cpp b/src/Functions/reinterpretAs.cpp index 8e656863cdb..76afedb4f06 100644 --- a/src/Functions/reinterpretAs.cpp +++ b/src/Functions/reinterpretAs.cpp @@ -301,7 +301,7 @@ private: ColumnFixedString::Chars & data_to = dst.getChars(); data_to.resize(n * rows); - memcpy(data_to.data(), src.getRawData().data, data_to.size()); + memcpy(data_to.data(), src.getRawData().data(), data_to.size()); } static void NO_INLINE executeToString(const IColumn & src, ColumnString & dst) diff --git a/src/IO/HadoopSnappyReadBuffer.cpp b/src/IO/HadoopSnappyReadBuffer.cpp index 2a65ca9826b..408e76e19be 100644 --- a/src/IO/HadoopSnappyReadBuffer.cpp +++ b/src/IO/HadoopSnappyReadBuffer.cpp @@ -183,23 +183,28 @@ bool HadoopSnappyReadBuffer::nextImpl() if (eof) return false; - if (!in_available) + do { - in->nextIfAtEnd(); - in_available = in->buffer().end() - in->position(); - in_data = in->position(); + if (!in_available) + { + in->nextIfAtEnd(); + in_available = in->buffer().end() - in->position(); + in_data = in->position(); + } + + if (decoder->result == Status::NEEDS_MORE_INPUT && (!in_available || in->eof())) + { + throw Exception(String("hadoop snappy decode error:") + statusToString(decoder->result), ErrorCodes::SNAPPY_UNCOMPRESS_FAILED); + } + + out_capacity = internal_buffer.size(); + out_data = internal_buffer.begin(); + decoder->result = decoder->readBlock(&in_available, &in_data, &out_capacity, &out_data); + + in->position() = in->buffer().end() - in_available; } + while (decoder->result == Status::NEEDS_MORE_INPUT); - if (decoder->result == Status::NEEDS_MORE_INPUT && (!in_available || in->eof())) - { - throw Exception(String("hadoop snappy decode error:") + statusToString(decoder->result), ErrorCodes::SNAPPY_UNCOMPRESS_FAILED); - } - - out_capacity = internal_buffer.size(); - out_data = internal_buffer.begin(); - decoder->result = decoder->readBlock(&in_available, &in_data, &out_capacity, &out_data); - - in->position() = in->buffer().end() - in_available; working_buffer.resize(internal_buffer.size() - out_capacity); if (decoder->result == Status::OK) diff --git a/src/IO/ReadSettings.h b/src/IO/ReadSettings.h index 199ae4dcf7f..e639ecbedc2 100644 --- a/src/IO/ReadSettings.h +++ b/src/IO/ReadSettings.h @@ -80,7 +80,6 @@ struct ReadSettings size_t remote_fs_read_backoff_max_tries = 4; bool enable_filesystem_cache = true; - size_t filesystem_cache_max_wait_sec = 1; bool read_from_filesystem_cache_if_exists_otherwise_bypass_cache = false; bool enable_filesystem_cache_log = false; bool is_file_cache_persistent = false; /// Some files can be made non-evictable. diff --git a/src/IO/tests/gtest_hadoop_snappy_decoder.cpp b/src/IO/tests/gtest_hadoop_snappy_decoder.cpp index f681e8e61e1..4db0deac08e 100644 --- a/src/IO/tests/gtest_hadoop_snappy_decoder.cpp +++ b/src/IO/tests/gtest_hadoop_snappy_decoder.cpp @@ -60,7 +60,8 @@ TEST(HadoopSnappyDecoder, repeatNeedMoreInput) String output; WriteBufferFromString out(output); copyData(read_buffer, out); + out.finalize(); UInt128 hashcode = sipHash128(output.c_str(), output.size()); String hashcode_str = getHexUIntLowercase(hashcode); - ASSERT_EQ(hashcode_str, "593afe14f61866915cc00b8c7bd86046"); + ASSERT_EQ(hashcode_str, "673e5b065186cec146789451c2a8f703"); } diff --git a/src/Interpreters/Context.cpp b/src/Interpreters/Context.cpp index 55ac93f7e7b..6335748cdcf 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -3451,7 +3451,6 @@ ReadSettings Context::getReadSettings() const res.remote_fs_read_max_backoff_ms = settings.remote_fs_read_max_backoff_ms; res.remote_fs_read_backoff_max_tries = settings.remote_fs_read_backoff_max_tries; res.enable_filesystem_cache = settings.enable_filesystem_cache; - res.filesystem_cache_max_wait_sec = settings.filesystem_cache_max_wait_sec; res.read_from_filesystem_cache_if_exists_otherwise_bypass_cache = settings.read_from_filesystem_cache_if_exists_otherwise_bypass_cache; res.enable_filesystem_cache_log = settings.enable_filesystem_cache_log; res.enable_filesystem_cache_on_lower_level = settings.enable_filesystem_cache_on_lower_level; diff --git a/src/Interpreters/ExpressionJIT.cpp b/src/Interpreters/ExpressionJIT.cpp index a179c4d8bf6..c37d4d5b6a2 100644 --- a/src/Interpreters/ExpressionJIT.cpp +++ b/src/Interpreters/ExpressionJIT.cpp @@ -113,14 +113,14 @@ public: const auto & null_map_column = nullable_column->getNullMapColumn(); auto nested_column_raw_data = nested_column.getRawData(); - __msan_unpoison(nested_column_raw_data.data, nested_column_raw_data.size); + __msan_unpoison(nested_column_raw_data.data(), nested_column_raw_data.size()); auto null_map_column_raw_data = null_map_column.getRawData(); - __msan_unpoison(null_map_column_raw_data.data, null_map_column_raw_data.size); + __msan_unpoison(null_map_column_raw_data.data(), null_map_column_raw_data.size()); } else { - __msan_unpoison(result_column->getRawData().data, result_column->getRawData().size); + __msan_unpoison(result_column->getRawData().data(), result_column->getRawData().size()); } #endif diff --git a/src/Interpreters/JIT/compileFunction.cpp b/src/Interpreters/JIT/compileFunction.cpp index 9d2ab40bf76..353ab84674c 100644 --- a/src/Interpreters/JIT/compileFunction.cpp +++ b/src/Interpreters/JIT/compileFunction.cpp @@ -47,11 +47,11 @@ ColumnData getColumnData(const IColumn * column) if (const auto * nullable = typeid_cast(column)) { - result.null_data = nullable->getNullMapColumn().getRawData().data; + result.null_data = nullable->getNullMapColumn().getRawData().data(); column = & nullable->getNestedColumn(); } - result.data = column->getRawData().data; + result.data = column->getRawData().data(); return result; } diff --git a/src/Parsers/ASTQueryWithOutput.cpp b/src/Parsers/ASTQueryWithOutput.cpp index 95bcaaad416..6db011417a6 100644 --- a/src/Parsers/ASTQueryWithOutput.cpp +++ b/src/Parsers/ASTQueryWithOutput.cpp @@ -1,4 +1,5 @@ #include +#include namespace DB { @@ -40,7 +41,7 @@ void ASTQueryWithOutput::formatImpl(const FormatSettings & s, FormatState & stat format->formatImpl(s, state, frame); } - if (settings_ast) + if (settings_ast && assert_cast(settings_ast.get())->print_in_format) { s.ostr << (s.hilite ? hilite_keyword : "") << s.nl_or_ws << indent_str << "SETTINGS " << (s.hilite ? hilite_none : ""); settings_ast->formatImpl(s, state, frame); diff --git a/src/Parsers/ASTSelectQuery.cpp b/src/Parsers/ASTSelectQuery.cpp index 4e84e1d1e20..76849653b4e 100644 --- a/src/Parsers/ASTSelectQuery.cpp +++ b/src/Parsers/ASTSelectQuery.cpp @@ -192,7 +192,7 @@ void ASTSelectQuery::formatImpl(const FormatSettings & s, FormatState & state, F limitOffset()->formatImpl(s, state, frame); } - if (settings()) + if (settings() && assert_cast(settings().get())->print_in_format) { s.ostr << (s.hilite ? hilite_keyword : "") << s.nl_or_ws << indent_str << "SETTINGS " << (s.hilite ? hilite_none : ""); settings()->formatImpl(s, state, frame); diff --git a/src/Parsers/ASTSetQuery.h b/src/Parsers/ASTSetQuery.h index 4e3d9d227b6..2c79ea18359 100644 --- a/src/Parsers/ASTSetQuery.h +++ b/src/Parsers/ASTSetQuery.h @@ -14,6 +14,12 @@ class ASTSetQuery : public IAST public: bool is_standalone = true; /// If false, this AST is a part of another query, such as SELECT. + /// To support overriding certain settings in a **subquery**, we add a ASTSetQuery with Settings to all subqueries, containing + /// the list of all settings that affect them (specifically or globally to the whole query). + /// We use `print_in_format` to avoid printing these nodes when they were left unchanged from the parent copy + /// See more: https://github.com/ClickHouse/ClickHouse/issues/38895 + bool print_in_format = true; + SettingsChanges changes; NameToNameMap query_parameters; diff --git a/src/Parsers/ParserQueryWithOutput.cpp b/src/Parsers/ParserQueryWithOutput.cpp index 6107bd2a5eb..163e71e3201 100644 --- a/src/Parsers/ParserQueryWithOutput.cpp +++ b/src/Parsers/ParserQueryWithOutput.cpp @@ -142,7 +142,9 @@ bool ParserQueryWithOutput::parseImpl(Pos & pos, ASTPtr & node, Expected & expec // Pass them manually, to apply in InterpreterSelectQuery::initSettings() if (query->as()) { - QueryWithOutputSettingsPushDownVisitor::Data data{query_with_output.settings_ast}; + auto settings = query_with_output.settings_ast->clone(); + assert_cast(settings.get())->print_in_format = false; + QueryWithOutputSettingsPushDownVisitor::Data data{settings}; QueryWithOutputSettingsPushDownVisitor(data).visit(query); } } diff --git a/src/Server/HTTPHandlerFactory.cpp b/src/Server/HTTPHandlerFactory.cpp index 526b86a5c28..6659c9b8390 100644 --- a/src/Server/HTTPHandlerFactory.cpp +++ b/src/Server/HTTPHandlerFactory.cpp @@ -169,10 +169,20 @@ void addCommonDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IS replicas_status_handler->allowGetAndHeadRequest(); factory.addHandler(replicas_status_handler); - auto web_ui_handler = std::make_shared>(server, "play.html"); - web_ui_handler->attachNonStrictPath("/play"); - web_ui_handler->allowGetAndHeadRequest(); - factory.addHandler(web_ui_handler); + auto play_handler = std::make_shared>(server); + play_handler->attachNonStrictPath("/play"); + play_handler->allowGetAndHeadRequest(); + factory.addHandler(play_handler); + + auto dashboard_handler = std::make_shared>(server); + dashboard_handler->attachNonStrictPath("/dashboard"); + dashboard_handler->allowGetAndHeadRequest(); + factory.addHandler(dashboard_handler); + + auto js_handler = std::make_shared>(server); + js_handler->attachNonStrictPath("/js/"); + js_handler->allowGetAndHeadRequest(); + factory.addHandler(js_handler); } void addDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IServer & server, AsynchronousMetrics & async_metrics) diff --git a/src/Server/WebUIRequestHandler.cpp b/src/Server/WebUIRequestHandler.cpp index 50aa0be4778..3997e0f19b6 100644 --- a/src/Server/WebUIRequestHandler.cpp +++ b/src/Server/WebUIRequestHandler.cpp @@ -8,12 +8,14 @@ #include #include +#include + namespace DB { -WebUIRequestHandler::WebUIRequestHandler(IServer & server_, std::string resource_name_) - : server(server_), resource_name(std::move(resource_name_)) +WebUIRequestHandler::WebUIRequestHandler(IServer & server_) + : server(server_) { } @@ -28,8 +30,38 @@ void WebUIRequestHandler::handleRequest(HTTPServerRequest & request, HTTPServerR response.setChunkedTransferEncoding(true); setResponseDefaultHeaders(response, keep_alive_timeout); - response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_OK); - *response.send() << getResource(resource_name); + + if (request.getURI().starts_with("/play")) + { + response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_OK); + *response.send() << getResource("play.html"); + } + else if (request.getURI().starts_with("/dashboard")) + { + response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_OK); + + std::string html(getResource("dashboard.html")); + + /// Replace a link to external JavaScript file to embedded file. + /// This allows to open the HTML without running a server and to host it on server. + /// Note: we can embed the JavaScript file inline to the HTML, + /// but we don't do it to keep the "view-source" perfectly readable. + + static re2::RE2 uplot_url = R"(https://[^\s"'`]+u[Pp]lot[^\s"'`]*\.js)"; + RE2::Replace(&html, uplot_url, "/js/uplot.js"); + + *response.send() << html; + } + else if (request.getURI() == "/js/uplot.js") + { + response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_OK); + *response.send() << getResource("js/uplot.js"); + } + else + { + response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_NOT_FOUND); + *response.send() << "Not found.\n"; + } } } diff --git a/src/Server/WebUIRequestHandler.h b/src/Server/WebUIRequestHandler.h index 1c52b626091..09fe62d41c3 100644 --- a/src/Server/WebUIRequestHandler.h +++ b/src/Server/WebUIRequestHandler.h @@ -13,11 +13,10 @@ class WebUIRequestHandler : public HTTPRequestHandler { private: IServer & server; - std::string resource_name; + public: - WebUIRequestHandler(IServer & server_, std::string resource_name_); + WebUIRequestHandler(IServer & server_); void handleRequest(HTTPServerRequest & request, HTTPServerResponse & response) override; }; } - diff --git a/tests/jepsen.clickhouse-keeper/src/jepsen/clickhouse_keeper/utils.clj b/tests/jepsen.clickhouse-keeper/src/jepsen/clickhouse_keeper/utils.clj index 514334e677c..cdb25ba0a2d 100644 --- a/tests/jepsen.clickhouse-keeper/src/jepsen/clickhouse_keeper/utils.clj +++ b/tests/jepsen.clickhouse-keeper/src/jepsen/clickhouse_keeper/utils.clj @@ -196,13 +196,14 @@ [url] (let [encoded-url (md5 url) expected-file-name (.getName (io/file url)) - dest-file (str binaries-cache-dir "/" encoded-url) + dest-folder (str binaries-cache-dir "/" encoded-url) + dest-file (str dest-folder "/clickhouse") dest-symlink (str common-prefix "/" expected-file-name) wget-opts (concat cu/std-wget-opts [:-O dest-file])] (when-not (cu/exists? dest-file) (info "Downloading" url) - (do (c/exec :mkdir :-p binaries-cache-dir) - (c/cd binaries-cache-dir + (do (c/exec :mkdir :-p dest-folder) + (c/cd dest-folder (cu/wget-helper! wget-opts url)))) (c/exec :rm :-rf dest-symlink) (c/exec :ln :-s dest-file dest-symlink) diff --git a/tests/queries/0_stateless/02353_format_settings.reference b/tests/queries/0_stateless/02353_format_settings.reference new file mode 100644 index 00000000000..8bd61bbd7cd --- /dev/null +++ b/tests/queries/0_stateless/02353_format_settings.reference @@ -0,0 +1,17 @@ +SELECT 1 +FORMAT CSV +SETTINGS max_execution_time = 0.001 +SELECT 1 +SETTINGS max_execution_time = 0.001 +FORMAT CSV +SELECT 1 +UNION ALL +SELECT 2 +FORMAT CSV +SETTINGS max_execution_time = 0.001 +SELECT 1 +SETTINGS max_threads = 1 +UNION ALL +SELECT 2 +SETTINGS max_execution_time = 2 +FORMAT `Null` diff --git a/tests/queries/0_stateless/02353_format_settings.sh b/tests/queries/0_stateless/02353_format_settings.sh new file mode 100755 index 00000000000..6d65f143633 --- /dev/null +++ b/tests/queries/0_stateless/02353_format_settings.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CUR_DIR"/../shell_config.sh + +set -e + +format="$CLICKHOUSE_FORMAT" + +echo "select 1 format CSV settings max_execution_time = 0.001" | $format +echo "select 1 settings max_execution_time = 0.001 format CSV" | $format +echo "select 1 UNION ALL Select 2 format CSV settings max_execution_time = 0.001" | $format + +# I don't think having multiple settings makes sense, but it's supported so test that it still works +echo "select 1 settings max_threads=1 UNION ALL select 2 settings max_execution_time=2 format Null" | $format diff --git a/tests/queries/0_stateless/02389_dashboard.reference b/tests/queries/0_stateless/02389_dashboard.reference new file mode 100644 index 00000000000..bcde69ce24a --- /dev/null +++ b/tests/queries/0_stateless/02389_dashboard.reference @@ -0,0 +1,2 @@ +🌚 +leeoniya diff --git a/tests/queries/0_stateless/02389_dashboard.sh b/tests/queries/0_stateless/02389_dashboard.sh new file mode 100755 index 00000000000..9250663e3e8 --- /dev/null +++ b/tests/queries/0_stateless/02389_dashboard.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +${CLICKHOUSE_CURL} -sS "${CLICKHOUSE_PORT_HTTP_PROTO}://${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT_HTTP}/dashboard" | grep -oF '🌚' +${CLICKHOUSE_CURL} -sS "${CLICKHOUSE_PORT_HTTP_PROTO}://${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT_HTTP}/js/uplot.js" | grep -oF 'leeoniya'