From 94167c1ed77b63200fd9098fece26f252cb3203b Mon Sep 17 00:00:00 2001 From: OlivierCavadenti Date: Tue, 18 Jul 2023 07:21:29 +0000 Subject: [PATCH] deploy: 67306e9b5d65f66ffc262beef4863b65a4139ea3 --- .nojekyll | 0 CNAME | 1 + assets/app.ef097145.js | 1 + assets/changelog.md.997a0d36.js | 1 + assets/changelog.md.997a0d36.lean.js | 1 + assets/chunks/AlgoliaSearchBox.3caa7b39.js | 1 + assets/chunks/AlgoliaSearchBox.8f0df019.js | 1 + assets/chunks/index.d76f8212.js | 13 + assets/faq_index.md.98b8e957.js | 5 + assets/faq_index.md.98b8e957.lean.js | 1 + assets/faq_recipes.md.71463d9b.js | 132 ++ assets/faq_recipes.md.71463d9b.lean.js | 1 + assets/faq_support.md.3a43ae3b.js | 1 + assets/faq_support.md.3a43ae3b.lean.js | 1 + assets/guide_extending.md.c10412b7.js | 34 + assets/guide_extending.md.c10412b7.lean.js | 1 + assets/guide_index.md.895872e8.js | 357 ++++ assets/guide_index.md.895872e8.lean.js | 1 + assets/guide_interfaces.md.da877da3.js | 160 ++ assets/guide_interfaces.md.da877da3.lean.js | 1 + assets/guide_migrations.md.8f3715e4.js | 292 ++++ assets/guide_migrations.md.8f3715e4.lean.js | 1 + assets/guide_query-builder.md.c2c99abe.js | 1412 ++++++++++++++++ .../guide_query-builder.md.c2c99abe.lean.js | 1 + assets/guide_raw.md.bcfb9bab.js | 63 + assets/guide_raw.md.bcfb9bab.lean.js | 1 + assets/guide_ref.md.0b35ab7a.js | 1 + assets/guide_ref.md.0b35ab7a.lean.js | 1 + assets/guide_schema-builder.md.1a118d1d.js | 263 +++ .../guide_schema-builder.md.1a118d1d.lean.js | 1 + assets/guide_transactions.md.1e8e7cd5.js | 176 ++ assets/guide_transactions.md.1e8e7cd5.lean.js | 1 + assets/guide_utility.md.bc42b63a.js | 24 + assets/guide_utility.md.bc42b63a.lean.js | 1 + assets/index.md.0854ee54.js | 1 + assets/index.md.0854ee54.lean.js | 1 + assets/style.fcb294ee.css | 1 + changelog.html | 24 + faq/index.html | 28 + faq/recipes.html | 155 ++ faq/support.html | 24 + guide/extending.html | 57 + guide/index.html | 380 +++++ guide/interfaces.html | 183 +++ guide/migrations.html | 315 ++++ guide/query-builder.html | 1435 +++++++++++++++++ guide/raw.html | 86 + guide/ref.html | 51 + guide/schema-builder.html | 286 ++++ guide/transactions.html | 199 +++ guide/utility.html | 47 + hashmap.json | 1 + index.html | 24 + knex-logo.png | Bin 0 -> 9640 bytes 54 files changed, 6250 insertions(+) create mode 100644 .nojekyll create mode 100644 CNAME create mode 100644 assets/app.ef097145.js create mode 100644 assets/changelog.md.997a0d36.js create mode 100644 assets/changelog.md.997a0d36.lean.js create mode 100644 assets/chunks/AlgoliaSearchBox.3caa7b39.js create mode 100644 assets/chunks/AlgoliaSearchBox.8f0df019.js create mode 100644 assets/chunks/index.d76f8212.js create mode 100644 assets/faq_index.md.98b8e957.js create mode 100644 assets/faq_index.md.98b8e957.lean.js create mode 100644 assets/faq_recipes.md.71463d9b.js create mode 100644 assets/faq_recipes.md.71463d9b.lean.js create mode 100644 assets/faq_support.md.3a43ae3b.js create mode 100644 assets/faq_support.md.3a43ae3b.lean.js create mode 100644 assets/guide_extending.md.c10412b7.js create mode 100644 assets/guide_extending.md.c10412b7.lean.js create mode 100644 assets/guide_index.md.895872e8.js create mode 100644 assets/guide_index.md.895872e8.lean.js create mode 100644 assets/guide_interfaces.md.da877da3.js create mode 100644 assets/guide_interfaces.md.da877da3.lean.js create mode 100644 assets/guide_migrations.md.8f3715e4.js create mode 100644 assets/guide_migrations.md.8f3715e4.lean.js create mode 100644 assets/guide_query-builder.md.c2c99abe.js create mode 100644 assets/guide_query-builder.md.c2c99abe.lean.js create mode 100644 assets/guide_raw.md.bcfb9bab.js create mode 100644 assets/guide_raw.md.bcfb9bab.lean.js create mode 100644 assets/guide_ref.md.0b35ab7a.js create mode 100644 assets/guide_ref.md.0b35ab7a.lean.js create mode 100644 assets/guide_schema-builder.md.1a118d1d.js create mode 100644 assets/guide_schema-builder.md.1a118d1d.lean.js create mode 100644 assets/guide_transactions.md.1e8e7cd5.js create mode 100644 assets/guide_transactions.md.1e8e7cd5.lean.js create mode 100644 assets/guide_utility.md.bc42b63a.js create mode 100644 assets/guide_utility.md.bc42b63a.lean.js create mode 100644 assets/index.md.0854ee54.js create mode 100644 assets/index.md.0854ee54.lean.js create mode 100644 assets/style.fcb294ee.css create mode 100644 changelog.html create mode 100644 faq/index.html create mode 100644 faq/recipes.html create mode 100644 faq/support.html create mode 100644 guide/extending.html create mode 100644 guide/index.html create mode 100644 guide/interfaces.html create mode 100644 guide/migrations.html create mode 100644 guide/query-builder.html create mode 100644 guide/raw.html create mode 100644 guide/ref.html create mode 100644 guide/schema-builder.html create mode 100644 guide/transactions.html create mode 100644 guide/utility.html create mode 100644 hashmap.json create mode 100644 index.html create mode 100644 knex-logo.png diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/CNAME b/CNAME new file mode 100644 index 00000000..6191b26f --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +knexjs.org diff --git a/assets/app.ef097145.js b/assets/app.ef097145.js new file mode 100644 index 00000000..f881a7db --- /dev/null +++ b/assets/app.ef097145.js @@ -0,0 +1 @@ +const hd={},ai="modulepreload",Xs={},ui="/",vs=function(t,n){return!n||n.length===0?t():Promise.all(n.map(s=>{if(s=`${ui}${s}`,s in Xs)return;Xs[s]=!0;const r=s.endsWith(".css"),o=r?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${s}"]${o}`))return;const i=document.createElement("link");if(i.rel=r?"stylesheet":ai,r||(i.as="script",i.crossOrigin=""),i.href=s,document.head.appendChild(i),r)return new Promise((l,c)=>{i.addEventListener("load",l),i.addEventListener("error",()=>c(new Error(`Unable to preload CSS for ${s}`)))})})).then(()=>t())};function bs(e,t){const n=Object.create(null),s=e.split(",");for(let r=0;r!!n[r.toLowerCase()]:r=>!!n[r]}const fi="itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly",di=bs(fi);function qr(e){return!!e||e===""}function ys(e){if(N(e)){const t={};for(let n=0;n{if(n){const s=n.split(pi);s.length>1&&(t[s[0].trim()]=s[1].trim())}}),t}function mt(e){let t="";if(me(e))t=e;else if(N(e))for(let n=0;nLn(n,t))}const Ce=e=>me(e)?e:e==null?"":N(e)||ae(e)&&(e.toString===zr||!K(e.toString))?JSON.stringify(e,Wr,2):String(e),Wr=(e,t)=>t&&t.__v_isRef?Wr(e,t.value):At(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[s,r])=>(n[`${s} =>`]=r,n),{})}:Sn(t)?{[`Set(${t.size})`]:[...t.values()]}:ae(t)&&!N(t)&&!Zr(t)?String(t):t,ne={},Tt=[],Ne=()=>{},vi=()=>!1,bi=/^on[^a-z]/,Gt=e=>bi.test(e),xs=e=>e.startsWith("onUpdate:"),be=Object.assign,ws=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},yi=Object.prototype.hasOwnProperty,Y=(e,t)=>yi.call(e,t),N=Array.isArray,At=e=>Pn(e)==="[object Map]",Sn=e=>Pn(e)==="[object Set]",Gs=e=>e instanceof Date,K=e=>typeof e=="function",me=e=>typeof e=="string",Cs=e=>typeof e=="symbol",ae=e=>e!==null&&typeof e=="object",Vr=e=>ae(e)&&K(e.then)&&K(e.catch),zr=Object.prototype.toString,Pn=e=>zr.call(e),xi=e=>Pn(e).slice(8,-1),Zr=e=>Pn(e)==="[object Object]",ks=e=>me(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Ut=bs(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),In=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},wi=/-(\w)/g,Ke=In(e=>e.replace(wi,(t,n)=>n?n.toUpperCase():"")),Ci=/\B([A-Z])/g,bt=In(e=>e.replace(Ci,"-$1").toLowerCase()),Mn=In(e=>e.charAt(0).toUpperCase()+e.slice(1)),Zn=In(e=>e?`on${Mn(e)}`:""),zt=(e,t)=>!Object.is(e,t),hn=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},$s=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let er;const ki=()=>er||(er=typeof globalThis!="undefined"?globalThis:typeof self!="undefined"?self:typeof window!="undefined"?window:typeof global!="undefined"?global:{});let De;class $i{constructor(t=!1){this.active=!0,this.effects=[],this.cleanups=[],!t&&De&&(this.parent=De,this.index=(De.scopes||(De.scopes=[])).push(this)-1)}run(t){if(this.active){const n=De;try{return De=this,t()}finally{De=n}}}on(){De=this}off(){De=this.parent}stop(t){if(this.active){let n,s;for(n=0,s=this.effects.length;n{const t=new Set(e);return t.w=0,t.n=0,t},Yr=e=>(e.w&it)>0,Jr=e=>(e.n&it)>0,Ti=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let n=0;for(let s=0;s{(u==="length"||u>=s)&&l.push(c)});else switch(n!==void 0&&l.push(i.get(n)),t){case"add":N(e)?ks(n)&&l.push(i.get("length")):(l.push(i.get(pt)),At(e)&&l.push(i.get(os)));break;case"delete":N(e)||(l.push(i.get(pt)),At(e)&&l.push(i.get(os)));break;case"set":At(e)&&l.push(i.get(pt));break}if(l.length===1)l[0]&&is(l[0]);else{const c=[];for(const u of l)u&&c.push(...u);is(Es(c))}}function is(e,t){for(const n of N(e)?e:[...e])(n!==Fe||n.allowRecurse)&&(n.scheduler?n.scheduler():n.run())}const Li=bs("__proto__,__v_isRef,__isVue"),Gr=new Set(Object.getOwnPropertyNames(Symbol).map(e=>Symbol[e]).filter(Cs)),Si=As(),Pi=As(!1,!0),Ii=As(!0),nr=Mi();function Mi(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const s=X(this);for(let o=0,i=this.length;o{e[t]=function(...n){Ot();const s=X(this)[t].apply(this,n);return Ft(),s}}),e}function As(e=!1,t=!1){return function(s,r,o){if(r==="__v_isReactive")return!e;if(r==="__v_isReadonly")return e;if(r==="__v_isShallow")return t;if(r==="__v_raw"&&o===(e?t?Yi:ro:t?so:no).get(s))return s;const i=N(s);if(!e&&i&&Y(nr,r))return Reflect.get(nr,r,o);const l=Reflect.get(s,r,o);return(Cs(r)?Gr.has(r):Li(r))||(e||Ae(s,"get",r),t)?l:_e(l)?!i||!ks(r)?l.value:l:ae(l)?e?oo(l):Fn(l):l}}const Oi=eo(),Fi=eo(!0);function eo(e=!1){return function(n,s,r,o){let i=n[s];if(Zt(i)&&_e(i)&&!_e(r))return!1;if(!e&&!Zt(r)&&(io(r)||(r=X(r),i=X(i)),!N(n)&&_e(i)&&!_e(r)))return i.value=r,!0;const l=N(n)&&ks(s)?Number(s)e,On=e=>Reflect.getPrototypeOf(e);function on(e,t,n=!1,s=!1){e=e.__v_raw;const r=X(e),o=X(t);t!==o&&!n&&Ae(r,"get",t),!n&&Ae(r,"get",o);const{has:i}=On(r),l=s?Ls:n?Is:Yt;if(i.call(r,t))return l(e.get(t));if(i.call(r,o))return l(e.get(o));e!==r&&e.get(t)}function ln(e,t=!1){const n=this.__v_raw,s=X(n),r=X(e);return e!==r&&!t&&Ae(s,"has",e),!t&&Ae(s,"has",r),e===r?n.has(e):n.has(e)||n.has(r)}function cn(e,t=!1){return e=e.__v_raw,!t&&Ae(X(e),"iterate",pt),Reflect.get(e,"size",e)}function sr(e){e=X(e);const t=X(this);return On(t).has.call(t,e)||(t.add(e),ze(t,"add",e,e)),this}function rr(e,t){t=X(t);const n=X(this),{has:s,get:r}=On(n);let o=s.call(n,e);o||(e=X(e),o=s.call(n,e));const i=r.call(n,e);return n.set(e,t),o?zt(t,i)&&ze(n,"set",e,t):ze(n,"add",e,t),this}function or(e){const t=X(this),{has:n,get:s}=On(t);let r=n.call(t,e);r||(e=X(e),r=n.call(t,e)),s&&s.call(t,e);const o=t.delete(e);return r&&ze(t,"delete",e,void 0),o}function ir(){const e=X(this),t=e.size!==0,n=e.clear();return t&&ze(e,"clear",void 0,void 0),n}function an(e,t){return function(s,r){const o=this,i=o.__v_raw,l=X(i),c=t?Ls:e?Is:Yt;return!e&&Ae(l,"iterate",pt),i.forEach((u,d)=>s.call(r,c(u),c(d),o))}}function un(e,t,n){return function(...s){const r=this.__v_raw,o=X(r),i=At(o),l=e==="entries"||e===Symbol.iterator&&i,c=e==="keys"&&i,u=r[e](...s),d=n?Ls:t?Is:Yt;return!t&&Ae(o,"iterate",c?os:pt),{next(){const{value:p,done:_}=u.next();return _?{value:p,done:_}:{value:l?[d(p[0]),d(p[1])]:d(p),done:_}},[Symbol.iterator](){return this}}}}function Xe(e){return function(...t){return e==="delete"?!1:this}}function Ui(){const e={get(o){return on(this,o)},get size(){return cn(this)},has:ln,add:sr,set:rr,delete:or,clear:ir,forEach:an(!1,!1)},t={get(o){return on(this,o,!1,!0)},get size(){return cn(this)},has:ln,add:sr,set:rr,delete:or,clear:ir,forEach:an(!1,!0)},n={get(o){return on(this,o,!0)},get size(){return cn(this,!0)},has(o){return ln.call(this,o,!0)},add:Xe("add"),set:Xe("set"),delete:Xe("delete"),clear:Xe("clear"),forEach:an(!0,!1)},s={get(o){return on(this,o,!0,!0)},get size(){return cn(this,!0)},has(o){return ln.call(this,o,!0)},add:Xe("add"),set:Xe("set"),delete:Xe("delete"),clear:Xe("clear"),forEach:an(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(o=>{e[o]=un(o,!1,!1),n[o]=un(o,!0,!1),t[o]=un(o,!1,!0),s[o]=un(o,!0,!0)}),[e,n,t,s]}const[ji,Ki,qi,Wi]=Ui();function Ss(e,t){const n=t?e?Wi:qi:e?Ki:ji;return(s,r,o)=>r==="__v_isReactive"?!e:r==="__v_isReadonly"?e:r==="__v_raw"?s:Reflect.get(Y(n,r)&&r in s?n:s,r,o)}const Vi={get:Ss(!1,!1)},zi={get:Ss(!1,!0)},Zi={get:Ss(!0,!1)},no=new WeakMap,so=new WeakMap,ro=new WeakMap,Yi=new WeakMap;function Ji(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function Qi(e){return e.__v_skip||!Object.isExtensible(e)?0:Ji(xi(e))}function Fn(e){return Zt(e)?e:Ps(e,!1,to,Vi,no)}function Xi(e){return Ps(e,!1,Di,zi,so)}function oo(e){return Ps(e,!0,Bi,Zi,ro)}function Ps(e,t,n,s,r){if(!ae(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const o=r.get(e);if(o)return o;const i=Qi(e);if(i===0)return e;const l=new Proxy(e,i===2?s:n);return r.set(e,l),l}function Lt(e){return Zt(e)?Lt(e.__v_raw):!!(e&&e.__v_isReactive)}function Zt(e){return!!(e&&e.__v_isReadonly)}function io(e){return!!(e&&e.__v_isShallow)}function lo(e){return Lt(e)||Zt(e)}function X(e){const t=e&&e.__v_raw;return t?X(t):e}function jt(e){return _n(e,"__v_skip",!0),e}const Yt=e=>ae(e)?Fn(e):e,Is=e=>ae(e)?oo(e):e;function co(e){rt&&Fe&&(e=X(e),Xr(e.dep||(e.dep=Es())))}function ao(e,t){e=X(e),e.dep&&is(e.dep)}function _e(e){return!!(e&&e.__v_isRef===!0)}function je(e){return uo(e,!1)}function Gi(e){return uo(e,!0)}function uo(e,t){return _e(e)?e:new el(e,t)}class el{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:X(t),this._value=n?t:Yt(t)}get value(){return co(this),this._value}set value(t){t=this.__v_isShallow?t:X(t),zt(t,this._rawValue)&&(this._rawValue=t,this._value=this.__v_isShallow?t:Yt(t),ao(this))}}function x(e){return _e(e)?e.value:e}const tl={get:(e,t,n)=>x(Reflect.get(e,t,n)),set:(e,t,n,s)=>{const r=e[t];return _e(r)&&!_e(n)?(r.value=n,!0):Reflect.set(e,t,n,s)}};function fo(e){return Lt(e)?e:new Proxy(e,tl)}function ho(e){const t=N(e)?new Array(e.length):{};for(const n in e)t[n]=sl(e,n);return t}class nl{constructor(t,n,s){this._object=t,this._key=n,this._defaultValue=s,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}}function sl(e,t,n){const s=e[t];return _e(s)?s:new nl(e,t,n)}class rl{constructor(t,n,s,r){this._setter=n,this.dep=void 0,this.__v_isRef=!0,this._dirty=!0,this.effect=new Ts(t,()=>{this._dirty||(this._dirty=!0,ao(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!r,this.__v_isReadonly=s}get value(){const t=X(this);return co(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}function ol(e,t,n=!1){let s,r;const o=K(e);return o?(s=e,r=Ne):(s=e.get,r=e.set),new rl(s,r,o||!r,n)}function ot(e,t,n,s){let r;try{r=s?e(...s):e()}catch(o){en(o,t,n)}return r}function Pe(e,t,n,s){if(K(e)){const o=ot(e,t,n,s);return o&&Vr(o)&&o.catch(i=>{en(i,t,n)}),o}const r=[];for(let o=0;o>>1;Jt(Ee[s])Ve&&Ee.splice(t,1)}function go(e,t,n,s){N(e)?n.push(...e):(!t||!t.includes(e,e.allowRecurse?s+1:s))&&n.push(e),_o()}function al(e){go(e,Dt,Kt,$t)}function ul(e){go(e,nt,qt,Et)}function Fs(e,t=null){if(Kt.length){for(cs=t,Dt=[...new Set(Kt)],Kt.length=0,$t=0;$tJt(n)-Jt(s)),Et=0;Ete.id==null?1/0:e.id;function mo(e){ls=!1,gn=!0,Fs(e),Ee.sort((n,s)=>Jt(n)-Jt(s));const t=Ne;try{for(Ve=0;Vek.trim()):p&&(r=n.map($s))}let l,c=s[l=Zn(t)]||s[l=Zn(Ke(t))];!c&&o&&(c=s[l=Zn(bt(t))]),c&&Pe(c,e,6,r);const u=s[l+"Once"];if(u){if(!e.emitted)e.emitted={};else if(e.emitted[l])return;e.emitted[l]=!0,Pe(u,e,6,r)}}function vo(e,t,n=!1){const s=t.emitsCache,r=s.get(e);if(r!==void 0)return r;const o=e.emits;let i={},l=!1;if(!K(e)){const c=u=>{const d=vo(u,t,!0);d&&(l=!0,be(i,d))};!n&&t.mixins.length&&t.mixins.forEach(c),e.extends&&c(e.extends),e.mixins&&e.mixins.forEach(c)}return!o&&!l?(s.set(e,null),null):(N(o)?o.forEach(c=>i[c]=null):be(i,o),s.set(e,i),i)}function Nn(e,t){return!e||!Gt(t)?!1:(t=t.slice(2).replace(/Once$/,""),Y(e,t[0].toLowerCase()+t.slice(1))||Y(e,bt(t))||Y(e,t))}let xe=null,Hn=null;function vn(e){const t=xe;return xe=e,Hn=e&&e.type.__scopeId||null,t}function Rs(e){Hn=e}function Ns(){Hn=null}function Me(e,t=xe,n){if(!t||e._n)return e;const s=(...r)=>{s._d&&mr(-1);const o=vn(t),i=e(...r);return vn(o),s._d&&mr(1),i};return s._n=!0,s._c=!0,s._d=!0,s}function Yn(e){const{type:t,vnode:n,proxy:s,withProxy:r,props:o,propsOptions:[i],slots:l,attrs:c,emit:u,render:d,renderCache:p,data:_,setupState:k,ctx:T,inheritAttrs:R}=e;let g,y;const M=vn(e);try{if(n.shapeFlag&4){const D=r||s;g=Oe(d.call(D,D,p,o,k,_,T)),y=c}else{const D=t;g=Oe(D.length>1?D(o,{attrs:c,slots:l,emit:u}):D(o,null)),y=t.props?c:dl(c)}}catch(D){Vt.length=0,en(D,e,1),g=B(Te)}let I=g;if(y&&R!==!1){const D=Object.keys(y),{shapeFlag:z}=I;D.length&&z&7&&(i&&D.some(xs)&&(y=hl(y,i)),I=vt(I,y))}return n.dirs&&(I.dirs=I.dirs?I.dirs.concat(n.dirs):n.dirs),n.transition&&(I.transition=n.transition),g=I,vn(M),g}const dl=e=>{let t;for(const n in e)(n==="class"||n==="style"||Gt(n))&&((t||(t={}))[n]=e[n]);return t},hl=(e,t)=>{const n={};for(const s in e)(!xs(s)||!(s.slice(9)in t))&&(n[s]=e[s]);return n};function pl(e,t,n){const{props:s,children:r,component:o}=e,{props:i,children:l,patchFlag:c}=t,u=o.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&c>=0){if(c&1024)return!0;if(c&16)return s?lr(s,i,u):!!i;if(c&8){const d=t.dynamicProps;for(let p=0;pe.__isSuspense;function bo(e,t){t&&t.pendingBranch?N(e)?t.effects.push(...e):t.effects.push(e):ul(e)}function ml(e,t){if(pe){let n=pe.provides;const s=pe.parent&&pe.parent.provides;s===n&&(n=pe.provides=Object.create(s)),n[e]=t}}function St(e,t,n=!1){const s=pe||xe;if(s){const r=s.parent==null?s.vnode.appContext&&s.vnode.appContext.provides:s.parent.provides;if(r&&e in r)return r[e];if(arguments.length>1)return n&&K(t)?t.call(s.proxy):t}}function yo(e,t){return Hs(e,null,t)}const cr={};function He(e,t,n){return Hs(e,t,n)}function Hs(e,t,{immediate:n,deep:s,flush:r,onTrack:o,onTrigger:i}=ne){const l=pe;let c,u=!1,d=!1;if(_e(e)?(c=()=>e.value,u=io(e)):Lt(e)?(c=()=>e,s=!0):N(e)?(d=!0,u=e.some(Lt),c=()=>e.map(y=>{if(_e(y))return y.value;if(Lt(y))return ht(y);if(K(y))return ot(y,l,2)})):K(e)?t?c=()=>ot(e,l,2):c=()=>{if(!(l&&l.isUnmounted))return p&&p(),Pe(e,l,3,[_])}:c=Ne,t&&s){const y=c;c=()=>ht(y())}let p,_=y=>{p=g.onStop=()=>{ot(y,l,4)}};if(It)return _=Ne,t?n&&Pe(t,l,3,[c(),d?[]:void 0,_]):c(),Ne;let k=d?[]:cr;const T=()=>{if(!!g.active)if(t){const y=g.run();(s||u||(d?y.some((M,I)=>zt(M,k[I])):zt(y,k)))&&(p&&p(),Pe(t,l,3,[y,k===cr?void 0:k,_]),k=y)}else g.run()};T.allowRecurse=!!t;let R;r==="sync"?R=T:r==="post"?R=()=>ke(T,l&&l.suspense):R=()=>{!l||l.isMounted?al(T):T()};const g=new Ts(c,R);return t?n?T():k=g.run():r==="post"?ke(g.run.bind(g),l&&l.suspense):g.run(),()=>{g.stop(),l&&l.scope&&ws(l.scope.effects,g)}}function vl(e,t,n){const s=this.proxy,r=me(e)?e.includes(".")?xo(s,e):()=>s[e]:e.bind(s,s);let o;K(t)?o=t:(o=t.handler,n=t);const i=pe;Pt(this);const l=Hs(r,o.bind(s),n);return i?Pt(i):gt(),l}function xo(e,t){const n=t.split(".");return()=>{let s=e;for(let r=0;r{ht(n,t)});else if(Zr(e))for(const n in e)ht(e[n],t);return e}function bl(){const e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return Ye(()=>{e.isMounted=!0}),Ao(()=>{e.isUnmounting=!0}),e}const Le=[Function,Array],yl={name:"BaseTransition",props:{mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Le,onEnter:Le,onAfterEnter:Le,onEnterCancelled:Le,onBeforeLeave:Le,onLeave:Le,onAfterLeave:Le,onLeaveCancelled:Le,onBeforeAppear:Le,onAppear:Le,onAfterAppear:Le,onAppearCancelled:Le},setup(e,{slots:t}){const n=rc(),s=bl();let r;return()=>{const o=t.default&&ko(t.default(),!0);if(!o||!o.length)return;let i=o[0];if(o.length>1){for(const R of o)if(R.type!==Te){i=R;break}}const l=X(e),{mode:c}=l;if(s.isLeaving)return Jn(i);const u=ar(i);if(!u)return Jn(i);const d=as(u,l,s,n);us(u,d);const p=n.subTree,_=p&&ar(p);let k=!1;const{getTransitionKey:T}=u.type;if(T){const R=T();r===void 0?r=R:R!==r&&(r=R,k=!0)}if(_&&_.type!==Te&&(!ft(u,_)||k)){const R=as(_,l,s,n);if(us(_,R),c==="out-in")return s.isLeaving=!0,R.afterLeave=()=>{s.isLeaving=!1,n.update()},Jn(i);c==="in-out"&&u.type!==Te&&(R.delayLeave=(g,y,M)=>{const I=Co(s,_);I[String(_.key)]=_,g._leaveCb=()=>{y(),g._leaveCb=void 0,delete d.delayedLeave},d.delayedLeave=M})}return i}}},wo=yl;function Co(e,t){const{leavingVNodes:n}=e;let s=n.get(t.type);return s||(s=Object.create(null),n.set(t.type,s)),s}function as(e,t,n,s){const{appear:r,mode:o,persisted:i=!1,onBeforeEnter:l,onEnter:c,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:p,onLeave:_,onAfterLeave:k,onLeaveCancelled:T,onBeforeAppear:R,onAppear:g,onAfterAppear:y,onAppearCancelled:M}=t,I=String(e.key),D=Co(n,e),z=(S,Z)=>{S&&Pe(S,s,9,Z)},Q={mode:o,persisted:i,beforeEnter(S){let Z=l;if(!n.isMounted)if(r)Z=R||l;else return;S._leaveCb&&S._leaveCb(!0);const q=D[I];q&&ft(e,q)&&q.el._leaveCb&&q.el._leaveCb(),z(Z,[S])},enter(S){let Z=c,q=u,G=d;if(!n.isMounted)if(r)Z=g||c,q=y||u,G=M||d;else return;let se=!1;const $=S._enterCb=W=>{se||(se=!0,W?z(G,[S]):z(q,[S]),Q.delayedLeave&&Q.delayedLeave(),S._enterCb=void 0)};Z?(Z(S,$),Z.length<=1&&$()):$()},leave(S,Z){const q=String(e.key);if(S._enterCb&&S._enterCb(!0),n.isUnmounting)return Z();z(p,[S]);let G=!1;const se=S._leaveCb=$=>{G||(G=!0,Z(),$?z(T,[S]):z(k,[S]),S._leaveCb=void 0,D[q]===e&&delete D[q])};D[q]=e,_?(_(S,se),_.length<=1&&se()):se()},clone(S){return as(S,t,n,s)}};return Q}function Jn(e){if(tn(e))return e=vt(e),e.children=null,e}function ar(e){return tn(e)?e.children?e.children[0]:void 0:e}function us(e,t){e.shapeFlag&6&&e.component?us(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function ko(e,t=!1,n){let s=[],r=0;for(let o=0;o1)for(let o=0;o!!e.type.__asyncLoader;function $o(e){K(e)&&(e={loader:e});const{loader:t,loadingComponent:n,errorComponent:s,delay:r=200,timeout:o,suspensible:i=!0,onError:l}=e;let c=null,u,d=0;const p=()=>(d++,c=null,_()),_=()=>{let k;return c||(k=c=t().catch(T=>{if(T=T instanceof Error?T:new Error(String(T)),l)return new Promise((R,g)=>{l(T,()=>R(p()),()=>g(T),d+1)});throw T}).then(T=>k!==c&&c?c:(T&&(T.__esModule||T[Symbol.toStringTag]==="Module")&&(T=T.default),u=T,T)))};return ue({name:"AsyncComponentWrapper",__asyncLoader:_,get __asyncResolved(){return u},setup(){const k=pe;if(u)return()=>Qn(u,k);const T=M=>{c=null,en(M,k,13,!s)};if(i&&k.suspense||It)return _().then(M=>()=>Qn(M,k)).catch(M=>(T(M),()=>s?B(s,{error:M}):null));const R=je(!1),g=je(),y=je(!!r);return r&&setTimeout(()=>{y.value=!1},r),o!=null&&setTimeout(()=>{if(!R.value&&!g.value){const M=new Error(`Async component timed out after ${o}ms.`);T(M),g.value=M}},o),_().then(()=>{R.value=!0,k.parent&&tn(k.parent.vnode)&&Os(k.parent.update)}).catch(M=>{T(M),g.value=M}),()=>{if(R.value&&u)return Qn(u,k);if(g.value&&s)return B(s,{error:g.value});if(n&&!y.value)return B(n)}}})}function Qn(e,{vnode:{ref:t,props:n,children:s}}){const r=B(e,n,s);return r.ref=t,r}const tn=e=>e.type.__isKeepAlive;function xl(e,t){Eo(e,"a",t)}function wl(e,t){Eo(e,"da",t)}function Eo(e,t,n=pe){const s=e.__wdc||(e.__wdc=()=>{let r=n;for(;r;){if(r.isDeactivated)return;r=r.parent}return e()});if(Bn(t,s,n),n){let r=n.parent;for(;r&&r.parent;)tn(r.parent.vnode)&&Cl(s,t,n,r),r=r.parent}}function Cl(e,t,n,s){const r=Bn(t,e,s,!0);Dn(()=>{ws(s[t],r)},n)}function Bn(e,t,n=pe,s=!1){if(n){const r=n[e]||(n[e]=[]),o=t.__weh||(t.__weh=(...i)=>{if(n.isUnmounted)return;Ot(),Pt(n);const l=Pe(t,n,e,i);return gt(),Ft(),l});return s?r.unshift(o):r.push(o),o}}const Ze=e=>(t,n=pe)=>(!It||e==="sp")&&Bn(e,t,n),kl=Ze("bm"),Ye=Ze("m"),$l=Ze("bu"),To=Ze("u"),Ao=Ze("bum"),Dn=Ze("um"),El=Ze("sp"),Tl=Ze("rtg"),Al=Ze("rtc");function Ll(e,t=pe){Bn("ec",e,t)}let fs=!0;function Sl(e){const t=So(e),n=e.proxy,s=e.ctx;fs=!1,t.beforeCreate&&ur(t.beforeCreate,e,"bc");const{data:r,computed:o,methods:i,watch:l,provide:c,inject:u,created:d,beforeMount:p,mounted:_,beforeUpdate:k,updated:T,activated:R,deactivated:g,beforeDestroy:y,beforeUnmount:M,destroyed:I,unmounted:D,render:z,renderTracked:Q,renderTriggered:S,errorCaptured:Z,serverPrefetch:q,expose:G,inheritAttrs:se,components:$,directives:W,filters:le}=t;if(u&&Pl(u,s,null,e.appContext.config.unwrapInjectedRef),i)for(const ce in i){const re=i[ce];K(re)&&(s[ce]=re.bind(n))}if(r){const ce=r.call(n,n);ae(ce)&&(e.data=Fn(ce))}if(fs=!0,o)for(const ce in o){const re=o[ce],qe=K(re)?re.bind(n,n):K(re.get)?re.get.bind(n,n):Ne,Wn=!K(re)&&K(re.set)?re.set.bind(n):Ne,Rt=V({get:qe,set:Wn});Object.defineProperty(s,ce,{enumerable:!0,configurable:!0,get:()=>Rt.value,set:xt=>Rt.value=xt})}if(l)for(const ce in l)Lo(l[ce],s,n,ce);if(c){const ce=K(c)?c.call(n):c;Reflect.ownKeys(ce).forEach(re=>{ml(re,ce[re])})}d&&ur(d,e,"c");function ve(ce,re){N(re)?re.forEach(qe=>ce(qe.bind(n))):re&&ce(re.bind(n))}if(ve(kl,p),ve(Ye,_),ve($l,k),ve(To,T),ve(xl,R),ve(wl,g),ve(Ll,Z),ve(Al,Q),ve(Tl,S),ve(Ao,M),ve(Dn,D),ve(El,q),N(G))if(G.length){const ce=e.exposed||(e.exposed={});G.forEach(re=>{Object.defineProperty(ce,re,{get:()=>n[re],set:qe=>n[re]=qe})})}else e.exposed||(e.exposed={});z&&e.render===Ne&&(e.render=z),se!=null&&(e.inheritAttrs=se),$&&(e.components=$),W&&(e.directives=W)}function Pl(e,t,n=Ne,s=!1){N(e)&&(e=ds(e));for(const r in e){const o=e[r];let i;ae(o)?"default"in o?i=St(o.from||r,o.default,!0):i=St(o.from||r):i=St(o),_e(i)&&s?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>i.value,set:l=>i.value=l}):t[r]=i}}function ur(e,t,n){Pe(N(e)?e.map(s=>s.bind(t.proxy)):e.bind(t.proxy),t,n)}function Lo(e,t,n,s){const r=s.includes(".")?xo(n,s):()=>n[s];if(me(e)){const o=t[e];K(o)&&He(r,o)}else if(K(e))He(r,e.bind(n));else if(ae(e))if(N(e))e.forEach(o=>Lo(o,t,n,s));else{const o=K(e.handler)?e.handler.bind(n):t[e.handler];K(o)&&He(r,o,e)}}function So(e){const t=e.type,{mixins:n,extends:s}=t,{mixins:r,optionsCache:o,config:{optionMergeStrategies:i}}=e.appContext,l=o.get(t);let c;return l?c=l:!r.length&&!n&&!s?c=t:(c={},r.length&&r.forEach(u=>bn(c,u,i,!0)),bn(c,t,i)),o.set(t,c),c}function bn(e,t,n,s=!1){const{mixins:r,extends:o}=t;o&&bn(e,o,n,!0),r&&r.forEach(i=>bn(e,i,n,!0));for(const i in t)if(!(s&&i==="expose")){const l=Il[i]||n&&n[i];e[i]=l?l(e[i],t[i]):t[i]}return e}const Il={data:fr,props:ut,emits:ut,methods:ut,computed:ut,beforeCreate:we,created:we,beforeMount:we,mounted:we,beforeUpdate:we,updated:we,beforeDestroy:we,beforeUnmount:we,destroyed:we,unmounted:we,activated:we,deactivated:we,errorCaptured:we,serverPrefetch:we,components:ut,directives:ut,watch:Ol,provide:fr,inject:Ml};function fr(e,t){return t?e?function(){return be(K(e)?e.call(this,this):e,K(t)?t.call(this,this):t)}:t:e}function Ml(e,t){return ut(ds(e),ds(t))}function ds(e){if(N(e)){const t={};for(let n=0;n0)&&!(i&16)){if(i&8){const d=e.vnode.dynamicProps;for(let p=0;p{c=!0;const[_,k]=Io(p,t,!0);be(i,_),k&&l.push(...k)};!n&&t.mixins.length&&t.mixins.forEach(d),e.extends&&d(e.extends),e.mixins&&e.mixins.forEach(d)}if(!o&&!c)return s.set(e,Tt),Tt;if(N(o))for(let d=0;d-1,k[1]=R<0||T-1||Y(k,"default"))&&l.push(p)}}}const u=[i,l];return s.set(e,u),u}function dr(e){return e[0]!=="$"}function hr(e){const t=e&&e.toString().match(/^\s*function (\w+)/);return t?t[1]:e===null?"null":""}function pr(e,t){return hr(e)===hr(t)}function _r(e,t){return N(t)?t.findIndex(n=>pr(n,e)):K(t)&&pr(t,e)?0:-1}const Mo=e=>e[0]==="_"||e==="$stable",Bs=e=>N(e)?e.map(Oe):[Oe(e)],Nl=(e,t,n)=>{const s=Me((...r)=>Bs(t(...r)),n);return s._c=!1,s},Oo=(e,t,n)=>{const s=e._ctx;for(const r in e){if(Mo(r))continue;const o=e[r];if(K(o))t[r]=Nl(r,o,s);else if(o!=null){const i=Bs(o);t[r]=()=>i}}},Fo=(e,t)=>{const n=Bs(t);e.slots.default=()=>n},Hl=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=X(t),_n(t,"_",n)):Oo(t,e.slots={})}else e.slots={},t&&Fo(e,t);_n(e.slots,Un,1)},Bl=(e,t,n)=>{const{vnode:s,slots:r}=e;let o=!0,i=ne;if(s.shapeFlag&32){const l=t._;l?n&&l===1?o=!1:(be(r,t),!n&&l===1&&delete r._):(o=!t.$stable,Oo(t,r)),i=t}else t&&(Fo(e,t),i={default:1});if(o)for(const l in r)!Mo(l)&&!(l in i)&&delete r[l]};function Dl(e,t){const n=xe;if(n===null)return e;const s=Kn(n)||n.proxy,r=e.dirs||(e.dirs=[]);for(let o=0;oyn(_,t&&(N(t)?t[k]:t),n,s,r));return}if(Qt(s)&&!r)return;const o=s.shapeFlag&4?Kn(s.component)||s.component.proxy:s.el,i=r?null:o,{i:l,r:c}=e,u=t&&t.r,d=l.refs===ne?l.refs={}:l.refs,p=l.setupState;if(u!=null&&u!==c&&(me(u)?(d[u]=null,Y(p,u)&&(p[u]=null)):_e(u)&&(u.value=null)),K(c))ot(c,l,12,[i,d]);else{const _=me(c),k=_e(c);if(_||k){const T=()=>{if(e.f){const R=_?d[c]:c.value;r?N(R)&&ws(R,o):N(R)?R.includes(o)||R.push(o):_?(d[c]=[o],Y(p,c)&&(p[c]=d[c])):(c.value=[o],e.k&&(d[e.k]=c.value))}else _?(d[c]=i,Y(p,c)&&(p[c]=i)):_e(c)&&(c.value=i,e.k&&(d[e.k]=i))};i?(T.id=-1,ke(T,n)):T()}}}let Ge=!1;const fn=e=>/svg/.test(e.namespaceURI)&&e.tagName!=="foreignObject",Xn=e=>e.nodeType===8;function Kl(e){const{mt:t,p:n,o:{patchProp:s,nextSibling:r,parentNode:o,remove:i,insert:l,createComment:c}}=e,u=(g,y)=>{if(!y.hasChildNodes()){n(null,g,y),mn();return}Ge=!1,d(y.firstChild,g,null,null,null),mn(),Ge&&console.error("Hydration completed but contains mismatches.")},d=(g,y,M,I,D,z=!1)=>{const Q=Xn(g)&&g.data==="[",S=()=>T(g,y,M,I,D,Q),{type:Z,ref:q,shapeFlag:G}=y,se=g.nodeType;y.el=g;let $=null;switch(Z){case Xt:se!==3?$=S():(g.data!==y.children&&(Ge=!0,g.data=y.children),$=r(g));break;case Te:se!==8||Q?$=S():$=r(g);break;case Wt:if(se!==1)$=S();else{$=g;const W=!y.children.length;for(let le=0;le{z=z||!!y.dynamicChildren;const{type:Q,props:S,patchFlag:Z,shapeFlag:q,dirs:G}=y,se=Q==="input"&&G||Q==="option";if(se||Z!==-1){if(G&&Ue(y,null,M,"created"),S)if(se||!z||Z&48)for(const W in S)(se&&W.endsWith("value")||Gt(W)&&!Ut(W))&&s(g,W,null,S[W],!1,void 0,M);else S.onClick&&s(g,"onClick",null,S.onClick,!1,void 0,M);let $;if(($=S&&S.onVnodeBeforeMount)&&Se($,M,y),G&&Ue(y,null,M,"beforeMount"),(($=S&&S.onVnodeMounted)||G)&&bo(()=>{$&&Se($,M,y),G&&Ue(y,null,M,"mounted")},I),q&16&&!(S&&(S.innerHTML||S.textContent))){let W=_(g.firstChild,y,g,M,I,D,z);for(;W;){Ge=!0;const le=W;W=W.nextSibling,i(le)}}else q&8&&g.textContent!==y.children&&(Ge=!0,g.textContent=y.children)}return g.nextSibling},_=(g,y,M,I,D,z,Q)=>{Q=Q||!!y.dynamicChildren;const S=y.children,Z=S.length;for(let q=0;q{const{slotScopeIds:Q}=y;Q&&(D=D?D.concat(Q):Q);const S=o(g),Z=_(r(g),y,S,M,I,D,z);return Z&&Xn(Z)&&Z.data==="]"?r(y.anchor=Z):(Ge=!0,l(y.anchor=c("]"),S,Z),Z)},T=(g,y,M,I,D,z)=>{if(Ge=!0,y.el=null,z){const Z=R(g);for(;;){const q=r(g);if(q&&q!==Z)i(q);else break}}const Q=r(g),S=o(g);return i(g),n(null,y,S,Q,M,I,fn(S),D),Q},R=g=>{let y=0;for(;g;)if(g=r(g),g&&Xn(g)&&(g.data==="["&&y++,g.data==="]")){if(y===0)return r(g);y--}return g};return[u,d]}const ke=bo;function ql(e){return Wl(e,Kl)}function Wl(e,t){const n=ki();n.__VUE__=!0;const{insert:s,remove:r,patchProp:o,createElement:i,createText:l,createComment:c,setText:u,setElementText:d,parentNode:p,nextSibling:_,setScopeId:k=Ne,cloneNode:T,insertStaticContent:R}=e,g=(a,f,h,v=null,m=null,C=null,A=!1,w=null,E=!!f.dynamicChildren)=>{if(a===f)return;a&&!ft(a,f)&&(v=rn(a),Qe(a,m,C,!0),a=null),f.patchFlag===-2&&(E=!1,f.dynamicChildren=null);const{type:b,ref:O,shapeFlag:P}=f;switch(b){case Xt:y(a,f,h,v);break;case Te:M(a,f,h,v);break;case Wt:a==null&&I(f,h,v,A);break;case he:W(a,f,h,v,m,C,A,w,E);break;default:P&1?Q(a,f,h,v,m,C,A,w,E):P&6?le(a,f,h,v,m,C,A,w,E):(P&64||P&128)&&b.process(a,f,h,v,m,C,A,w,E,wt)}O!=null&&m&&yn(O,a&&a.ref,C,f||a,!f)},y=(a,f,h,v)=>{if(a==null)s(f.el=l(f.children),h,v);else{const m=f.el=a.el;f.children!==a.children&&u(m,f.children)}},M=(a,f,h,v)=>{a==null?s(f.el=c(f.children||""),h,v):f.el=a.el},I=(a,f,h,v)=>{[a.el,a.anchor]=R(a.children,f,h,v,a.el,a.anchor)},D=({el:a,anchor:f},h,v)=>{let m;for(;a&&a!==f;)m=_(a),s(a,h,v),a=m;s(f,h,v)},z=({el:a,anchor:f})=>{let h;for(;a&&a!==f;)h=_(a),r(a),a=h;r(f)},Q=(a,f,h,v,m,C,A,w,E)=>{A=A||f.type==="svg",a==null?S(f,h,v,m,C,A,w,E):G(a,f,m,C,A,w,E)},S=(a,f,h,v,m,C,A,w)=>{let E,b;const{type:O,props:P,shapeFlag:F,transition:j,patchFlag:J,dirs:ie}=a;if(a.el&&T!==void 0&&J===-1)E=a.el=T(a.el);else{if(E=a.el=i(a.type,C,P&&P.is,P),F&8?d(E,a.children):F&16&&q(a.children,E,null,v,m,C&&O!=="foreignObject",A,w),ie&&Ue(a,null,v,"created"),P){for(const oe in P)oe!=="value"&&!Ut(oe)&&o(E,oe,null,P[oe],C,a.children,v,m,We);"value"in P&&o(E,"value",null,P.value),(b=P.onVnodeBeforeMount)&&Se(b,v,a)}Z(E,a,a.scopeId,A,v)}ie&&Ue(a,null,v,"beforeMount");const te=(!m||m&&!m.pendingBranch)&&j&&!j.persisted;te&&j.beforeEnter(E),s(E,f,h),((b=P&&P.onVnodeMounted)||te||ie)&&ke(()=>{b&&Se(b,v,a),te&&j.enter(E),ie&&Ue(a,null,v,"mounted")},m)},Z=(a,f,h,v,m)=>{if(h&&k(a,h),v)for(let C=0;C{for(let b=E;b{const w=f.el=a.el;let{patchFlag:E,dynamicChildren:b,dirs:O}=f;E|=a.patchFlag&16;const P=a.props||ne,F=f.props||ne;let j;h&&ct(h,!1),(j=F.onVnodeBeforeUpdate)&&Se(j,h,f,a),O&&Ue(f,a,h,"beforeUpdate"),h&&ct(h,!0);const J=m&&f.type!=="foreignObject";if(b?se(a.dynamicChildren,b,w,h,v,J,C):A||qe(a,f,w,null,h,v,J,C,!1),E>0){if(E&16)$(w,f,P,F,h,v,m);else if(E&2&&P.class!==F.class&&o(w,"class",null,F.class,m),E&4&&o(w,"style",P.style,F.style,m),E&8){const ie=f.dynamicProps;for(let te=0;te{j&&Se(j,h,f,a),O&&Ue(f,a,h,"updated")},v)},se=(a,f,h,v,m,C,A)=>{for(let w=0;w{if(h!==v){for(const w in v){if(Ut(w))continue;const E=v[w],b=h[w];E!==b&&w!=="value"&&o(a,w,b,E,A,f.children,m,C,We)}if(h!==ne)for(const w in h)!Ut(w)&&!(w in v)&&o(a,w,h[w],null,A,f.children,m,C,We);"value"in v&&o(a,"value",h.value,v.value)}},W=(a,f,h,v,m,C,A,w,E)=>{const b=f.el=a?a.el:l(""),O=f.anchor=a?a.anchor:l("");let{patchFlag:P,dynamicChildren:F,slotScopeIds:j}=f;j&&(w=w?w.concat(j):j),a==null?(s(b,h,v),s(O,h,v),q(f.children,h,O,m,C,A,w,E)):P>0&&P&64&&F&&a.dynamicChildren?(se(a.dynamicChildren,F,h,m,C,A,w),(f.key!=null||m&&f===m.subTree)&&No(a,f,!0)):qe(a,f,h,O,m,C,A,w,E)},le=(a,f,h,v,m,C,A,w,E)=>{f.slotScopeIds=w,a==null?f.shapeFlag&512?m.ctx.activate(f,h,v,A,E):yt(f,h,v,m,C,A,E):ve(a,f,E)},yt=(a,f,h,v,m,C,A)=>{const w=a.component=sc(a,v,m);if(tn(a)&&(w.ctx.renderer=wt),oc(w),w.asyncDep){if(m&&m.registerDep(w,ce),!a.el){const E=w.subTree=B(Te);M(null,E,f,h)}return}ce(w,a,f,h,m,C,A)},ve=(a,f,h)=>{const v=f.component=a.component;if(pl(a,f,h))if(v.asyncDep&&!v.asyncResolved){re(v,f,h);return}else v.next=f,cl(v.update),v.update();else f.component=a.component,f.el=a.el,v.vnode=f},ce=(a,f,h,v,m,C,A)=>{const w=()=>{if(a.isMounted){let{next:O,bu:P,u:F,parent:j,vnode:J}=a,ie=O,te;ct(a,!1),O?(O.el=J.el,re(a,O,A)):O=J,P&&hn(P),(te=O.props&&O.props.onVnodeBeforeUpdate)&&Se(te,j,O,J),ct(a,!0);const oe=Yn(a),Ie=a.subTree;a.subTree=oe,g(Ie,oe,p(Ie.el),rn(Ie),a,m,C),O.el=oe.el,ie===null&&_l(a,oe.el),F&&ke(F,m),(te=O.props&&O.props.onVnodeUpdated)&&ke(()=>Se(te,j,O,J),m)}else{let O;const{el:P,props:F}=f,{bm:j,m:J,parent:ie}=a,te=Qt(f);if(ct(a,!1),j&&hn(j),!te&&(O=F&&F.onVnodeBeforeMount)&&Se(O,ie,f),ct(a,!0),P&&zn){const oe=()=>{a.subTree=Yn(a),zn(P,a.subTree,a,m,null)};te?f.type.__asyncLoader().then(()=>!a.isUnmounted&&oe()):oe()}else{const oe=a.subTree=Yn(a);g(null,oe,h,v,a,m,C),f.el=oe.el}if(J&&ke(J,m),!te&&(O=F&&F.onVnodeMounted)){const oe=f;ke(()=>Se(O,ie,oe),m)}f.shapeFlag&256&&a.a&&ke(a.a,m),a.isMounted=!0,f=h=v=null}},E=a.effect=new Ts(w,()=>Os(a.update),a.scope),b=a.update=E.run.bind(E);b.id=a.uid,ct(a,!0),b()},re=(a,f,h)=>{f.component=a;const v=a.vnode.props;a.vnode=f,a.next=null,Rl(a,f.props,v,h),Bl(a,f.children,h),Ot(),Fs(void 0,a.update),Ft()},qe=(a,f,h,v,m,C,A,w,E=!1)=>{const b=a&&a.children,O=a?a.shapeFlag:0,P=f.children,{patchFlag:F,shapeFlag:j}=f;if(F>0){if(F&128){Rt(b,P,h,v,m,C,A,w,E);return}else if(F&256){Wn(b,P,h,v,m,C,A,w,E);return}}j&8?(O&16&&We(b,m,C),P!==b&&d(h,P)):O&16?j&16?Rt(b,P,h,v,m,C,A,w,E):We(b,m,C,!0):(O&8&&d(h,""),j&16&&q(P,h,v,m,C,A,w,E))},Wn=(a,f,h,v,m,C,A,w,E)=>{a=a||Tt,f=f||Tt;const b=a.length,O=f.length,P=Math.min(b,O);let F;for(F=0;FO?We(a,m,C,!0,!1,P):q(f,h,v,m,C,A,w,E,P)},Rt=(a,f,h,v,m,C,A,w,E)=>{let b=0;const O=f.length;let P=a.length-1,F=O-1;for(;b<=P&&b<=F;){const j=a[b],J=f[b]=E?st(f[b]):Oe(f[b]);if(ft(j,J))g(j,J,h,null,m,C,A,w,E);else break;b++}for(;b<=P&&b<=F;){const j=a[P],J=f[F]=E?st(f[F]):Oe(f[F]);if(ft(j,J))g(j,J,h,null,m,C,A,w,E);else break;P--,F--}if(b>P){if(b<=F){const j=F+1,J=jF)for(;b<=P;)Qe(a[b],m,C,!0),b++;else{const j=b,J=b,ie=new Map;for(b=J;b<=F;b++){const $e=f[b]=E?st(f[b]):Oe(f[b]);$e.key!=null&&ie.set($e.key,b)}let te,oe=0;const Ie=F-J+1;let Ct=!1,Ys=0;const Nt=new Array(Ie);for(b=0;b=Ie){Qe($e,m,C,!0);continue}let Be;if($e.key!=null)Be=ie.get($e.key);else for(te=J;te<=F;te++)if(Nt[te-J]===0&&ft($e,f[te])){Be=te;break}Be===void 0?Qe($e,m,C,!0):(Nt[Be-J]=b+1,Be>=Ys?Ys=Be:Ct=!0,g($e,f[Be],h,null,m,C,A,w,E),oe++)}const Js=Ct?Vl(Nt):Tt;for(te=Js.length-1,b=Ie-1;b>=0;b--){const $e=J+b,Be=f[$e],Qs=$e+1{const{el:C,type:A,transition:w,children:E,shapeFlag:b}=a;if(b&6){xt(a.component.subTree,f,h,v);return}if(b&128){a.suspense.move(f,h,v);return}if(b&64){A.move(a,f,h,wt);return}if(A===he){s(C,f,h);for(let P=0;Pw.enter(C),m);else{const{leave:P,delayLeave:F,afterLeave:j}=w,J=()=>s(C,f,h),ie=()=>{P(C,()=>{J(),j&&j()})};F?F(C,J,ie):ie()}else s(C,f,h)},Qe=(a,f,h,v=!1,m=!1)=>{const{type:C,props:A,ref:w,children:E,dynamicChildren:b,shapeFlag:O,patchFlag:P,dirs:F}=a;if(w!=null&&yn(w,null,h,a,!0),O&256){f.ctx.deactivate(a);return}const j=O&1&&F,J=!Qt(a);let ie;if(J&&(ie=A&&A.onVnodeBeforeUnmount)&&Se(ie,f,a),O&6)ci(a.component,h,v);else{if(O&128){a.suspense.unmount(h,v);return}j&&Ue(a,null,f,"beforeUnmount"),O&64?a.type.remove(a,f,h,m,wt,v):b&&(C!==he||P>0&&P&64)?We(b,f,h,!1,!0):(C===he&&P&384||!m&&O&16)&&We(E,f,h),v&&zs(a)}(J&&(ie=A&&A.onVnodeUnmounted)||j)&&ke(()=>{ie&&Se(ie,f,a),j&&Ue(a,null,f,"unmounted")},h)},zs=a=>{const{type:f,el:h,anchor:v,transition:m}=a;if(f===he){li(h,v);return}if(f===Wt){z(a);return}const C=()=>{r(h),m&&!m.persisted&&m.afterLeave&&m.afterLeave()};if(a.shapeFlag&1&&m&&!m.persisted){const{leave:A,delayLeave:w}=m,E=()=>A(h,C);w?w(a.el,C,E):E()}else C()},li=(a,f)=>{let h;for(;a!==f;)h=_(a),r(a),a=h;r(f)},ci=(a,f,h)=>{const{bum:v,scope:m,update:C,subTree:A,um:w}=a;v&&hn(v),m.stop(),C&&(C.active=!1,Qe(A,a,f,h)),w&&ke(w,f),ke(()=>{a.isUnmounted=!0},f),f&&f.pendingBranch&&!f.isUnmounted&&a.asyncDep&&!a.asyncResolved&&a.suspenseId===f.pendingId&&(f.deps--,f.deps===0&&f.resolve())},We=(a,f,h,v=!1,m=!1,C=0)=>{for(let A=C;Aa.shapeFlag&6?rn(a.component.subTree):a.shapeFlag&128?a.suspense.next():_(a.anchor||a.el),Zs=(a,f,h)=>{a==null?f._vnode&&Qe(f._vnode,null,null,!0):g(f._vnode||null,a,f,null,null,null,h),mn(),f._vnode=a},wt={p:g,um:Qe,m:xt,r:zs,mt:yt,mc:q,pc:qe,pbc:se,n:rn,o:e};let Vn,zn;return t&&([Vn,zn]=t(wt)),{render:Zs,hydrate:Vn,createApp:jl(Zs,Vn)}}function ct({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function No(e,t,n=!1){const s=e.children,r=t.children;if(N(s)&&N(r))for(let o=0;o>1,e[n[l]]0&&(t[s]=n[o-1]),n[o]=s)}}for(o=n.length,i=n[o-1];o-- >0;)n[o]=i,i=t[i];return n}const zl=e=>e.__isTeleport,Ho="components";function xn(e,t){return Yl(Ho,e,!0,t)||e}const Zl=Symbol();function Yl(e,t,n=!0,s=!1){const r=xe||pe;if(r){const o=r.type;if(e===Ho){const l=ac(o);if(l&&(l===t||l===Ke(t)||l===Mn(Ke(t))))return o}const i=gr(r[e]||o[e],t)||gr(r.appContext[e],t);return!i&&s?o:i}}function gr(e,t){return e&&(e[t]||e[Ke(t)]||e[Mn(Ke(t))])}const he=Symbol(void 0),Xt=Symbol(void 0),Te=Symbol(void 0),Wt=Symbol(void 0),Vt=[];let _t=null;function L(e=!1){Vt.push(_t=e?null:[])}function Jl(){Vt.pop(),_t=Vt[Vt.length-1]||null}let wn=1;function mr(e){wn+=e}function Bo(e){return e.dynamicChildren=wn>0?_t||Tt:null,Jl(),wn>0&&_t&&_t.push(e),e}function H(e,t,n,s,r,o){return Bo(U(e,t,n,s,r,o,!0))}function ge(e,t,n,s,r){return Bo(B(e,t,n,s,r,!0))}function Cn(e){return e?e.__v_isVNode===!0:!1}function ft(e,t){return e.type===t.type&&e.key===t.key}const Un="__vInternal",Do=({key:e})=>e!=null?e:null,pn=({ref:e,ref_key:t,ref_for:n})=>e!=null?me(e)||_e(e)||K(e)?{i:xe,r:e,k:t,f:!!n}:e:null;function U(e,t=null,n=null,s=0,r=null,o=e===he?0:1,i=!1,l=!1){const c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Do(t),ref:t&&pn(t),scopeId:Hn,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:o,patchFlag:s,dynamicProps:r,dynamicChildren:null,appContext:null};return l?(Ds(c,n),o&128&&e.normalize(c)):n&&(c.shapeFlag|=me(n)?8:16),wn>0&&!i&&_t&&(c.patchFlag>0||o&6)&&c.patchFlag!==32&&_t.push(c),c}const B=Ql;function Ql(e,t=null,n=null,s=0,r=null,o=!1){if((!e||e===Zl)&&(e=Te),Cn(e)){const l=vt(e,t,!0);return n&&Ds(l,n),l}if(uc(e)&&(e=e.__vccOpts),t){t=Xl(t);let{class:l,style:c}=t;l&&!me(l)&&(t.class=mt(l)),ae(c)&&(lo(c)&&!N(c)&&(c=be({},c)),t.style=ys(c))}const i=me(e)?1:gl(e)?128:zl(e)?64:ae(e)?4:K(e)?2:0;return U(e,t,n,s,r,i,o,!0)}function Xl(e){return e?lo(e)||Un in e?be({},e):e:null}function vt(e,t,n=!1){const{props:s,ref:r,patchFlag:o,children:i}=e,l=t?Us(s||{},t):s;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:l,key:l&&Do(l),ref:t&&t.ref?n&&r?N(r)?r.concat(pn(t)):[r,pn(t)]:pn(t):r,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:i,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==he?o===-1?16:o|16:o,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&vt(e.ssContent),ssFallback:e.ssFallback&&vt(e.ssFallback),el:e.el,anchor:e.anchor}}function nn(e=" ",t=0){return B(Xt,null,e,t)}function Gl(e,t){const n=B(Wt,null,e);return n.staticCount=t,n}function ee(e="",t=!1){return t?(L(),ge(Te,null,e)):B(Te,null,e)}function Oe(e){return e==null||typeof e=="boolean"?B(Te):N(e)?B(he,null,e.slice()):typeof e=="object"?st(e):B(Xt,null,String(e))}function st(e){return e.el===null||e.memo?e:vt(e)}function Ds(e,t){let n=0;const{shapeFlag:s}=e;if(t==null)t=null;else if(N(t))n=16;else if(typeof t=="object")if(s&65){const r=t.default;r&&(r._c&&(r._d=!1),Ds(e,r()),r._c&&(r._d=!0));return}else{n=32;const r=t._;!r&&!(Un in t)?t._ctx=xe:r===3&&xe&&(xe.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else K(t)?(t={default:t,_ctx:xe},n=32):(t=String(t),s&64?(n=16,t=[nn(t)]):n=8);e.children=t,e.shapeFlag|=n}function Us(...e){const t={};for(let n=0;nt(i,l,void 0,o&&o[l]));else{const i=Object.keys(e);r=new Array(i.length);for(let l=0,c=i.length;lCn(t)?!(t.type===Te||t.type===he&&!Uo(t.children)):!0)?e:null}const ps=e=>e?jo(e)?Kn(e)||e.proxy:ps(e.parent):null,kn=be(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>ps(e.parent),$root:e=>ps(e.root),$emit:e=>e.emit,$options:e=>So(e),$forceUpdate:e=>()=>Os(e.update),$nextTick:e=>Rn.bind(e.proxy),$watch:e=>vl.bind(e)}),ec={get({_:e},t){const{ctx:n,setupState:s,data:r,props:o,accessCache:i,type:l,appContext:c}=e;let u;if(t[0]!=="$"){const k=i[t];if(k!==void 0)switch(k){case 1:return s[t];case 2:return r[t];case 4:return n[t];case 3:return o[t]}else{if(s!==ne&&Y(s,t))return i[t]=1,s[t];if(r!==ne&&Y(r,t))return i[t]=2,r[t];if((u=e.propsOptions[0])&&Y(u,t))return i[t]=3,o[t];if(n!==ne&&Y(n,t))return i[t]=4,n[t];fs&&(i[t]=0)}}const d=kn[t];let p,_;if(d)return t==="$attrs"&&Ae(e,"get",t),d(e);if((p=l.__cssModules)&&(p=p[t]))return p;if(n!==ne&&Y(n,t))return i[t]=4,n[t];if(_=c.config.globalProperties,Y(_,t))return _[t]},set({_:e},t,n){const{data:s,setupState:r,ctx:o}=e;return r!==ne&&Y(r,t)?(r[t]=n,!0):s!==ne&&Y(s,t)?(s[t]=n,!0):Y(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(o[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:s,appContext:r,propsOptions:o}},i){let l;return!!n[i]||e!==ne&&Y(e,i)||t!==ne&&Y(t,i)||(l=o[0])&&Y(l,i)||Y(s,i)||Y(kn,i)||Y(r.config.globalProperties,i)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:Y(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}},tc=Ro();let nc=0;function sc(e,t,n){const s=e.type,r=(t?t.appContext:e.appContext)||tc,o={uid:nc++,vnode:e,type:s,parent:t,appContext:r,root:null,next:null,subTree:null,effect:null,update:null,scope:new $i(!0),render:null,proxy:null,exposed:null,exposeProxy:null,withProxy:null,provides:t?t.provides:Object.create(r.provides),accessCache:null,renderCache:[],components:null,directives:null,propsOptions:Io(s,r),emitsOptions:vo(s,r),emit:null,emitted:null,propsDefaults:ne,inheritAttrs:s.inheritAttrs,ctx:ne,data:ne,props:ne,attrs:ne,slots:ne,refs:ne,setupState:ne,setupContext:null,suspense:n,suspenseId:n?n.pendingId:0,asyncDep:null,asyncResolved:!1,isMounted:!1,isUnmounted:!1,isDeactivated:!1,bc:null,c:null,bm:null,m:null,bu:null,u:null,um:null,bum:null,da:null,a:null,rtg:null,rtc:null,ec:null,sp:null};return o.ctx={_:o},o.root=t?t.root:o,o.emit=fl.bind(null,o),e.ce&&e.ce(o),o}let pe=null;const rc=()=>pe||xe,Pt=e=>{pe=e,e.scope.on()},gt=()=>{pe&&pe.scope.off(),pe=null};function jo(e){return e.vnode.shapeFlag&4}let It=!1;function oc(e,t=!1){It=t;const{props:n,children:s}=e.vnode,r=jo(e);Fl(e,n,r,t),Hl(e,s);const o=r?ic(e,t):void 0;return It=!1,o}function ic(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=jt(new Proxy(e.ctx,ec));const{setup:s}=n;if(s){const r=e.setupContext=s.length>1?cc(e):null;Pt(e),Ot();const o=ot(s,e,0,[e.props,r]);if(Ft(),gt(),Vr(o)){if(o.then(gt,gt),t)return o.then(i=>{vr(e,i,t)}).catch(i=>{en(i,e,0)});e.asyncDep=o}else vr(e,o,t)}else Ko(e,t)}function vr(e,t,n){K(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:ae(t)&&(e.setupState=fo(t)),Ko(e,n)}let br;function Ko(e,t,n){const s=e.type;if(!e.render){if(!t&&br&&!s.render){const r=s.template;if(r){const{isCustomElement:o,compilerOptions:i}=e.appContext.config,{delimiters:l,compilerOptions:c}=s,u=be(be({isCustomElement:o,delimiters:l},i),c);s.render=br(r,u)}}e.render=s.render||Ne}Pt(e),Ot(),Sl(e),Ft(),gt()}function lc(e){return new Proxy(e.attrs,{get(t,n){return Ae(e,"get","$attrs"),t[n]}})}function cc(e){const t=s=>{e.exposed=s||{}};let n;return{get attrs(){return n||(n=lc(e))},slots:e.slots,emit:e.emit,expose:t}}function Kn(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(fo(jt(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in kn)return kn[n](e)}}))}function ac(e){return K(e)&&e.displayName||e.name}function uc(e){return K(e)&&"__vccOpts"in e}const V=(e,t)=>ol(e,t,It);function lt(e,t,n){const s=arguments.length;return s===2?ae(t)&&!N(t)?Cn(t)?B(e,null,[t]):B(e,t):B(e,null,t):(s>3?n=Array.prototype.slice.call(arguments,2):s===3&&Cn(n)&&(n=[n]),B(e,t,n))}const fc="3.2.33",dc="http://www.w3.org/2000/svg",dt=typeof document!="undefined"?document:null,yr=dt&&dt.createElement("template"),hc={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,s)=>{const r=t?dt.createElementNS(dc,e):dt.createElement(e,n?{is:n}:void 0);return e==="select"&&s&&s.multiple!=null&&r.setAttribute("multiple",s.multiple),r},createText:e=>dt.createTextNode(e),createComment:e=>dt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>dt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},cloneNode(e){const t=e.cloneNode(!0);return"_value"in e&&(t._value=e._value),t},insertStaticContent(e,t,n,s,r,o){const i=n?n.previousSibling:t.lastChild;if(r&&(r===o||r.nextSibling))for(;t.insertBefore(r.cloneNode(!0),n),!(r===o||!(r=r.nextSibling)););else{yr.innerHTML=s?`${e}`:e;const l=yr.content;if(s){const c=l.firstChild;for(;c.firstChild;)l.appendChild(c.firstChild);l.removeChild(c)}t.insertBefore(l,n)}return[i?i.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}};function pc(e,t,n){const s=e._vtc;s&&(t=(t?[t,...s]:[...s]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}function _c(e,t,n){const s=e.style,r=me(n);if(n&&!r){for(const o in n)_s(s,o,n[o]);if(t&&!me(t))for(const o in t)n[o]==null&&_s(s,o,"")}else{const o=s.display;r?t!==n&&(s.cssText=n):t&&e.removeAttribute("style"),"_vod"in e&&(s.display=o)}}const xr=/\s*!important$/;function _s(e,t,n){if(N(n))n.forEach(s=>_s(e,t,s));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const s=gc(e,t);xr.test(n)?e.setProperty(bt(s),n.replace(xr,""),"important"):e[s]=n}}const wr=["Webkit","Moz","ms"],Gn={};function gc(e,t){const n=Gn[t];if(n)return n;let s=Ke(t);if(s!=="filter"&&s in e)return Gn[t]=s;s=Mn(s);for(let r=0;r{let e=Date.now,t=!1;if(typeof window!="undefined"){Date.now()>document.createEvent("Event").timeStamp&&(e=()=>performance.now());const n=navigator.userAgent.match(/firefox\/(\d+)/i);t=!!(n&&Number(n[1])<=53)}return[e,t]})();let gs=0;const yc=Promise.resolve(),xc=()=>{gs=0},wc=()=>gs||(yc.then(xc),gs=qo());function Wo(e,t,n,s){e.addEventListener(t,n,s)}function Cc(e,t,n,s){e.removeEventListener(t,n,s)}function kc(e,t,n,s,r=null){const o=e._vei||(e._vei={}),i=o[t];if(s&&i)i.value=s;else{const[l,c]=$c(t);if(s){const u=o[t]=Ec(s,r);Wo(e,l,u,c)}else i&&(Cc(e,l,i,c),o[t]=void 0)}}const kr=/(?:Once|Passive|Capture)$/;function $c(e){let t;if(kr.test(e)){t={};let n;for(;n=e.match(kr);)e=e.slice(0,e.length-n[0].length),t[n[0].toLowerCase()]=!0}return[bt(e.slice(2)),t]}function Ec(e,t){const n=s=>{const r=s.timeStamp||qo();(bc||r>=n.attached-1)&&Pe(Tc(s,n.value),t,5,[s])};return n.value=e,n.attached=wc(),n}function Tc(e,t){if(N(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(s=>r=>!r._stopped&&s&&s(r))}else return t}const $r=/^on[a-z]/,Ac=(e,t,n,s,r=!1,o,i,l,c)=>{t==="class"?pc(e,s,r):t==="style"?_c(e,n,s):Gt(t)?xs(t)||kc(e,t,n,s,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Lc(e,t,s,r))?vc(e,t,s,o,i,l,c):(t==="true-value"?e._trueValue=s:t==="false-value"&&(e._falseValue=s),mc(e,t,s,r))};function Lc(e,t,n,s){return s?!!(t==="innerHTML"||t==="textContent"||t in e&&$r.test(t)&&K(n)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||$r.test(t)&&me(n)?!1:t in e}const et="transition",Ht="animation",js=(e,{slots:t})=>lt(wo,Sc(e),t);js.displayName="Transition";const Vo={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String};js.props=be({},wo.props,Vo);const at=(e,t=[])=>{N(e)?e.forEach(n=>n(...t)):e&&e(...t)},Er=e=>e?N(e)?e.some(t=>t.length>1):e.length>1:!1;function Sc(e){const t={};for(const $ in e)$ in Vo||(t[$]=e[$]);if(e.css===!1)return t;const{name:n="v",type:s,duration:r,enterFromClass:o=`${n}-enter-from`,enterActiveClass:i=`${n}-enter-active`,enterToClass:l=`${n}-enter-to`,appearFromClass:c=o,appearActiveClass:u=i,appearToClass:d=l,leaveFromClass:p=`${n}-leave-from`,leaveActiveClass:_=`${n}-leave-active`,leaveToClass:k=`${n}-leave-to`}=e,T=Pc(r),R=T&&T[0],g=T&&T[1],{onBeforeEnter:y,onEnter:M,onEnterCancelled:I,onLeave:D,onLeaveCancelled:z,onBeforeAppear:Q=y,onAppear:S=M,onAppearCancelled:Z=I}=t,q=($,W,le)=>{kt($,W?d:l),kt($,W?u:i),le&&le()},G=($,W)=>{kt($,k),kt($,_),W&&W()},se=$=>(W,le)=>{const yt=$?S:M,ve=()=>q(W,$,le);at(yt,[W,ve]),Tr(()=>{kt(W,$?c:o),tt(W,$?d:l),Er(yt)||Ar(W,s,R,ve)})};return be(t,{onBeforeEnter($){at(y,[$]),tt($,o),tt($,i)},onBeforeAppear($){at(Q,[$]),tt($,c),tt($,u)},onEnter:se(!1),onAppear:se(!0),onLeave($,W){const le=()=>G($,W);tt($,p),Oc(),tt($,_),Tr(()=>{kt($,p),tt($,k),Er(D)||Ar($,s,g,le)}),at(D,[$,le])},onEnterCancelled($){q($,!1),at(I,[$])},onAppearCancelled($){q($,!0),at(Z,[$])},onLeaveCancelled($){G($),at(z,[$])}})}function Pc(e){if(e==null)return null;if(ae(e))return[es(e.enter),es(e.leave)];{const t=es(e);return[t,t]}}function es(e){return $s(e)}function tt(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e._vtc||(e._vtc=new Set)).add(t)}function kt(e,t){t.split(/\s+/).forEach(s=>s&&e.classList.remove(s));const{_vtc:n}=e;n&&(n.delete(t),n.size||(e._vtc=void 0))}function Tr(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let Ic=0;function Ar(e,t,n,s){const r=e._endId=++Ic,o=()=>{r===e._endId&&s()};if(n)return setTimeout(o,n);const{type:i,timeout:l,propCount:c}=Mc(e,t);if(!i)return s();const u=i+"end";let d=0;const p=()=>{e.removeEventListener(u,_),o()},_=k=>{k.target===e&&++d>=c&&p()};setTimeout(()=>{d(n[T]||"").split(", "),r=s(et+"Delay"),o=s(et+"Duration"),i=Lr(r,o),l=s(Ht+"Delay"),c=s(Ht+"Duration"),u=Lr(l,c);let d=null,p=0,_=0;t===et?i>0&&(d=et,p=i,_=o.length):t===Ht?u>0&&(d=Ht,p=u,_=c.length):(p=Math.max(i,u),d=p>0?i>u?et:Ht:null,_=d?d===et?o.length:c.length:0);const k=d===et&&/\b(transform|all)(,|$)/.test(n[et+"Property"]);return{type:d,timeout:p,propCount:_,hasTransform:k}}function Lr(e,t){for(;e.lengthSr(n)+Sr(e[s])))}function Sr(e){return Number(e.slice(0,-1).replace(",","."))*1e3}function Oc(){return document.body.offsetHeight}const Pr=e=>{const t=e.props["onUpdate:modelValue"];return N(t)?n=>hn(t,n):t},Fc={deep:!0,created(e,{value:t,modifiers:{number:n}},s){const r=Sn(t);Wo(e,"change",()=>{const o=Array.prototype.filter.call(e.options,i=>i.selected).map(i=>n?$s($n(i)):$n(i));e._assign(e.multiple?r?new Set(o):o:o[0])}),e._assign=Pr(s)},mounted(e,{value:t}){Ir(e,t)},beforeUpdate(e,t,n){e._assign=Pr(n)},updated(e,{value:t}){Ir(e,t)}};function Ir(e,t){const n=e.multiple;if(!(n&&!N(t)&&!Sn(t))){for(let s=0,r=e.options.length;s-1:o.selected=t.has(i);else if(Ln($n(o),t)){e.selectedIndex!==s&&(e.selectedIndex=s);return}}!n&&e.selectedIndex!==-1&&(e.selectedIndex=-1)}}function $n(e){return"_value"in e?e._value:e.value}const Rc=["ctrl","shift","alt","meta"],Nc={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Rc.some(n=>e[`${n}Key`]&&!t.includes(n))},Mr=(e,t)=>(n,...s)=>{for(let r=0;rn=>{if(!("key"in n))return;const s=bt(n.key);if(t.some(r=>r===s||Hc[r]===s))return e(n)},Dc=be({patchProp:Ac},hc);let ts,Or=!1;function Uc(){return ts=Or?ts:ql(Dc),Or=!0,ts}const jc=(...e)=>{const t=Uc().createApp(...e),{mount:n}=t;return t.mount=s=>{const r=Kc(s);if(r)return n(r,!0,r instanceof SVGElement)},t};function Kc(e){return me(e)?document.querySelector(e):e}var qc='{"lang":"en-US","title":"Knex.js","description":"Beta knex.js documentation.","base":"/","head":[],"themeConfig":{"logo":"/knex-logo.png","repo":"knex/knex","docsRepo":"knex/documentation","docsDir":"src","docsBranch":"main","editLinks":true,"editLinkText":"Edit this page on GitHub","lastUpdated":"Last Updated","nav":[{"text":"Guide","link":"/guide/","activeMatch":"^/guide/"},{"text":"F.A.Q.","link":"/faq/"},{"text":"Changelog","link":"/changelog.html"}],"sidebar":{"/guide/":[{"text":"Installation","link":"/guide/"},{"text":"Query Builder","link":"/guide/query-builder"},{"text":"Transactions","link":"/guide/transactions"},{"text":"Schema Builder","link":"/guide/schema-builder"},{"text":"Raw","link":"/guide/raw"},{"text":"Ref","link":"/guide/ref"},{"text":"Utility","link":"/guide/utility"},{"text":"Interfaces","link":"/guide/interfaces"},{"text":"Migrations","link":"/guide/migrations"},{"text":"Extending","link":"/guide/extending"}],"/faq/":[{"text":"F.A.Q.","link":"/faq/"},{"text":"Recipes","link":"/faq/recipes"},{"text":"Support","link":"/faq/support"}]},"algolia":{"appId":"V7E3EHUPD6","apiKey":"44b5077836c1c8fba0f364383dde7fb4","indexName":"knex","initialQuery":""}},"locales":{},"langs":{},"scrollOffset":90}';const zo=/^https?:/i,Re=typeof window!="undefined";function Wc(e,t){t.sort((n,s)=>{const r=s.split("/").length-n.split("/").length;return r!==0?r:s.length-n.length});for(const n of t)if(e.startsWith(n))return n}function Fr(e,t){const n=Wc(t,Object.keys(e));return n?e[n]:void 0}function Vc(e){const{locales:t}=e.themeConfig||{},n=e.locales;return t&&n?Object.keys(t).reduce((s,r)=>(s[r]={label:t[r].label,lang:n[r].lang},s),{}):{}}function zc(e,t){t=Zc(e,t);const n=Fr(e.locales||{},t),s=Fr(e.themeConfig.locales||{},t);return Object.assign({},e,n,{themeConfig:Object.assign({},e.themeConfig,s,{locales:{}}),lang:(n||e).lang,locales:{},langs:Vc(e)})}function Zc(e,t){if(!Re)return t;const n=e.base,s=n.endsWith("/")?n.slice(0,-1):n;return t.slice(s.length)}const Zo=Symbol(),sn=Gi(Yc(qc));function Yc(e){return JSON.parse(e)}function Jc(e){const t=V(()=>zc(sn.value,e.path));return{site:t,theme:V(()=>t.value.themeConfig),page:V(()=>e.data),frontmatter:V(()=>e.data.frontmatter),lang:V(()=>t.value.lang),localePath:V(()=>{const{langs:n,lang:s}=t.value,r=Object.keys(n).find(o=>n[o].lang===s);return Mt(r||"/")}),title:V(()=>e.data.title?e.data.title+" | "+t.value.title:t.value.title),description:V(()=>e.data.description||t.value.description)}}function ye(){const e=St(Zo);if(!e)throw new Error("vitepress data not properly injected in app");return e}function Qc(e,t){return`${e}${t}`.replace(/\/+/g,"/")}function Mt(e){return zo.test(e)?e:Qc(sn.value.base,e)}function Yo(e){let t=e.replace(/\.html$/,"");if(t=decodeURIComponent(t),t.endsWith("/")&&(t+="index"),Re){const n="/";t=t.slice(n.length).replace(/\//g,"_")+".md";const s=__VP_HASH_MAP__[t.toLowerCase()];t=`${n}assets/${t}.${s}.js`}else t=`./${t.slice(1).replace(/\//g,"_")}.md.js`;return t}const Jo=Symbol(),Rr="http://a.com",Qo={relativePath:"",title:"404",description:"Not Found",headers:[],frontmatter:{},lastUpdated:0},Xc=()=>({path:"/",component:null,data:Qo});function Gc(e,t){const n=Fn(Xc());function s(i=Re?location.href:"/"){const l=new URL(i,Rr);return!l.pathname.endsWith("/")&&!l.pathname.endsWith(".html")&&(l.pathname+=".html",i=l.pathname+l.search+l.hash),Re&&(history.replaceState({scrollPosition:window.scrollY},document.title),history.pushState(null,"",i)),o(i)}let r=null;async function o(i,l=0,c=!1){const u=new URL(i,Rr),d=r=u.pathname;try{let p=e(d);if("then"in p&&typeof p.then=="function"&&(p=await p),r===d){r=null;const{default:_,__pageData:k}=p;if(!_)throw new Error(`Invalid route component: ${_}`);n.path=d,n.component=jt(_),n.data=jt(JSON.parse(k)),Re&&Rn(()=>{if(u.hash&&!l){let T=null;try{T=document.querySelector(decodeURIComponent(u.hash))}catch(R){console.warn(R)}if(T){Nr(T,u.hash);return}}window.scrollTo(0,l)})}}catch(p){if(p.message.match(/fetch/)||console.error(p),!c)try{const _=await fetch(sn.value.base+"hashmap.json");window.__VP_HASH_MAP__=await _.json(),await o(i,l,!0);return}catch{}r===d&&(r=null,n.path=d,n.component=t?jt(t):null,n.data=Qo)}}return Re&&(window.addEventListener("click",i=>{const l=i.target.closest("a");if(l){const{href:c,protocol:u,hostname:d,pathname:p,hash:_,target:k}=l,T=window.location,R=p.match(/\.\w+$/);!i.ctrlKey&&!i.shiftKey&&!i.altKey&&!i.metaKey&&k!=="_blank"&&u===T.protocol&&d===T.hostname&&!(R&&R[0]!==".html")&&(i.preventDefault(),p===T.pathname?_&&_!==T.hash&&(history.pushState(null,"",_),window.dispatchEvent(new Event("hashchange")),Nr(l,_,l.classList.contains("header-anchor"))):s(c))}},{capture:!0}),window.addEventListener("popstate",i=>{o(location.href,i.state&&i.state.scrollPosition||0)}),window.addEventListener("hashchange",i=>{i.preventDefault()})),{route:n,go:s}}function ea(){const e=St(Jo);if(!e)throw new Error("useRouter() is called without provider.");return e}function Je(){return ea().route}function Nr(e,t,n=!1){let s=null;try{s=e.classList.contains("header-anchor")?e:document.querySelector(decodeURIComponent(t))}catch(r){console.warn(r)}if(s){let r=sn.value.scrollOffset;typeof r=="string"&&(r=document.querySelector(r).getBoundingClientRect().bottom+24);const o=parseInt(window.getComputedStyle(s).paddingTop,10),i=window.scrollY+s.getBoundingClientRect().top-r+o;!n||Math.abs(i-window.scrollY)>window.innerHeight?window.scrollTo(0,i):window.scrollTo({left:0,top:i,behavior:"smooth"})}}function ta(e,t){let n=[],s=!0;const r=o=>{if(s){s=!1;return}const i=[],l=Math.min(n.length,o.length);for(let c=0;cdocument.head.removeChild(c)),o.slice(l).forEach(c=>{const u=Hr(c);document.head.appendChild(u),i.push(u)}),n=i};yo(()=>{const o=e.data,i=t.value,l=o&&o.title,c=o&&o.description,u=o&&o.frontmatter.head;document.title=(l?l+" | ":"")+i.title,document.querySelector("meta[name=description]").setAttribute("content",c||i.description),r([...u?sa(u):[]])})}function Hr([e,t,n]){const s=document.createElement(e);for(const r in t)s.setAttribute(r,t[r]);return n&&(s.innerHTML=n),s}function na(e){return e[0]==="meta"&&e[1]&&e[1].name==="description"}function sa(e){return e.filter(t=>!na(t))}const ra=ue({name:"VitePressContent",setup(){const e=Je();return()=>lt("div",{style:{position:"relative"}},[e.component?lt(e.component):null])}});var fe=(e,t)=>{const n=e.__vccOpts||e;for(const[s,r]of t)n[s]=r;return n};const oa=/#.*$/,ia=/(index)?\.(md|html)$/,En=/\/$/,la=/^[a-z]+:/i;function Ks(e){return Array.isArray(e)}function qs(e){return la.test(e)}function ca(e,t){if(t===void 0)return!1;const n=Br(`/${e.data.relativePath}`),s=Br(t);return n===s}function Br(e){return decodeURI(e).replace(oa,"").replace(ia,"")}function aa(e,t){const n=e.endsWith("/"),s=t.startsWith("/");return n&&s?e.slice(0,-1)+t:!n&&!s?`${e}/${t}`:e+t}function ms(e){return/^\//.test(e)?e:`/${e}`}function Xo(e){return e.replace(/(index)?(\.(md|html))?$/,"")||"/"}function ua(e){return e===!1||e==="auto"||Ks(e)}function fa(e){return e.children!==void 0}function da(e){return Ks(e)?e.length===0:!e}function Ws(e,t){if(ua(e))return e;t=ms(t);for(const n in e)if(t.startsWith(ms(n)))return e[n];return"auto"}function Go(e){return e.reduce((t,n)=>(n.link&&t.push({text:n.text,link:Xo(n.link)}),fa(n)&&(t=[...t,...Go(n.children)]),t),[])}function ei(e){const t=Je(),n=qs(e.value.link);return{props:V(()=>{const r=Dr(`/${t.data.relativePath}`);let o=!1;if(e.value.activeMatch)o=new RegExp(e.value.activeMatch).test(r);else{const i=Dr(e.value.link);o=i==="/"?i===r:r.startsWith(i)}return{class:{active:o,isExternal:n},href:n?e.value.link:Mt(e.value.link),target:e.value.target||(n?"_blank":null),rel:e.value.rel||(n?"noopener noreferrer":null),"aria-label":e.value.ariaLabel}}),isExternal:n}}function Dr(e){return e.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\.(html|md)$/,"").replace(/\/index$/,"/")}const ha={},pa={class:"icon outbound",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},_a=U("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"},null,-1),ga=U("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"},null,-1),ma=[_a,ga];function va(e,t){return L(),H("svg",pa,ma)}var Vs=fe(ha,[["render",va]]);const ba={class:"nav-link"},ya=ue({props:{item:null},setup(e){const n=ho(e),{props:s,isExternal:r}=ei(n.item);return(o,i)=>(L(),H("div",ba,[U("a",Us({class:"item"},x(s)),[nn(Ce(e.item.text)+" ",1),x(r)?(L(),ge(Vs,{key:0})):ee("",!0)],16)]))}});var Tn=fe(ya,[["__scopeId","data-v-b8818f8c"]]);const xa={key:0,class:"home-hero"},wa={key:0,class:"figure"},Ca=["src","alt"],ka={key:1,id:"main-title",class:"title"},$a={key:2,class:"tagline"},Ea=ue({setup(e){const{site:t,frontmatter:n}=ye(),s=V(()=>{const{heroImage:i,heroText:l,tagline:c,actionLink:u,actionText:d}=n.value;return i||l||c||u&&d}),r=V(()=>n.value.heroText||t.value.title),o=V(()=>n.value.tagline||t.value.description);return(i,l)=>x(s)?(L(),H("header",xa,[x(n).heroImage?(L(),H("figure",wa,[U("img",{class:"image",src:x(Mt)(x(n).heroImage),alt:x(n).heroAlt},null,8,Ca)])):ee("",!0),x(r)?(L(),H("h1",ka,Ce(x(r)),1)):ee("",!0),x(o)?(L(),H("p",$a,Ce(x(o)),1)):ee("",!0),x(n).actionLink&&x(n).actionText?(L(),ge(Tn,{key:3,item:{link:x(n).actionLink,text:x(n).actionText},class:"action"},null,8,["item"])):ee("",!0),x(n).altActionLink&&x(n).altActionText?(L(),ge(Tn,{key:4,item:{link:x(n).altActionLink,text:x(n).altActionText},class:"action alt"},null,8,["item"])):ee("",!0)])):ee("",!0)}});var Ta=fe(Ea,[["__scopeId","data-v-370f18c0"]]);const Aa={key:0,class:"home-features"},La={class:"wrapper"},Sa={class:"container"},Pa={class:"features"},Ia={key:0,class:"title"},Ma={key:1,class:"details"},Oa=ue({setup(e){const{frontmatter:t}=ye(),n=V(()=>t.value.features&&t.value.features.length>0),s=V(()=>t.value.features?t.value.features:[]);return(r,o)=>x(n)?(L(),H("div",Aa,[U("div",La,[U("div",Sa,[U("div",Pa,[(L(!0),H(he,null,jn(x(s),(i,l)=>(L(),H("section",{key:l,class:"feature"},[i.title?(L(),H("h2",Ia,Ce(i.title),1)):ee("",!0),i.details?(L(),H("p",Ma,Ce(i.details),1)):ee("",!0)]))),128))])])])])):ee("",!0)}});var Fa=fe(Oa,[["__scopeId","data-v-e39c13e0"]]);const Ra={key:0,class:"footer"},Na={class:"container"},Ha={class:"text"},Ba=ue({setup(e){const{frontmatter:t}=ye();return(n,s)=>x(t).footer?(L(),H("footer",Ra,[U("div",Na,[U("p",Ha,Ce(x(t).footer),1)])])):ee("",!0)}});var Da=fe(Ba,[["__scopeId","data-v-30918238"]]);const Ua={class:"home","aria-labelledby":"main-title"},ja={class:"home-content"},Ka=ue({setup(e){return(t,n)=>{const s=xn("Content");return L(),H("main",Ua,[B(Ta),de(t.$slots,"hero",{},void 0,!0),B(Fa),U("div",ja,[B(s)]),de(t.$slots,"features",{},void 0,!0),B(Da),de(t.$slots,"footer",{},void 0,!0)])}}});var qa=fe(Ka,[["__scopeId","data-v-10122c92"]]);const Wa=["href","aria-label"],Va=["src"],za=ue({setup(e){const{site:t,theme:n,localePath:s}=ye();return(r,o)=>(L(),H("a",{class:"nav-bar-title",href:x(s),"aria-label":`${x(t).title}, back to home`},[x(n).logo?(L(),H("img",{key:0,class:"logo",src:x(Mt)(x(n).logo),alt:"Logo"},null,8,Va)):ee("",!0),nn(" "+Ce(x(t).title),1)],8,Wa))}});var Za=fe(za,[["__scopeId","data-v-cc01ef16"]]);function Ya(){const{site:e,localePath:t,theme:n}=ye();return V(()=>{const s=e.value.langs,r=Object.keys(s);if(r.length<2)return null;const i=Je().path.replace(t.value,""),l=r.map(u=>({text:s[u].label,link:`${u}${i}`}));return{text:n.value.selectText||"Languages",items:l}})}const Ja=["GitHub","GitLab","Bitbucket"].map(e=>[e,new RegExp(e,"i")]);function Qa(){const{site:e}=ye();return V(()=>{const t=e.value.themeConfig,n=t.docsRepo||t.repo;if(!n)return null;const s=Xa(n);return{text:Ga(s,t.repoLabel),link:s}})}function Xa(e){return zo.test(e)?e:`https://github.com/${e}`}function Ga(e,t){if(t)return t;const n=e.match(/^https?:\/\/[^/]+/);if(!n)return"Source";const s=Ja.find(([r,o])=>o.test(n[0]));return s&&s[0]?s[0]:"Source"}const eu=e=>(Rs("data-v-bbc27490"),e=e(),Ns(),e),tu={class:"nav-dropdown-link-item"},nu=eu(()=>U("span",{class:"arrow"},null,-1)),su={class:"text"},ru={class:"icon"},ou=ue({props:{item:null},setup(e){const n=ho(e),{props:s,isExternal:r}=ei(n.item);return(o,i)=>(L(),H("div",tu,[U("a",Us({class:"item"},x(s)),[nu,U("span",su,Ce(e.item.text),1),U("span",ru,[x(r)?(L(),ge(Vs,{key:0})):ee("",!0)])],16)]))}});var iu=fe(ou,[["__scopeId","data-v-bbc27490"]]);const lu=["aria-label"],cu={class:"button-text"},au={class:"dialog"},uu=ue({props:{item:null},setup(e){const t=Je(),n=je(!1);He(()=>t.path,()=>{n.value=!1});function s(){n.value=!n.value}return(r,o)=>(L(),H("div",{class:mt(["nav-dropdown-link",{open:n.value}])},[U("button",{class:"button","aria-label":e.item.ariaLabel,onClick:s},[U("span",cu,Ce(e.item.text),1),U("span",{class:mt(["button-arrow",n.value?"down":"right"])},null,2)],8,lu),U("ul",au,[(L(!0),H(he,null,jn(e.item.items,i=>(L(),H("li",{key:i.text,class:"dialog-item"},[B(iu,{item:i},null,8,["item"])]))),128))])],2))}});var Ur=fe(uu,[["__scopeId","data-v-56bf3a3f"]]);const fu={key:0,class:"nav-links"},du={key:1,class:"item"},hu={key:2,class:"item"},pu=ue({setup(e){const{theme:t}=ye(),n=Ya(),s=Qa(),r=V(()=>t.value.nav||s.value||n.value);return(o,i)=>x(r)?(L(),H("nav",fu,[x(t).nav?(L(!0),H(he,{key:0},jn(x(t).nav,l=>(L(),H("div",{key:l.text,class:"item"},[l.items?(L(),ge(Ur,{key:0,item:l},null,8,["item"])):(L(),ge(Tn,{key:1,item:l},null,8,["item"]))]))),128)):ee("",!0),x(n)?(L(),H("div",du,[B(Ur,{item:x(n)},null,8,["item"])])):ee("",!0),x(s)?(L(),H("div",hu,[B(Tn,{item:x(s)},null,8,["item"])])):ee("",!0)])):ee("",!0)}});var ti=fe(pu,[["__scopeId","data-v-eab3edfe"]]);const _u={emits:["toggle"]},gu=U("svg",{class:"icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",role:"img",viewBox:"0 0 448 512"},[U("path",{fill:"currentColor",d:"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z",class:""})],-1),mu=[gu];function vu(e,t,n,s,r,o){return L(),H("div",{class:"sidebar-button",onClick:t[0]||(t[0]=i=>e.$emit("toggle"))},mu)}var bu=fe(_u,[["render",vu]]);const yu=e=>(Rs("data-v-675d8756"),e=e(),Ns(),e),xu={class:"nav-bar"},wu=yu(()=>U("div",{class:"flex-grow"},null,-1)),Cu={class:"nav"},ku=ue({emits:["toggle"],setup(e){return(t,n)=>(L(),H("header",xu,[B(bu,{onToggle:n[0]||(n[0]=s=>t.$emit("toggle"))}),B(Za),wu,U("div",Cu,[B(ti)]),de(t.$slots,"search",{},void 0,!0)]))}});var $u=fe(ku,[["__scopeId","data-v-675d8756"]]);function Eu(){let e=null,t=null;const n=Pu(s,300);function s(){const i=Tu(),l=Au(i);for(let c=0;c ul > li");l&&l!==t.parentElement?(e=l.querySelector("a"),e&&e.classList.add("active")):e=null}function o(i){i&&i.classList.remove("active")}Ye(()=>{s(),window.addEventListener("scroll",n)}),To(()=>{r(decodeURIComponent(location.hash))}),Dn(()=>{window.removeEventListener("scroll",n)})}function Tu(){return[].slice.call(document.querySelectorAll(".sidebar a.sidebar-link-item"))}function Au(e){return[].slice.call(document.querySelectorAll(".header-anchor")).filter(t=>e.some(n=>n.hash===t.hash))}function Lu(){return document.querySelector(".nav-bar").offsetHeight}function jr(e){const t=Lu();return e.parentElement.offsetTop-t-15}function Su(e,t,n){const s=window.scrollY;return e===0&&s===0?[!0,null]:s{n&&clearTimeout(n),s?n=setTimeout(e,t):(e(),s=!0,setTimeout(()=>{s=!1},t))}}function Iu(){const e=Je(),{site:t}=ye();return Eu(),V(()=>{const n=e.data.headers,s=e.data.frontmatter.sidebar,r=e.data.frontmatter.sidebarDepth;if(s===!1)return[];if(s==="auto")return Kr(n,r);const o=Ws(t.value.themeConfig.sidebar,e.data.relativePath);return o===!1?[]:o==="auto"?Kr(n,r):o})}function Kr(e,t){const n=[];if(e===void 0)return[];let s;return e.forEach(({level:r,title:o,slug:i})=>{if(r-1>t)return;const l={text:o,link:`#${i}`};r===2?(s=l,n.push(l)):s&&(s.children||(s.children=[])).push(l)}),n}const ni=e=>{const t=Je(),{site:n,frontmatter:s}=ye(),r=e.depth||1,o=s.value.sidebarDepth||1/0,i=t.data.headers,l=e.item.text,c=Mu(n.value.base,e.item.link),u=e.item.children,d=ca(t,e.item.link),p=r0?lt("ul",{class:"sidebar-links"},t.map(r=>lt(ni,{item:r,depth:s}))):e&&n?si(!1,Ou(n),void 0,s):null}function Ou(e){return ri(Fu(e))}function Fu(e){e=e.map(n=>Object.assign({},n));let t;return e.forEach(n=>{n.level===2?t=n:t&&(t.children||(t.children=[])).push(n)}),e.filter(n=>n.level===2)}function ri(e){return e.map(t=>({text:t.title,link:`#${t.slug}`,children:t.children?ri(t.children):void 0}))}const Ru={key:0,class:"sidebar-links"},Nu=ue({setup(e){const t=Iu();return(n,s)=>x(t).length>0?(L(),H("ul",Ru,[(L(!0),H(he,null,jn(x(t),r=>(L(),ge(x(ni),{item:r},null,8,["item"]))),256))])):ee("",!0)}});const Hu=ue({props:{open:{type:Boolean}},setup(e){return(t,n)=>(L(),H("aside",{class:mt(["sidebar",{open:e.open}])},[B(ti,{class:"nav"}),de(t.$slots,"sidebar-top",{},void 0,!0),B(Nu),de(t.$slots,"sidebar-bottom",{},void 0,!0)],2))}});var Bu=fe(Hu,[["__scopeId","data-v-83e92a68"]]);const Du=/bitbucket.org/;function Uu(){const{page:e,theme:t,frontmatter:n}=ye(),s=V(()=>{const{repo:o,docsDir:i="",docsBranch:l="master",docsRepo:c=o,editLinks:u}=t.value,d=n.value.editLink!=null?n.value.editLink:u,{relativePath:p}=e.value;return!d||!p||!o?null:ju(o,c,i,l,p)}),r=V(()=>t.value.editLinkText||"Edit this page");return{url:s,text:r}}function ju(e,t,n,s,r){return Du.test(e)?qu(e,t,n,s,r):Ku(e,t,n,s,r)}function Ku(e,t,n,s,r){return(qs(t)?t:`https://github.com/${t}`).replace(En,"")+`/edit/${s}/`+(n?n.replace(En,"")+"/":"")+r}function qu(e,t,n,s,r){return(qs(t)?t:e).replace(En,"")+`/src/${s}/`+(n?n.replace(En,"")+"/":"")+r+`?mode=edit&spa=0&at=${s}&fileviewer=file-view-default`}const Wu={class:"edit-link"},Vu=["href"],zu=ue({setup(e){const{url:t,text:n}=Uu();return(s,r)=>(L(),H("div",Wu,[x(t)?(L(),H("a",{key:0,class:"link",href:x(t),target:"_blank",rel:"noopener noreferrer"},[nn(Ce(x(n))+" ",1),B(Vs,{class:"icon"})],8,Vu)):ee("",!0)]))}});var Zu=fe(zu,[["__scopeId","data-v-1ed99556"]]);const Yu={key:0,class:"last-updated"},Ju={class:"prefix"},Qu={class:"datetime"},Xu=ue({setup(e){const{theme:t,page:n}=ye(),s=V(()=>{const i=t.value.lastUpdated;return i!==void 0&&i!==!1&&n.value.lastUpdated!==0}),r=V(()=>{const i=t.value.lastUpdated;return i===!0?"Last Updated":i}),o=je("");return Ye(()=>{yo(()=>{o.value=new Date(n.value.lastUpdated).toLocaleString("en-US")})}),(i,l)=>x(s)?(L(),H("p",Yu,[U("span",Ju,Ce(x(r))+":",1),U("span",Qu,Ce(o.value),1)])):ee("",!0)}});var Gu=fe(Xu,[["__scopeId","data-v-abce3432"]]);const ef={class:"page-footer"},tf={class:"edit"},nf={class:"updated"},sf=ue({setup(e){const{page:t}=ye();return(n,s)=>(L(),H("footer",ef,[U("div",tf,[B(Zu)]),U("div",nf,[x(t).lastUpdated?(L(),ge(Gu,{key:0})):ee("",!0)])]))}});var rf=fe(sf,[["__scopeId","data-v-07c132fc"]]);function of(){const{page:e,theme:t}=ye(),n=V(()=>Xo(ms(e.value.relativePath))),s=V(()=>{const c=Ws(t.value.sidebar,n.value);return Ks(c)?Go(c):[]}),r=V(()=>s.value.findIndex(c=>c.link===n.value)),o=V(()=>{if(t.value.nextLinks!==!1&&r.value>-1&&r.value{if(t.value.prevLinks!==!1&&r.value>0)return s.value[r.value-1]}),l=V(()=>!!o.value||!!i.value);return{next:o,prev:i,hasLinks:l}}const lf={},cf={xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},af=U("path",{d:"M19,11H7.4l5.3-5.3c0.4-0.4,0.4-1,0-1.4s-1-0.4-1.4,0l-7,7c-0.1,0.1-0.2,0.2-0.2,0.3c-0.1,0.2-0.1,0.5,0,0.8c0.1,0.1,0.1,0.2,0.2,0.3l7,7c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3c0.4-0.4,0.4-1,0-1.4L7.4,13H19c0.6,0,1-0.4,1-1S19.6,11,19,11z"},null,-1),uf=[af];function ff(e,t){return L(),H("svg",cf,uf)}var df=fe(lf,[["render",ff]]);const hf={},pf={xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},_f=U("path",{d:"M19.9,12.4c0.1-0.2,0.1-0.5,0-0.8c-0.1-0.1-0.1-0.2-0.2-0.3l-7-7c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l5.3,5.3H5c-0.6,0-1,0.4-1,1s0.4,1,1,1h11.6l-5.3,5.3c-0.4,0.4-0.4,1,0,1.4c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3l7-7C19.8,12.6,19.9,12.5,19.9,12.4z"},null,-1),gf=[_f];function mf(e,t){return L(),H("svg",pf,gf)}var vf=fe(hf,[["render",mf]]);const bf={key:0,class:"next-and-prev-link"},yf={class:"container"},xf={class:"prev"},wf=["href"],Cf={class:"text"},kf={class:"next"},$f=["href"],Ef={class:"text"},Tf=ue({setup(e){const{hasLinks:t,prev:n,next:s}=of();return(r,o)=>x(t)?(L(),H("div",bf,[U("div",yf,[U("div",xf,[x(n)?(L(),H("a",{key:0,class:"link",href:x(Mt)(x(n).link)},[B(df,{class:"icon icon-prev"}),U("span",Cf,Ce(x(n).text),1)],8,wf)):ee("",!0)]),U("div",kf,[x(s)?(L(),H("a",{key:0,class:"link",href:x(Mt)(x(s).link)},[U("span",Ef,Ce(x(s).text),1),B(vf,{class:"icon icon-next"})],8,$f)):ee("",!0)])])])):ee("",!0)}});var Af=fe(Tf,[["__scopeId","data-v-38ede35f"]]);const Lf={class:"page"},Sf={class:"container"},Pf=ue({setup(e){return(t,n)=>{const s=xn("Content");return L(),H("main",Lf,[U("div",Sf,[de(t.$slots,"top",{},void 0,!0),B(s,{class:"content"}),B(rf),B(Af),de(t.$slots,"bottom",{},void 0,!0)])])}}});var If=fe(Pf,[["__scopeId","data-v-7eddb2c4"]]);const Mf={key:0,id:"ads-container"},Of=ue({setup(e){const t=()=>null,n=t,s=t,r=$o(()=>vs(()=>import("./chunks/AlgoliaSearchBox.3caa7b39.js"),["assets/chunks/AlgoliaSearchBox.3caa7b39.js","assets/chunks/index.d76f8212.js"])),o=Je(),{site:i,page:l,theme:c,frontmatter:u}=ye(),d=V(()=>!!u.value.customLayout),p=V(()=>!!u.value.home),_=V(()=>Object.keys(i.value.langs).length>1),k=V(()=>{const I=c.value;return u.value.navbar===!1||I.navbar===!1?!1:i.value.title||I.logo||I.repo||I.nav}),T=je(!1),R=V(()=>u.value.home||u.value.sidebar===!1?!1:!da(Ws(c.value.sidebar,o.data.relativePath))),g=I=>{T.value=typeof I=="boolean"?I:!T.value},y=g.bind(null,!1);He(o,y);const M=V(()=>[{"no-navbar":!k.value,"sidebar-open":T.value,"no-sidebar":!R.value}]);return(I,D)=>{const z=xn("Content"),Q=xn("Debug");return L(),H(he,null,[U("div",{class:mt(["theme",x(M)])},[x(k)?(L(),ge($u,{key:0,onToggle:g},{search:Me(()=>[de(I.$slots,"navbar-search",{},()=>[x(c).algolia?(L(),ge(x(r),{key:0,options:x(c).algolia,multilang:x(_)},null,8,["options","multilang"])):ee("",!0)])]),_:3})):ee("",!0),B(Bu,{open:T.value},{"sidebar-top":Me(()=>[de(I.$slots,"sidebar-top")]),"sidebar-bottom":Me(()=>[de(I.$slots,"sidebar-bottom")]),_:3},8,["open"]),U("div",{class:"sidebar-mask",onClick:D[0]||(D[0]=S=>g(!1))}),x(d)?(L(),ge(z,{key:1})):x(p)?de(I.$slots,"home",{key:2},()=>[B(qa,null,{hero:Me(()=>[de(I.$slots,"home-hero")]),features:Me(()=>[de(I.$slots,"home-features")]),footer:Me(()=>[de(I.$slots,"home-footer")]),_:3})]):(L(),ge(If,{key:3},{top:Me(()=>[de(I.$slots,"page-top-ads",{},()=>[x(c).carbonAds&&x(c).carbonAds.carbon?(L(),H("div",Mf,[(L(),ge(x(n),{key:"carbon"+x(l).relativePath,code:x(c).carbonAds.carbon,placement:x(c).carbonAds.placement},null,8,["code","placement"]))])):ee("",!0)]),de(I.$slots,"page-top")]),bottom:Me(()=>[de(I.$slots,"page-bottom"),de(I.$slots,"page-bottom-ads",{},()=>[x(c).carbonAds&&x(c).carbonAds.custom?(L(),ge(x(s),{key:"custom"+x(l).relativePath,code:x(c).carbonAds.custom,placement:x(c).carbonAds.placement},null,8,["code","placement"])):ee("",!0)])]),_:3}))],2),B(Q)],64)}}}),Ff={class:"theme"},Rf=U("h1",null,"404",-1),Nf=["href"],Hf=ue({setup(e){const{site:t}=ye(),n=["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."];function s(){return n[Math.floor(Math.random()*n.length)]}return(r,o)=>(L(),H("div",Ff,[Rf,U("blockquote",null,Ce(s()),1),U("a",{href:x(t).base,"aria-label":"go to home"},"Take me home.",8,Nf)]))}}),oi={Layout:Of,NotFound:Hf};function Bf(e){const t=je("mysql");return hd.url||(He(t,n=>{localStorage.setItem("sql-dialect",n)}),Rn(()=>{const n=localStorage.getItem("sql-dialect");n&&(t.value=n)})),e.provide("dialect",t),Object.defineProperty(e.config.globalProperties,"$dialect",{get(){return t.value}}),{dialect:t}}function Df(){return{dialect:St("dialect")}}const Uf=Gl('',9),jf=[Uf],Kf={setup(e){const{dialect:t}=Df(),n=Je(),s=()=>{Rn(()=>{document.querySelectorAll("[data-dialect]").forEach(r=>{r.style.display="none"}),document.querySelectorAll(`[data-dialect="${t.value}"]`).forEach(r=>{r.style.display="block"})})};return He(n,s),He(t,s),Ye(s),(r,o)=>Dl((L(),H("select",{"onUpdate:modelValue":o[0]||(o[0]=i=>_e(t)?t.value=i:null),class:"sql-dropdown item nav-link"},jf,512)),[[Fc,x(t)]])}};const qn=e=>(Rs("data-v-f817ae2e"),e=e(),Ns(),e),qf=["onClick","onKeydown"],Wf={key:0,xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink","aria-hidden":"true",role:"img",class:"iconify iconify--ph",width:"32",height:"32",preserveAspectRatio:"xMidYMid meet",viewBox:"0 0 256 256"},Vf=qn(()=>U("path",{fill:"currentColor",d:"M216.7 152.6A91.9 91.9 0 0 1 103.4 39.3a92 92 0 1 0 113.3 113.3Z",opacity:".2"},null,-1)),zf=qn(()=>U("path",{fill:"currentColor",d:"M224.3 150.3a8.1 8.1 0 0 0-7.8-5.7l-2.2.4A84 84 0 0 1 111 41.6a5.7 5.7 0 0 0 .3-1.8a7.9 7.9 0 0 0-10.3-8.1a100 100 0 1 0 123.3 123.2a7.2 7.2 0 0 0 0-4.6ZM128 212A84 84 0 0 1 92.8 51.7a99.9 99.9 0 0 0 111.5 111.5A84.4 84.4 0 0 1 128 212Z"},null,-1)),Zf=[Vf,zf],Yf={key:1,xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink","aria-hidden":"true",role:"img",class:"iconify iconify--ph",width:"32",height:"32",preserveAspectRatio:"xMidYMid meet",viewBox:"0 0 256 256"},Jf=qn(()=>U("circle",{cx:"128",cy:"128",r:"60",fill:"currentColor",opacity:".2"},null,-1)),Qf=qn(()=>U("path",{fill:"currentColor",d:"M128 60a68 68 0 1 0 68 68a68.1 68.1 0 0 0-68-68Zm0 120a52 52 0 1 1 52-52a52 52 0 0 1-52 52Zm-8-144V16a8 8 0 0 1 16 0v20a8 8 0 0 1-16 0ZM43.1 54.5a8.1 8.1 0 1 1 11.4-11.4l14.1 14.2a8 8 0 0 1 0 11.3a8.1 8.1 0 0 1-11.3 0ZM36 136H16a8 8 0 0 1 0-16h20a8 8 0 0 1 0 16Zm32.6 51.4a8 8 0 0 1 0 11.3l-14.1 14.2a8.3 8.3 0 0 1-5.7 2.3a8.5 8.5 0 0 1-5.7-2.3a8.1 8.1 0 0 1 0-11.4l14.2-14.1a8 8 0 0 1 11.3 0ZM136 220v20a8 8 0 0 1-16 0v-20a8 8 0 0 1 16 0Zm76.9-18.5a8.1 8.1 0 0 1 0 11.4a8.5 8.5 0 0 1-5.7 2.3a8.3 8.3 0 0 1-5.7-2.3l-14.1-14.2a8 8 0 0 1 11.3-11.3ZM248 128a8 8 0 0 1-8 8h-20a8 8 0 0 1 0-16h20a8 8 0 0 1 8 8Zm-60.6-59.4a8 8 0 0 1 0-11.3l14.1-14.2a8.1 8.1 0 0 1 11.4 11.4l-14.2 14.1a8.1 8.1 0 0 1-11.3 0Z"},null,-1)),Xf=[Jf,Qf],Gf={setup(e){const t=je(!1),n=je("auto"),s=V(()=>n.value==="dark"||t.value&&n.value!=="light"),r=()=>{n.value=n.value==="dark"?"light":"dark",localStorage.setItem("color-scheme",n.value)};return He(s,()=>{document.documentElement.classList.toggle("dark",s.value)}),Ye(()=>{t.value=window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches,n.value=localStorage.getItem("color-scheme")||"auto",document.documentElement.classList.toggle("dark",s.value)}),(o,i)=>(L(),H("a",{class:"toggle-dark",tabindex:"0",role:"button",onClick:Mr(r,["prevent"]),onKeydown:Bc(Mr(r,["prevent"]),["enter"])},[B(js,{name:"fade",mode:"out-in"},{default:Me(()=>[x(s)?(L(),H("svg",Wf,Zf)):(L(),H("svg",Yf,Xf))]),_:1})],40,qf))}};var ed=fe(Gf,[["__scopeId","data-v-f817ae2e"]]);const td={setup(e){const{Layout:t}=oi,{site:n,theme:s}=ye(),r=$o(()=>vs(()=>import("./chunks/AlgoliaSearchBox.8f0df019.js"),["assets/chunks/AlgoliaSearchBox.8f0df019.js","assets/chunks/index.d76f8212.js"])),o=V(()=>Object.keys(n.value.langs).length>1);return(i,l)=>(L(),ge(x(t),null,{"navbar-search":Me(()=>[B(Kf),x(s).algolia?(L(),ge(x(r),{key:0,options:x(s).algolia,multilang:x(o)},null,8,["options","multilang"])):ee("",!0),B(ed)]),_:1}))}},nd={setup(e){return(t,n)=>(L(),H("div",null,[de(t.$slots,"default")]))}};globalThis.process=globalThis.process||{env:{}};var An={Layout:td,NotFound:oi.NotFound,enhanceApp({app:e}){Bf(e),e.component("SqlOutput",nd)}};const ns=new Set,ii=()=>document.createElement("link"),sd=e=>{const t=ii();t.rel="prefetch",t.href=e,document.head.appendChild(t)},rd=e=>{const t=new XMLHttpRequest;t.open("GET",e,t.withCredentials=!0),t.send()};let dn;const od=Re&&(dn=ii())&&dn.relList&&dn.relList.supports&&dn.relList.supports("prefetch")?sd:rd;function id(){if(!Re||!window.IntersectionObserver)return;let e;if((e=navigator.connection)&&(e.saveData||/2g/.test(e.effectiveType)))return;const t=window.requestIdleCallback||setTimeout;let n=null;const s=()=>{n&&n.disconnect(),n=new IntersectionObserver(o=>{o.forEach(i=>{if(i.isIntersecting){const l=i.target;n.unobserve(l);const{pathname:c}=l;if(!ns.has(c)){ns.add(c);const u=Yo(c);od(u)}}})}),t(()=>{document.querySelectorAll("#app a").forEach(o=>{const{target:i,hostname:l,pathname:c}=o,u=c.match(/\.\w+$/);u&&u[0]!==".html"||i!=="_blank"&&l===location.hostname&&(c!==location.pathname?n.observe(o):ns.add(c))})})};Ye(s);const r=Je();He(()=>r.path,s),Dn(()=>{n&&n.disconnect()})}const ld=ue({setup(e,{slots:t}){const n=je(!1);return Ye(()=>{n.value=!0}),()=>n.value&&t.default?t.default():null}}),cd=An.NotFound||(()=>"404 Not Found"),ad={name:"VitePressApp",setup(){const{site:e}=ye();return Ye(()=>{He(()=>e.value.lang,t=>{document.documentElement.lang=t},{immediate:!0})}),id(),()=>lt(An.Layout)}};function ud(){const e=dd(),t=fd();t.provide(Jo,e);const n=Jc(e.route);return t.provide(Zo,n),t.component("Content",ra),t.component("ClientOnly",ld),t.component("Debug",()=>null),Object.defineProperty(t.config.globalProperties,"$frontmatter",{get(){return n.frontmatter.value}}),An.enhanceApp&&An.enhanceApp({app:t,router:e,siteData:sn}),{app:t,router:e,data:n}}function fd(){return jc(ad)}function dd(){let e=Re,t;return Gc(n=>{let s=Yo(n);return e&&(t=s),(e||t===s)&&(s=s.replace(/\.js$/,".lean.js")),Re?(e=!1,vs(()=>import(s),[])):require(s)},cd)}if(Re){const{app:e,router:t,data:n}=ud();t.go().then(()=>{ta(t.route,n.site),e.mount("#app")})}export{fe as _,Gl as a,ea as b,H as c,ud as createApp,ue as d,Ye as e,ye as f,rc as g,L as o,Je as u,He as w}; diff --git a/assets/changelog.md.997a0d36.js b/assets/changelog.md.997a0d36.js new file mode 100644 index 00000000..739d3328 --- /dev/null +++ b/assets/changelog.md.997a0d36.js @@ -0,0 +1 @@ +import{_ as e,c as r,o as n,a as t}from"./app.ef097145.js";const d='{"title":"Changelog","description":"","frontmatter":{},"headers":[{"level":2,"title":"Changelog","slug":"changelog"},{"level":3,"title":"2.5.1 - 12 July, 2023","slug":"_2-5-1-12-july-2023"},{"level":3,"title":"2.5.0 - 08 July, 2023","slug":"_2-5-0-08-july-2023"},{"level":3,"title":"2.4.2 - 22 January, 2023","slug":"_2-4-2-22-january-2023"},{"level":3,"title":"2.4.1 - 18 January, 2023","slug":"_2-4-1-18-january-2023"},{"level":3,"title":"2.4.0 - 6 January, 2023","slug":"_2-4-0-6-january-2023"},{"level":3,"title":"2.3.0 - 31 August, 2022","slug":"_2-3-0-31-august-2022"},{"level":3,"title":"2.2.0 - 18 July, 2022","slug":"_2-2-0-18-july-2022"},{"level":3,"title":"2.1.0 - 26 May, 2022","slug":"_2-1-0-26-may-2022"},{"level":3,"title":"2.0.0 - 21 April, 2022","slug":"_2-0-0-21-april-2022"},{"level":3,"title":"1.0.7 - 13 March, 2022","slug":"_1-0-7-13-march-2022"},{"level":3,"title":"1.0.6 - 12 March, 2022","slug":"_1-0-6-12-march-2022"},{"level":3,"title":"1.0.5 - 05 March, 2022","slug":"_1-0-5-05-march-2022"},{"level":3,"title":"1.0.4 - 13 March, 2022","slug":"_1-0-4-13-march-2022"},{"level":3,"title":"1.0.3 - 11 February, 2022","slug":"_1-0-3-11-february-2022"},{"level":3,"title":"1.0.2 - 02 February, 2022","slug":"_1-0-2-02-february-2022"},{"level":3,"title":"1.0.1 - 16 January, 2022","slug":"_1-0-1-16-january-2022"},{"level":3,"title":"1.0.0 - 16 January, 2022","slug":"_1-0-0-16-january-2022"},{"level":3,"title":"0.95.15 - 22 December, 2021","slug":"_0-95-15-22-december-2021"},{"level":3,"title":"0.95.14 - 09 November, 2021","slug":"_0-95-14-09-november-2021"},{"level":3,"title":"0.95.13 - 02 November, 2021","slug":"_0-95-13-02-november-2021"},{"level":3,"title":"0.95.12 - 28 October, 2021","slug":"_0-95-12-28-october-2021"},{"level":3,"title":"0.95.11 - 03 September, 2021","slug":"_0-95-11-03-september-2021"},{"level":3,"title":"0.95.10 - 20 August, 2021","slug":"_0-95-10-20-august-2021"},{"level":3,"title":"0.95.9 - 31 July, 2021","slug":"_0-95-9-31-july-2021"},{"level":3,"title":"0.95.8 - 25 July, 2021","slug":"_0-95-8-25-july-2021"},{"level":3,"title":"0.95.7 - 10 July, 2021","slug":"_0-95-7-10-july-2021"},{"level":3,"title":"0.95.6 - 17 May, 2021","slug":"_0-95-6-17-may-2021"},{"level":3,"title":"0.95.5 - 11 May, 2021","slug":"_0-95-5-11-may-2021"},{"level":3,"title":"0.95.4 - 26 March, 2021","slug":"_0-95-4-26-march-2021"},{"level":3,"title":"0.95.3 - 25 March, 2021","slug":"_0-95-3-25-march-2021"},{"level":3,"title":"0.95.2 - 11 March, 2021","slug":"_0-95-2-11-march-2021"},{"level":3,"title":"0.95.1 - 04 March, 2021","slug":"_0-95-1-04-march-2021"},{"level":3,"title":"0.95.0 - 03 March, 2021","slug":"_0-95-0-03-march-2021"},{"level":3,"title":"0.21.19 - 02 March, 2021","slug":"_0-21-19-02-march-2021"},{"level":3,"title":"0.21.18 - 22 February, 2021","slug":"_0-21-18-22-february-2021"},{"level":3,"title":"0.21.17 - 30 January, 2021","slug":"_0-21-17-30-january-2021"},{"level":3,"title":"0.21.16 - 17 January, 2021","slug":"_0-21-16-17-january-2021"},{"level":3,"title":"0.21.15 - 26 December, 2020","slug":"_0-21-15-26-december-2020"},{"level":3,"title":"0.21.14 - 18 December, 2020","slug":"_0-21-14-18-december-2020"},{"level":3,"title":"0.21.13 - 12 December, 2020","slug":"_0-21-13-12-december-2020"},{"level":3,"title":"0.21.12 - 02 November, 2020","slug":"_0-21-12-02-november-2020"},{"level":3,"title":"0.21.11 - 01 November, 2020","slug":"_0-21-11-01-november-2020"},{"level":3,"title":"0.21.10 - 31 October, 2020","slug":"_0-21-10-31-october-2020"},{"level":3,"title":"0.21.9 - 27 October, 2020","slug":"_0-21-9-27-october-2020"},{"level":3,"title":"0.21.8 - 27 October, 2020","slug":"_0-21-8-27-october-2020"},{"level":3,"title":"0.21.7 - 25 October, 2020","slug":"_0-21-7-25-october-2020"},{"level":3,"title":"0.21.6 - 27 September, 2020","slug":"_0-21-6-27-september-2020"},{"level":3,"title":"0.21.5 - 17 August, 2020","slug":"_0-21-5-17-august-2020"},{"level":3,"title":"0.21.4 - 10 August, 2020","slug":"_0-21-4-10-august-2020"},{"level":3,"title":"0.21.3 - 08 August, 2020","slug":"_0-21-3-08-august-2020"},{"level":3,"title":"0.21.2 - 10 July, 2020","slug":"_0-21-2-10-july-2020"},{"level":3,"title":"0.21.1 - 28 April, 2020","slug":"_0-21-1-28-april-2020"},{"level":3,"title":"0.21.0 - 18 April, 2020","slug":"_0-21-0-18-april-2020"},{"level":3,"title":"0.20.15 - 16 April, 2020","slug":"_0-20-15-16-april-2020"},{"level":3,"title":"0.20.14 - 13 April, 2020","slug":"_0-20-14-13-april-2020"},{"level":3,"title":"0.20.13 - 23 March, 2020","slug":"_0-20-13-23-march-2020"},{"level":3,"title":"0.20.12 - 19 March, 2020","slug":"_0-20-12-19-march-2020"},{"level":3,"title":"0.20.11 - 26 February, 2020","slug":"_0-20-11-26-february-2020"},{"level":3,"title":"0.20.10 - 13 February, 2020","slug":"_0-20-10-13-february-2020"},{"level":3,"title":"0.20.9 - 08 February, 2020","slug":"_0-20-9-08-february-2020"},{"level":3,"title":"0.20.8 - 14 January, 2020","slug":"_0-20-8-14-january-2020"},{"level":3,"title":"0.20.7 - 07 January, 2020","slug":"_0-20-7-07-january-2020"},{"level":3,"title":"0.20.6 - 29 December, 2019","slug":"_0-20-6-29-december-2019"},{"level":3,"title":"0.20.5 - 29 December, 2019","slug":"_0-20-5-29-december-2019"},{"level":3,"title":"0.20.4 - 08 December, 2019","slug":"_0-20-4-08-december-2019"},{"level":3,"title":"0.20.3 - 27 November, 2019","slug":"_0-20-3-27-november-2019"},{"level":3,"title":"0.20.2 - 14 November, 2019","slug":"_0-20-2-14-november-2019"},{"level":3,"title":"0.20.1 - 29 October, 2019","slug":"_0-20-1-29-october-2019"},{"level":3,"title":"0.20.0 - 25 October, 2019","slug":"_0-20-0-25-october-2019"},{"level":3,"title":"0.19.5 - 06 October, 2019","slug":"_0-19-5-06-october-2019"},{"level":3,"title":"0.19.4 - 09 September, 2019","slug":"_0-19-4-09-september-2019"},{"level":3,"title":"0.19.3 - 25 August, 2019","slug":"_0-19-3-25-august-2019"},{"level":3,"title":"0.19.2 - 17 August, 2019","slug":"_0-19-2-17-august-2019"},{"level":3,"title":"0.19.1 - 23 July, 2019","slug":"_0-19-1-23-july-2019"},{"level":3,"title":"0.19.0 - 11 July, 2019","slug":"_0-19-0-11-july-2019"},{"level":3,"title":"0.18.4 - 10 July, 2019","slug":"_0-18-4-10-july-2019"},{"level":3,"title":"0.18.3 - 04 July, 2019","slug":"_0-18-3-04-july-2019"},{"level":3,"title":"0.18.2 - 03 July, 2019","slug":"_0-18-2-03-july-2019"},{"level":3,"title":"0.18.1 - 30 June, 2019","slug":"_0-18-1-30-june-2019"},{"level":3,"title":"0.18.0 - 26 June, 2019","slug":"_0-18-0-26-june-2019"},{"level":3,"title":"0.17.5 - 8 June, 2019","slug":"_0-17-5-8-june-2019"},{"level":3,"title":"0.17.4 - 8 June, 2019","slug":"_0-17-4-8-june-2019"},{"level":3,"title":"0.17.3 - 2 June, 2019","slug":"_0-17-3-2-june-2019"},{"level":3,"title":"0.17.2 - 1 June, 2019","slug":"_0-17-2-1-june-2019"},{"level":3,"title":"0.17.1 - 31 May, 2019","slug":"_0-17-1-31-may-2019"},{"level":3,"title":"0.17.0 - 28 May, 2019","slug":"_0-17-0-28-may-2019"},{"level":3,"title":"0.16.5 - 11 Apr, 2019","slug":"_0-16-5-11-apr-2019"},{"level":3,"title":"0.16.4 - 11 Apr, 2019","slug":"_0-16-4-11-apr-2019"},{"level":3,"title":"0.16.3 - 19 Dec, 2018","slug":"_0-16-3-19-dec-2018"},{"level":3,"title":"0.16.2 - 10 Dec, 2018","slug":"_0-16-2-10-dec-2018"},{"level":3,"title":"0.16.1 - 28 Nov, 2018","slug":"_0-16-1-28-nov-2018"},{"level":3,"title":"0.16.0 - 27 Nov, 2018","slug":"_0-16-0-27-nov-2018"},{"level":3,"title":"0.15.2 - 19 Jul, 2018","slug":"_0-15-2-19-jul-2018"},{"level":3,"title":"0.15.1 - 12 Jul, 2018","slug":"_0-15-1-12-jul-2018"},{"level":3,"title":"0.15.0 - 1 Jul, 2018","slug":"_0-15-0-1-jul-2018"},{"level":3,"title":"0.14.6 - 12 Apr, 2018","slug":"_0-14-6-12-apr-2018"},{"level":3,"title":"0.14.5 - 8 Apr, 2018","slug":"_0-14-5-8-apr-2018"},{"level":3,"title":"0.14.4 - 19 Feb, 2018","slug":"_0-14-4-19-feb-2018"},{"level":3,"title":"0.14.3 - 8 Feb, 2018","slug":"_0-14-3-8-feb-2018"},{"level":3,"title":"0.14.2 - 24 Nov, 2017","slug":"_0-14-2-24-nov-2017"},{"level":3,"title":"0.14.1 - 19 Nov, 2017","slug":"_0-14-1-19-nov-2017"},{"level":3,"title":"0.14.0 - 6 Nov, 2017","slug":"_0-14-0-6-nov-2017"},{"level":3,"title":"0.13.0 - 29 Apr, 2017","slug":"_0-13-0-29-apr-2017"},{"level":3,"title":"0.12.9 - 23 Mar, 2017","slug":"_0-12-9-23-mar-2017"},{"level":3,"title":"0.12.8 - 15 Mar, 2017","slug":"_0-12-8-15-mar-2017"},{"level":3,"title":"0.12.7 - 17 Feb, 2017","slug":"_0-12-7-17-feb-2017"},{"level":3,"title":"0.12.6 - 19 Oct, 2016","slug":"_0-12-6-19-oct-2016"},{"level":3,"title":"0.12.5 - 12 Oct, 2016","slug":"_0-12-5-12-oct-2016"},{"level":3,"title":"0.12.3 - 9 Oct, 2016","slug":"_0-12-3-9-oct-2016"},{"level":3,"title":"0.12.2 - 27 Sep, 2016","slug":"_0-12-2-27-sep-2016"},{"level":3,"title":"0.12.1 - 16 Sep, 2016","slug":"_0-12-1-16-sep-2016"},{"level":3,"title":"0.12.0 - 13 Sep, 2016","slug":"_0-12-0-13-sep-2016"},{"level":3,"title":"0.11.10 - 9 Aug, 2016","slug":"_0-11-10-9-aug-2016"},{"level":3,"title":"0.11.9 - 21 Jul, 2016","slug":"_0-11-9-21-jul-2016"},{"level":3,"title":"0.11.8 - 21 Jul, 2016","slug":"_0-11-8-21-jul-2016"},{"level":3,"title":"0.11.7 - 19 Jun, 2016","slug":"_0-11-7-19-jun-2016"},{"level":3,"title":"0.11.6 - 18 Jun, 2016","slug":"_0-11-6-18-jun-2016"},{"level":3,"title":"0.11.5 - 26 May, 2016","slug":"_0-11-5-26-may-2016"},{"level":3,"title":"0.11.4 - 22 May, 2016","slug":"_0-11-4-22-may-2016"},{"level":3,"title":"0.11.3 - 14 May, 2016","slug":"_0-11-3-14-may-2016"},{"level":3,"title":"0.11.2 - 14 May, 2016","slug":"_0-11-2-14-may-2016"},{"level":3,"title":"0.11.1 - 6 May, 2016","slug":"_0-11-1-6-may-2016"},{"level":3,"title":"0.11.0 - 5 May, 2016","slug":"_0-11-0-5-may-2016"},{"level":3,"title":"0.10.0 - 15 Feb, 2016","slug":"_0-10-0-15-feb-2016"},{"level":3,"title":"0.9.0 - Nov 2, 2015","slug":"_0-9-0-nov-2-2015"},{"level":3,"title":"0.8.6 - May 20, 2015","slug":"_0-8-6-may-20-2015"},{"level":3,"title":"0.8.5 - May 14, 2015","slug":"_0-8-5-may-14-2015"},{"level":3,"title":"0.8.4 - May 13, 2015","slug":"_0-8-4-may-13-2015"},{"level":3,"title":"0.8.3 - May 2, 2015","slug":"_0-8-3-may-2-2015"},{"level":3,"title":"0.8.2 - May 1, 2015","slug":"_0-8-2-may-1-2015"},{"level":3,"title":"0.8.1 - May 1, 2015","slug":"_0-8-1-may-1-2015"},{"level":3,"title":"0.8.0 - Apr 30, 2015","slug":"_0-8-0-apr-30-2015"},{"level":3,"title":"0.7.5 - Mar 9, 2015","slug":"_0-7-5-mar-9-2015"},{"level":3,"title":"0.7.4 - Feb 25, 2015","slug":"_0-7-4-feb-25-2015"},{"level":3,"title":"0.7.3 - Oct 3, 2014","slug":"_0-7-3-oct-3-2014"},{"level":3,"title":"0.7.2 - Oct 1, 2014","slug":"_0-7-2-oct-1-2014"},{"level":3,"title":"0.7.1 - Oct 1, 2014","slug":"_0-7-1-oct-1-2014"},{"level":3,"title":"0.7.0 - Oct 1, 2014","slug":"_0-7-0-oct-1-2014"},{"level":3,"title":"0.6.22 - July 10, 2014","slug":"_0-6-22-july-10-2014"},{"level":3,"title":"0.6.21 - July 9, 2014","slug":"_0-6-21-july-9-2014"},{"level":3,"title":"0.6.20 - June 30, 2014","slug":"_0-6-20-june-30-2014"},{"level":3,"title":"0.6.19 - June 27, 2014","slug":"_0-6-19-june-27-2014"},{"level":3,"title":"0.6.18 - June 25, 2014","slug":"_0-6-18-june-25-2014"},{"level":3,"title":"0.6.17 - June 23, 2014","slug":"_0-6-17-june-23-2014"},{"level":3,"title":"0.6.16 - June 18, 2014","slug":"_0-6-16-june-18-2014"},{"level":3,"title":"0.6.15 - June 14, 2014","slug":"_0-6-15-june-14-2014"},{"level":3,"title":"0.6.14 - June 14, 2014","slug":"_0-6-14-june-14-2014"},{"level":3,"title":"0.6.13 - June 12, 2014","slug":"_0-6-13-june-12-2014"},{"level":3,"title":"0.6.12 - June 10, 2014","slug":"_0-6-12-june-10-2014"},{"level":3,"title":"0.6.11 - June 10, 2014","slug":"_0-6-11-june-10-2014"},{"level":3,"title":"0.6.10 - June 10, 2014","slug":"_0-6-10-june-10-2014"},{"level":3,"title":"0.6.9 - June 9, 2014","slug":"_0-6-9-june-9-2014"},{"level":3,"title":"0.6.8 - June 9, 2014","slug":"_0-6-8-june-9-2014"},{"level":3,"title":"0.6.7 - June 9, 2014","slug":"_0-6-7-june-9-2014"},{"level":3,"title":"0.6.6 - June 9, 2014","slug":"_0-6-6-june-9-2014"},{"level":3,"title":"0.6.5 - June 9, 2014","slug":"_0-6-5-june-9-2014"},{"level":3,"title":"0.6.4 - June 9, 2014","slug":"_0-6-4-june-9-2014"},{"level":3,"title":"0.6.3 - June 6, 2014","slug":"_0-6-3-june-6-2014"},{"level":3,"title":"0.6.2 - June 4, 2014","slug":"_0-6-2-june-4-2014"},{"level":3,"title":"0.6.1 - June 4, 2014","slug":"_0-6-1-june-4-2014"},{"level":3,"title":"0.6.0 - June 4, 2014","slug":"_0-6-0-june-4-2014"},{"level":3,"title":"0.5.15 - June 4, 2014","slug":"_0-5-15-june-4-2014"},{"level":3,"title":"0.5.14 - May 6, 2014","slug":"_0-5-14-may-6-2014"},{"level":3,"title":"0.5.13 - April 2, 2014","slug":"_0-5-13-april-2-2014"},{"level":3,"title":"0.5.12 - Mar 31, 2014","slug":"_0-5-12-mar-31-2014"},{"level":3,"title":"0.5.11 - Mar 25, 2014","slug":"_0-5-11-mar-25-2014"},{"level":3,"title":"0.5.10 - Mar 19, 2014","slug":"_0-5-10-mar-19-2014"},{"level":3,"title":"0.5.9 - Mar 18, 2014","slug":"_0-5-9-mar-18-2014"},{"level":3,"title":"0.5.8 - Feb 27, 2014","slug":"_0-5-8-feb-27-2014"},{"level":3,"title":"0.5.7 - Feb 18, 2014","slug":"_0-5-7-feb-18-2014"},{"level":3,"title":"0.5.6 - Feb 5, 2014","slug":"_0-5-6-feb-5-2014"},{"level":3,"title":"0.5.5 - Jan 28, 2014","slug":"_0-5-5-jan-28-2014"},{"level":3,"title":"0.5.4 - Jan 7, 2014","slug":"_0-5-4-jan-7-2014"},{"level":3,"title":"0.5.3 - Jan 2, 2014","slug":"_0-5-3-jan-2-2014"},{"level":3,"title":"0.5.2 - Dec 18, 2013","slug":"_0-5-2-dec-18-2013"},{"level":3,"title":"0.5.1 - Dec 12, 2013","slug":"_0-5-1-dec-12-2013"},{"level":3,"title":"0.5.0 - Nov 25, 2013","slug":"_0-5-0-nov-25-2013"},{"level":3,"title":"0.4.13 - Oct 31, 2013","slug":"_0-4-13-oct-31-2013"},{"level":3,"title":"0.4.12 - Oct 29, 2013","slug":"_0-4-12-oct-29-2013"},{"level":3,"title":"0.4.11 - Oct 15, 2013","slug":"_0-4-11-oct-15-2013"},{"level":3,"title":"0.4.10 - Oct 14, 2013","slug":"_0-4-10-oct-14-2013"},{"level":3,"title":"0.4.9 - Oct 7, 2013","slug":"_0-4-9-oct-7-2013"},{"level":3,"title":"0.4.8 - Oct 2, 2013","slug":"_0-4-8-oct-2-2013"},{"level":3,"title":"0.4.7 - Sep 27, 2013","slug":"_0-4-7-sep-27-2013"},{"level":3,"title":"0.4.6 - Sep 25, 2013","slug":"_0-4-6-sep-25-2013"},{"level":3,"title":"0.4.5 - Sep 24, 2013","slug":"_0-4-5-sep-24-2013"},{"level":3,"title":"0.4.4 - Sep 22, 2013","slug":"_0-4-4-sep-22-2013"},{"level":3,"title":"0.4.3 - Sep 18, 2013","slug":"_0-4-3-sep-18-2013"},{"level":3,"title":"0.4.2 - Sep 17, 2013","slug":"_0-4-2-sep-17-2013"},{"level":3,"title":"0.4.1 - Sep 16, 2013","slug":"_0-4-1-sep-16-2013"},{"level":3,"title":"0.4.0 - Sep 13, 2013","slug":"_0-4-0-sep-13-2013"},{"level":3,"title":"0.2.6 - Aug 29, 2013","slug":"_0-2-6-aug-29-2013"},{"level":3,"title":"0.2.5 - Aug 25, 2013","slug":"_0-2-5-aug-25-2013"},{"level":3,"title":"0.2.4 - Aug 22, 2013","slug":"_0-2-4-aug-22-2013"},{"level":3,"title":"0.2.3 - Aug 22, 2013","slug":"_0-2-3-aug-22-2013"},{"level":3,"title":"0.2.2 - Aug 20, 2013","slug":"_0-2-2-aug-20-2013"},{"level":3,"title":"0.2.1 - Aug 13, 2013","slug":"_0-2-1-aug-13-2013"},{"level":3,"title":"0.2.0 - Aug 7, 2013","slug":"_0-2-0-aug-7-2013"},{"level":3,"title":"0.1.8 - July 7, 2013","slug":"_0-1-8-july-7-2013"},{"level":3,"title":"0.1.7 - June 12, 2013","slug":"_0-1-7-june-12-2013"},{"level":3,"title":"0.1.6 - June 9, 2013","slug":"_0-1-6-june-9-2013"},{"level":3,"title":"0.1.5 - June 9, 2013","slug":"_0-1-5-june-9-2013"},{"level":3,"title":"0.1.4 - May 22, 2013","slug":"_0-1-4-may-22-2013"},{"level":3,"title":"0.1.3 - May 18, 2013","slug":"_0-1-3-may-18-2013"},{"level":3,"title":"0.1.2 - May 15, 2013","slug":"_0-1-2-may-15-2013"},{"level":3,"title":"0.1.1 - May 14, 2013","slug":"_0-1-1-may-14-2013"},{"level":3,"title":"0.1.0 - May 13, 2013","slug":"_0-1-0-may-13-2013"}],"relativePath":"changelog.md"}',a={},o=t('

Changelog

2.5.1 - 12 July, 2023

Bug fixes

2.5.0 - 08 July, 2023

New features

  • Add uuid helper function #5617
  • Add nativeBindings option to better-sqlite3 options #5461
  • Add QueryBuilder#updateFrom #5386
  • Add readonly transaction access mode #5445
  • Add readonly option to Better-SQLite3 #5530
  • Add EXCEPT as a valid keyword #5357
  • Add ability to prepend query comments #5289
  • Add fetchAsString option #5484

Bug fixes

  • Avoid password leaks on query logs #5559
  • Add knex.mjs to files in package.json #5518
  • Handle numeric array elements in .orderBy() #5551
  • Attach error handler early enough #5552
  • Fix Returning * in Oracle #5598
  • Fix indexType option in Postgres #5601
  • Add mjs extension type #5616
  • Use implicit check on json fields for OracleDB #5478
  • Fix when manually close source stream #5466
  • Fix case sensitive issue with get table #5509

Typings

  • Add Object syntax overload to increment method #5512
  • Add object syntax overload to decrement method #5555
  • Fix typing for toSql #5594
  • Add ResolveTableType for .merge() #5605
  • Add missing types for havingNull and havingNotNull #5529
  • Add collate to the columnbuilder interface #5568
  • TableBuilder methods return the SchemaBuilder. #5486

2.4.2 - 22 January, 2023

Bug fixes

  • CLI: Fix incorrent EOL causing errors on Linux #5455

2.4.1 - 18 January, 2023

Bug fixes

  • Fix Postgres Malformed array literal 2.4.0 Regression - #5439

2.4.0 - 6 January, 2023

New features

  • Support partial unique indexes #5316
  • Make compiling SQL in error message optional #5282

Bug fixes

  • Insert array into json column #5321
  • Fix unexpected max acquire-timeout #5377
  • Fix: orWhereJson #5361
  • MySQL: Add assertion for basic where clause not to be object or array #1227
  • SQLite: Fix changing the default value of a boolean column in SQLite #5319

Typings

  • add missing type for 'expirationChecker' on PgConnectionConfig #5334

2.3.0 - 31 August, 2022

New features

  • PostgreSQL: Explicit jsonb support for custom pg clients #5201
  • SQLite: Support returning with sqlite3 and better-sqlite3 #5285
  • MSSQL: Implement mapBinding mssql dialect option #5292

Typings

  • Update types for TS 4.8 #5279
  • Fix typo #5267
  • Fix WhereJsonObject withCompositeTableType #5306
  • Fix AnalyticFunction type #5304
  • Infer specific column value type in aggregations #5297

2.2.0 - 18 July, 2022

New features

  • Inline primary key creation for postgres flavours #5233
  • SQLite: Add warning for undefined connection file #5223
  • MSSQL: Add JSON parameter support for connection #5200

Bug fixes

  • PostgreSQL: add primaryKey option for uuid #5212

Typings

  • Add promisable and better types #5222
  • Update raw query bind parameter type #5208

2.1.0 - 26 May, 2022

New features

  • Improve bundling experience to safely import dialects while using static paths #5142
  • Implement extendable builders #5041
  • PostgreSQL: Refresh materialized view concurrently #5166

Bug fixes

  • Use correct paths in package.json browser field #5174
  • MariaDB: Fix 'NULL' returned instead of NULL on MariaDB 10.2.6+ #5181
  • MySQL: fix hasColumn Error (hasColumn ('a_id') is true, but hasColumn('a_Id') is false) #5148
  • MSSQL: Fix .hasTable result when using .withSchema #5176
  • Oracle: correctly INSERTS Buffer #4869

Typings

  • Update type definitions for pg connection #5139

2.0.0 - 21 April, 2022

Breaking changes

  • Restore sqlite3 package #5136

Test / internal changes

  • Migrate Husky from 4 to 7 #5137
  • Migrate Jake to 10.8.5 #5138

1.0.7 - 13 March, 2022

Bug fixes

  • CLI: Fix cli migrate:make SQLite dependency #5106

1.0.6 - 12 March, 2022

Bug fixes

  • PostgreSQL: Wait for search path to be set before returning connection #5107
  • CLI: No client override during migrate:make #5109

1.0.5 - 05 March, 2022

New features

  • Override knexfile options with CLI options #4047

Bug fixes

  • Stringify json value in update #5063
  • Fix isModuleType() for yarn #4447
  • Wrapped Unions Fixes #5072
  • SQLite: Fix @vscode-sqlite3 error message #5081
  • CLI: Fix completed migration listing #5060

Typings

  • Make default generic parameters of Knex match the generic parameter types of knex #5021
  • Update knex types for TS 4.7 #5095

1.0.4 - 13 March, 2022

New features

  • Add whereLike functions #5044

Bug fixes

  • Fix orWhereJsonPath clause #5022
  • Subquery in on clause missing parenthesis #5049
  • Rework Union Wrapping #5030
  • Oracle: Fix batch inserts with DEFAULT values with OracleDB #2592 #5037

Typings

  • Fix types for "returning" methods #5031
  • createTableLike callback should be optional #5055

Documentation

1.0.3 - 11 February, 2022

Bug fixes

  • Fix error message for missing migration files #4937
  • Add withMaterialized and withNotMaterialized to method-constants #5009
  • PostgreSQL: Fix whereJsonPath queries #5011
  • PostgreSQL: Fix delete joins #5016
  • CockroachDB: Fix whereJsonPath queries #5011
  • MySQL: Create primary keys in same statement #5017

Typings

  • Fix type definition for getMigration in MigrationSource #4998
  • Fix argument type of alter method #4996

Improvements

  • Use async / await syntax in seeds as default #5005

Documentation

1.0.2 - 02 February, 2022

New features

  • Support of MATERIALIZED and NOT MATERIALIZED with WITH/CTE #4940
  • Add raw support in onConflict clause #4960
  • Alter nullable constraint when alterNullable is set to true #4730
  • Add alterType parameter for alter function #4967
  • Support string json in json values #4988
  • MySQL: add with clause #4508

Bug fixes

  • Fix error message for missing migration files #4937
  • Move deferrable to after on update/on delete #4976
  • Do not use sys.tables to find if a table exists #2328
  • PostgreSQL: Fix Order nulls #4989
  • MySQL: Fix collation when renaming column #2666
  • SQLite: Same boolean handling in better-sqlite3 as in sqlite3 #4982

Typings

  • WhereILike - fix typo #4941

1.0.1 - 16 January, 2022

Bug fixes

  • Fix package.json metadata

1.0.0 - 16 January, 2022

Breaking changes

  • Dropped support for Node 10;
  • Replaced unsupported sqlite3 driver with @vscode/sqlite3;
  • Changed data structure from RETURNING operation to be consistent with SELECT;
  • Changed Migrator to return list of migrations as objects consistently.

New features

  • Support fromRaw #4781
  • Support zero precision in timestamp/datetime #4784
  • Support whereLike and whereILike #4779
  • Add JSDoc (TS flavor) to stub files #4809
  • Allow skip binding in limit and offset #4811
  • Support creating a new table in the database based on another table #4821
  • Accept Raw on onIn joins #4830
  • Implement support for custom seed sources #4842
  • Add binary uuid option #4836
  • ForUpdate array parameter #4882
  • Add camel case to timestamps method #4803
  • Advanced JSON support #4859
  • Add type to TypeScript knexfile #4909
  • Checks Constraints Support #4874
  • Support creating multiple PKs with increments #4903
  • Enable wrapIdentifier for SQLite .hasTable #4915
  • MSSQL: Add support for unique constraint #4887
  • SQLite: New dialect, using better-sqlite3 driver #4871
  • SQLite: Switch to @vscode/sqlite3 #4866
  • SQLite: Support createViewOrReplace #4856
  • SQLite: Support RETURNING statements for better-sqlite3 driver #4934
  • PostgreSQL: Support JOIN and USING syntax for Delete Statement #4800

Bug fixes

  • Fix overzealous warning on use of whereNot with "in" or "between" #4780
  • Fix Union all + first syntax error #4799
  • Make view columns optional in create view like #4829
  • Insert lock row fix during migration #4865
  • Fix for createViewOrReplace #4856
  • SQLite: Fix foreign key constraints when altering a table #4189
  • MySQL: Validate connection fix #4794
  • MySQL: Set comment size warning limit to 1024 #4867

Typings

  • Allow string indexType in index creation #4791
  • Add missing ints typings #4832
  • Returning method types #4881
  • Improve columnInfo type #4868

0.95.15 - 22 December, 2021

Bug fixes

  • Oracle:
  • MariaDB: lock row fix during migration in MariaDB and Oracle #4865

0.95.14 - 09 November, 2021

Bug fixes

  • MySQL: mysql2 dialect validate connection fix #4794

0.95.13 - 02 November, 2021

Bug fixes

  • PostgreSQL: Support zero precision in timestamp/datetime #4784

Typings

  • Allow string indexType in index creation #4791

0.95.12 - 28 October, 2021

New features

  • New dialect: CockroachDB #4742
  • New dialect: pg-native #4327
  • CockroachDB: add support for upsert #4767
  • PostgreSQL: Support SELECT .. FOR NO KEY UPDATE / KEY SHARE row level locking clauses #4755
  • PostgreSQL: Add support for 'CASCADE' in PostgreSQL 'DROP SCHEMA' queries #4713
  • MySQL: Add storage engine index Type support to index() and unique() schema #4756
  • MSSQL: Support table.primary, table.unique variant with options object #4710
  • SQLite: Add setNullable support to SQLite #4684
  • Add geometry column building #4776
  • Add support for creating table copies #1373
  • Implement support for views and materialized views #1626
  • Implement partial index support #4768
  • Support for 'is null' in 'order by' #3667

Bug fixes

  • Fix support for Oracle connections passed via knex.connection() #4757
  • Avoid inserting multiple locks if a migration lock already exists #4694

Typings

  • Some TableBuilder methods return wrong types #4764
  • Update JoinRaw bindings type to accept arrays #4752
  • fix onDelete/onUpdate for ColumnBuilder #4656

0.95.11 - 03 September, 2021

New features

  • Add support for nullability modification via schema builder (table.setNullable() and table.dropNullable()) #4657
  • MySQL: Add support for mysql/mariadb-client JSON parameters in connectionURIs #4629
  • MSSQL: Support comments as MS_Description properties #4632

Bug fixes

  • Fix Analytic orderBy and partitionBy to follow the SQL documentation #4602
  • CLI: fix migrate:up for migrations disabling transactions #4550
  • SQLite: Fix adding a column with a foreign key constraint in SQLite #4649
  • MSSQL: columnInfo() support case-sensitive database collations #4633
  • MSSQL: Generate valid SQL for withRecursive() #4514
  • Oracle: withRecursive: omit invalid RECURSIVE keyword, include column list #4514

Improvements

  • Add .mjs migration and seed stubs #4631
  • SQLite: Clean up DDL handling and move all operations to the parser-based approach #4648

0.95.10 - 20 August, 2021

Improvements

  • Use sys info function instead of connection db name #4623

Typings

  • Deferrable and withkeyName should not be in ColumnBuilder #4600

0.95.9 - 31 July, 2021

New features

  • Oracle: support specifying schema for dropTable and dropSequence #4596
  • Oracle: support specifying schema for autoincrement #4594

Typings

  • Add TypeScript support for deferrable, new Primary/Unique syntax #4589

0.95.8 - 25 July, 2021

New features

  • Add deferrable support for constraint #4584
  • Implement delete with join #4568
  • Add DPI error codes for Oracle #4536

Bug fixes

  • Fixing PostgreSQL datetime and timestamp column created with wrong format #4578

Typings

  • Improve analytic types #4576
  • MSSQL: Add trustServerCertificate option #4500

0.95.7 - 10 July, 2021

New features

  • Add ability to omit columns on an onConflict().ignore() #4557
  • CLI: Log error message #4534

Typings

  • Export Knex.TransactionConfig #4498
  • Include options object in count(Distinct) typings #4491
  • Add types for analytic functions #4544

0.95.6 - 17 May, 2021

Typings

  • Export TransactionProvider type #4489

0.95.5 - 11 May, 2021

New features

  • SQLite: Add support for file open flags #4446
  • Add .cjs extension to Seeder.js to support Node ESM #4381 #4382

Bug fixes

  • Remove peerDependencies to avoid auto-install on npm 7 #4480

Typings

  • Fix typing for increments and bigIncrements #4406
  • Add typings for on JoinClause for onVal #4436
  • Adding Type Definition for isTransaction #4418
  • Export client class from knex namespace #4479

0.95.4 - 26 March, 2021

Typings

  • Fix mistyping of stream #4400

0.95.3 - 25 March, 2021

New features

  • PostgreSQL: Add "same" as operator #4372
  • MSSQL: Improve an estimate of the max comment length #4362
  • Throw an error if negative offset is provided #4361

Bug fixes

  • Fix timeout method #4324
  • SQLite: prevent dropForeign from being silently ignored #4376

Typings

  • Allow config.client to be non-client instance #4367
  • Add dropForeign arg type for single column #4363
  • Update typings for TypePreservingAggregation and stream #4377

0.95.2 - 11 March, 2021

New features

  • Improve ESM import support #4350

Bug fixes

  • CLI: update ts.stub files to new TypeScript namespace #4344
  • CLI: fix TypeScript migration stub after 0.95.0 changes #4366

Typings

  • Move QueryBuilder and KnexTimeoutError into knex namespace #4358

Test / internal changes

  • Unify db test helpers #4356

0.95.1 - 04 March, 2021

Bug fixes

  • CLI: fix knex init not finding default knexfile #4339

0.95.0 - 03 March, 2021

Note: there are many breaking changes in this version, particularly in TypeScript support. Please see UPGRADING.md for details.

New features

  • Add transaction isolation support #4185
  • Add analytic functions #4188
  • Change default to not trigger a promise rejection for transactions with a specified handler #4195
  • Make toSQL().toNative() work for Raw to match the API for QueryBuilder #4058
  • Allow 'match' operator #3569
  • Support optimizer hints #4243
  • Add parameter to prevent autoincrement columns from being primary keys #4266
  • Make "first" and "pluck" mutually exclusive #4280
  • Added merge strategy to allow selecting columns to upsert. #4252
  • Throw error if the array passed to insert is empty #4289
  • Events: introduce queryContext on query-error #4301
  • CLI: Use UTC timestamp for new migrations #4245
  • MSSQL: Replace MSSQL dialect with Tedious.js implementation #2857 #4281
  • MSSQL: Use "nvarchar(max)" for ".json()" #4278
  • MSSQL: Schema builder - add predictable constraint names for default values #4319
  • MSSQL: Schema builder - attempt to drop default constraints when changing default value on columns #4321
  • SQLite: Fallback to json for sqlite3 when using jsonb #4186
  • SQLite: Return complete list of DDL commands for creating foreign keys #4194
  • SQLite: Support dropping composite foreign keys #4202
  • SQLite: Recreate indices when altering a table #4277
  • SQLite: Add support for altering columns #4322

Bug fixes

  • Fix issue with .withSchema usage with joins on a subquery #4267
  • Fix issue with schema usage with FROM clause contain QueryBuilder, function or Raw #4268
  • CLI: Address raised security warnings by dropping liftoff #4122
  • CLI: Fix an issue with npm@7 and ESM when type was set to 'module' in package.json #4295
  • PostgreSQL: Add check to only create native enum once #3658
  • SQLite: Fix foreign key "on delete" when altering a table #4225
  • SQLite: Made the constraint detection case-insensitive #4330
  • MySQL: Keep auto increment after rename #4266
  • MSSQL: don't raise query-error twice #4314
  • MSSQL: Alter column must have its own query #4317

Typings

  • TypeScript 4.1+ is now required
  • Add missing onConflict overrides #4182
  • Introduce the "infamous triplet" export #4181
  • Fix type definition of Transaction #4172
  • Add typedefinitions for havingNotIn #4265
  • Include 'name' property in MigratorConfig #4300
  • Improve join and conflict types #4318
  • Fix ArrayIfAlready type #4331

Test / internal changes

  • Drop global Knex.raw #4180
  • Stop using legacy url.parse API #3702
  • Various internal refactorings #4175 #4177 #4178 #4192
  • Refactor to classes #4190 #4191 #4193 #4210 #4253
  • Move transaction type tests to TSD #4208
  • Clean up destroy logic #4248
  • Colorize code snippets in readme files #4234
  • Add "Ecosystem" documentation for Knex plugins #4183
  • Documentation cleanup
  • SQLite: Use SQLite "rename column" instead of a DDL helper #4200
  • SQLite: Simplify reinsert logic when altering a table #4272

0.21.19 - 02 March, 2021

  • SQLite: Made the constraint detection case-insensitive #4332

0.21.18 - 22 February, 2021

  • CLI: Fix an issue with npm@7 and ESM when type was set to 'module' in package.json #4295

0.21.17 - 30 January, 2021

Bug fixes

  • SQLite: Fix SQLite foreign on delete when altering a table #4261

New features

0.21.16 - 17 January, 2021

Bug fixes

  • MSSQL: Avoid passing unsupported pool param. Fixes node-mssql 7+ support #4236

0.21.15 - 26 December, 2020

New features

  • SQLite: Add primary/foreign support on alterTable #4162
  • SQLite: Add dropPrimary/dropForeign support on alterTable #4162

Typings

  • Add "after" and "first" to columnBuilder types #3549 #4169

Test / internal changes

  • Extract knex config resolution logic #4166
  • Run CI using GitHub Actions #4168
  • Add Node.js 15 to CI matrix #4173

0.21.14 - 18 December, 2020

New features

  • MSSQL: support "returning" on inserts, updates and deletes on tables with triggers #4152
  • Use esm import if package.json type is "module" #4158

Bug fixes

  • Make sure query-response and query-error events contain _knexTxId #4160

Test / internal changes

  • Improved integration test framework #4161

0.21.13 - 12 December, 2020

New features

  • SQLite: Add support for dropForeign #4092
  • Add support for WHERE clauses to "upsert" queries #4148

Bug fixes

  • MSSQL: Avoid connection getting stuck on socket hangup #4157
  • Oracle: Support specifying non-default DB port #4147
  • Oracle: Support inserts with only default values (empty body) #4092
  • CLI: fix irregular seed file execution order #4156
  • Fix performance of asyncStackTraces with enable-source-maps node flag #4154

Typings

  • PostgreSQL: Add support for application_name #4153
  • Fix types for insert to allow array #4105
  • Add types for userParams and withUserParams #4119
  • Added type for withKeyName #4139
  • Fix batchInsert definitions #4131
  • Fix types for WhereIn signature (value or query builder) #3863
  • Add types for connection config of mysql2 driver #4144

Test / internal changes

0.21.12 - 02 November, 2020

Typings

  • Reintroduce support for globally defining table/record mapping #4100
  • Add a few missing types for MSSQL Connection #4103
  • Make .ignore() and .merge() return QueryBuilder rather than QueryInterface #4102
  • Use tarn config TS types instead of generic-pool #4064

0.21.11 - 01 November, 2020

Typings

  • Revert support for globally defining table/record mapping #4099

0.21.10 - 31 October, 2020

New features

  • Upsert support (Postgres/MySQL/Sqlite) #3763

Bug fixes

  • Switch to non-uuid knexQueryUids to avoid issues when mocking global date #4089

Typings

  • Allow to globally define table/record mapping #4071

0.21.9 - 27 October, 2020

New features

  • add method clear(statement) to QueryBuilder #4051

Bug fixes

  • CLI: fix help text being printed twice #4072
  • Oracle: columnInfo() no longer requires an Owner User #4053
  • Add missing "start" event propagation from transaction #4087

0.21.8 - 27 October, 2020

Bug fixes

  • MSSQL: Escape properly if literal '?' is needed #4053
  • Make toQuery behavior consistent with pre-0.21.7 (do not break on empty builder) #4083
  • Fix comment escaping for MySQL and PostgreSQL #4084

0.21.7 - 25 October, 2020

New features

  • CLI: Add migration stub for .cjs extension #4065

Bug fixes

  • MSSQL: Add dynamic scaling for decimal values and prevents a UInt64 overflow #3910
  • MSSQL: Fix apostrophe escaping #4077
  • Ensure that semicolon is not appended to statements that already end with a semicolon #4052

Typings

  • Add arguments to QueryCallback in Where #4034

Test / internal changes

  • Replace lodash type-checks with native solutions #4056
  • Replace mkdirp with native recursive flag #4060
  • Replace inherits package with builtin utility #4059

0.21.6 - 27 September, 2020

New features

  • CLI: New config parameter / CLI flag to prefixing seed filename with timestamp #3873
  • CLI: throw an error when specific seed file cannot be found #4011
  • Warn if whereNot is used with 'in' or 'between' #4038

Bug fixes

  • CLI: Fix double merging of config for migrator #4040

Typings

  • Unify SeedsConfig and SeederConfig #4003
  • Allow string[] type for directory in SeedsConfig #4033

0.21.5 - 17 August, 2020

New features

  • CLI: Improve Esm interop #3985
  • CLI: Improve mjs module support #3980

Test / internal changes

  • Bump version of dtslint #3984
  • Test/document esm interop mixed formats (knexfile/migrations/seeds) #3986

0.21.4 - 10 August, 2020

New features

  • CLI: Add new option for seed: recursive #3974

Bug fixes

  • CLI: Do not load seeds from subfolders recursively by default #3974

0.21.3 - 08 August, 2020

New features

  • CLI: Support multiple directories for seeds #3967

Bug fixes

  • Ensure DB stream is destroyed when the PassThrough is destroyed #2324
  • Support postProcessResponse for streams #3931
  • Fix ESM module interop for calling module/package of type 'module' #3938
  • CLI: Fix migration source name in rollback all #3956
  • Fix getMergedConfig calls to include client logger #3920
  • Escape single quoted values passed to defaultTo function #3899

Typings

  • Add .timeout(ms) to .raw()'s typescript typings #3885
  • Add typing for double table column builder #3950
  • Add a phantom tag to Ref type to mark received type parameters as used #3934
  • Add null as valid binding type #3946

Test / internal changes

  • Change query lab link to https #3933

0.21.2 - 10 July, 2020

New features

  • Warn user if custom migration source is being reset #3839
  • Prefer void as return type on migration generator ts stub #3865
  • MSSQL: Added the removal of a columns default constraint, before dropping the column #3855

Typings

  • Fix definition for raw querybuilders #3846

Test / internal changes

  • Refactor migration logic to use async/await #3838

0.21.1 - 28 April, 2020

New features

  • CLI: Add migrate:unlock command, truncate on forceFreeMigrationsLock #3822
  • CLI: Add support for cjs files by default #3829

Bug fixes

  • CLI: Fix inference of seed/migration extension from knexfile extension #3814
  • rewrite delay to not node-only version. Fixes compatibility with browsers #3820

Test / internal changes

  • Update dependencies. Explicitly support Node.js 14 #3825 #3830

0.21.0 - 18 April, 2020

Improvements

  • Reduce size of lodash in bundle #3804

Breaking changes

  • Dropped support for Node 8
  • Breaking upstream change in pg-query-stream: Changed stream.close to stream.destroy which is the official way to terminate a readable stream. This is a breaking change if you rely on the stream.close method on pg-query-stream...though should be just a find/replace type operation to upgrade as the semantics remain very similar (not exactly the same, since internals are rewritten, but more in line with how streams are "supposed" to behave).

Test / internal changes

  • Updated Tarn.js to a version 3.0.0
  • Updated mkdirp to a version 1.0.4
  • Updated examples to use ES2015 style #3810

0.20.15 - 16 April, 2020

Bug fixes

  • Support for .finally(..) on knex's Promise-alikes #3800

Typings

  • Add types for .distinctOn #3784

0.20.14 - 13 April, 2020

New features

  • CLI: adds support for asynchronous knexfile loading #3748
  • Add clearGroup method #3771

Typings

  • Support Raw types for insert, where, update #3730
  • Add typings for MigrationSource #3756
  • Update signature of orderBy to support QueryBuilder inside array #3757
  • Add toSQL and toString to SchemaBuilder #3758
  • interface Knex and function Knex should have the same types #3787
  • Fix minor issues around typings #3765

Test / internal changes

  • Minor test internal enhancements #3747
  • Minor improvements on the usage of fs utilities #3749
  • Split tests in groups #3785

0.20.13 - 23 March, 2020

Bug fixes

  • Correctly handle dateToString escaping without timezone passed #3742
  • Make protocol length check more defensive #3744

Typings

  • Make the ChainableInterface conform to Promise<T> #3724

0.20.12 - 19 March, 2020

Bug fixes

  • Added missing call to _reject in Transactor#transaction #3706
  • Fix method binding on knex proxy #3717
  • Oracle: Transaction_OracleDB can use config.connection #3731

Typings

  • Fix incorrect type signature of Having #3719

Test / internal changes

  • Cleanup/remove transaction stalling #3716
  • Rewrote Transaction#acquireConnection() methods to use async #3707

0.20.11 - 26 February, 2020

Breaking changes

  • Knex returns native JS promises instead of Bluebird ones. This means that you no longer use such methods as map, spread and reduce on QueryBuilder instance.

New features

  • Oracle: Add OracleDB handling for buffer type in fetchAsString #3685

Bug fixes

  • Fix race condition in non-container transactions #3671

Typings

  • Mark knex arguments of composite/collection types to be readonly #3680

Test / internal changes

  • Remove dependency on Bluebird methods from sources #3683
  • Cleanup and extract Transaction Workflow logic #3674

0.20.10 - 13 February, 2020

Bug fixes

  • Oracle: commit was a no-op causing race conditions #3668
  • CLI: Knex calls process.chdir() before opening Knexfile #3661
  • Fixed unresolved promise in cancelQuery() #3666

Typings

  • fn.now takes optionally a precision argument. #3662
  • PG: Include SSL in connection definition #3659

Test / internal changes

  • replace Bluebird.timeout #3634

0.20.9 - 08 February, 2020

Bug fixes

  • CLI: Improve Support for Liftoff's Preloaders - this should fix some cases like using TS for your migrations #3613

Typings

  • MSSQL: Add enableArithAbort to MsSqlConnectionConfig

Test / internal changes

  • Refactor more tests to use cli-testlab #3640
  • Update QueryCompiler implementation to use classes #3647

0.20.8 - 14 January, 2020

New features

  • CLI: Support ES6 modules via flag --esm #3616

Bug fixes

  • CLI: Print help only when there are no arguments #3617

Typings

  • Fix incorrect type of QueryBuilder.first('*') result #3621

0.20.7 - 07 January, 2020

New features

  • Throw better error when trying to modify schema while using unsupported dialect #3609

Bug fixes

  • Oracle: dispose connection on connection error #3611
  • Oracle: fix not releasing connection from pool on disconnect #3605
  • CLI: prevent warning with root command #3604

Typings

  • Add create/drop schema methods to SchemaBuilder #3579

0.20.6 - 29 December, 2019

Bug fixes

  • Enforce Unix (lf) line terminators #3598

0.20.5 - 29 December, 2019

New features

  • Return more information about empty updates #3597

Bug fixes

  • Fix colors in debug logs #3592

Test / internal changes

  • Use more efficient algorithm for generating internal ids #3595 #3596
  • Use Buffer.alloc() instead of deprecated constructor #3574

0.20.4 - 08 December, 2019

Bug fixes

  • Fix debug logger messing up queries with % #3566
  • Make logger methods mutually consistent #3567

Typings

  • Add missing methods to client type #3565
  • Fix queryContext function defintion #3562
  • Fix QueryBuilder.extend this type #3526 #3528

Test / internal changes

  • Remove bluebird.using #3552

0.20.3 - 27 November, 2019

New features

  • MSSQL, MySQL: Add connection string qs to connection params #3547

Bug fixes

  • Oracle: Fix issue retrieving BLOB from database #3545
  • PostgreSQL: Timeout for postgresql use cancel instead of terminate #3518
  • Make sure CLI works for namespaced knex packages #2539

Typings

  • Lift up dialect specific methods in the CreateTableBuilder #3532
  • Add client property to QueryBuilder type #3541
  • Support 'only' option #3551

0.20.2 - 14 November, 2019

New features

  • Add support for distinct on for postgres #3513

Bug fixes

  • Make sqlite3 hasColumn case insensitive #3435

Typings

  • Fix PoolConfig typing #3505
  • Expand SeedsConfig types #3531
  • Make the default type parameters of QueryBuilder less strict #3520
  • Fix regression in older version of node when Promise#finally was not available #3507

0.20.1 - 29 October, 2019

New features

  • Declare drivers as optional peerDependencies #3081
  • Dynamic connection configuration resolution #3497

Bug fixes

  • Wrap subQuery with parenthesis when it appears as table name #3496
  • Fix Oracle error codes #3498

Typings

  • Add interface for PG Connection object #3372
  • Gracefully handle global promise pollution #3502

0.20.0 - 25 October, 2019

New features

  • orderBy accepts QueryBuilder #3491
  • Add validation in .offset() #2908
  • disable_migrations_list_validation feature #3448

Bug fixes

  • Fix oracledb driver v4 support #3480
  • Fix some issues around seed and migration generation #3479
  • Fix bugs in replacement logic used when dropping columns in SQLite #3476

Typings

  • Add types to the Migrator interface #3459
  • Fix typings of index and dropIndex TableBuilder methods #3486
  • Fixes types for Seeder#run #3438

Test / internal changes

  • Execute CI on Node.js 13
  • Bluebird: remove usage of return, reflect, fromCallback methods #3483
  • Bluebird: remove Bluebird.bind #3477
  • Bluebird: use util.promisify instead of Bluebird.promisify #3470
  • Bluebird: remove Bluebird.each #3471
  • Bluebird: remove Bluebird.map and Bluebird.mapSeries #3474
  • Bluebird: replace Bluebird.map with Promise.all #3469
  • Update badges #3482

0.19.5 - 06 October, 2019

New features

  • CLI: Migrations up/down commands - filename parameter #3416
  • Oracle: Support stored procedures #3449

Bug fixes

  • MSSQL: Escape column ids correctly in all cases (reported by Snyk Security Research Team) #3382
  • SQLite: Fix handling of multiline SQL in SQLite3 schema #3411
  • Fix concurrent child transactions failing #2213 #3440

Typings

  • Add missing Migrator.list typing #3460
  • Fix Typescript type inference for to better support wildcard (*) calls #3444
  • Make options argument optional in timeout #3442

Test / internal changes

  • Enable linting in CI #3450

0.19.4 - 09 September, 2019

New features

  • Add undefined columns to undefined binding(s) error #3425

Typings

  • Add specific to SeederConfig type #3429
  • Fix some issues with QueryBuilder types #3427

0.19.3 - 25 August, 2019

Bug fixes

  • Fix migrations for native enums to use table schema #3307

New features

  • Add ability to manually define schema for native enums #3307
  • Add SSL/TLS support for Postgres connection string #3410
  • CLI: new command that lists all migrations with status #3390

Typings

  • Include schemaName in EnumOptions #3415
  • Allow ColumnBuilder.defaultTo() to be null #3407

Other Changes

  • migrate: Refactor _lockMigrations to avoid forUpdate - makes migrations compatible with CockroachDB #3395

0.19.2 - 17 August, 2019

Other Changes

  • Make transaction rejection consistent across dialects #3399
  • More consistent handling of nested transactions #3393

New features

  • Fallback to JSON when using JSONB in MySQL #3394

0.19.1 - 23 July, 2019

New features

  • Allow to extend knex query builder #3334
  • Add .isCompleted() to transaction #3368
  • Minor enhancements around aliasing of aggregates #3354

Typings

  • Update configuration typings to allow for oracle db connectionstring #3361
  • Update Knex.raw type to be any by default because the actual type is dialect specific #3349

0.19.0 - 11 July, 2019

Other Changes

  • Pooling: tarn.js connection pool was updated to version 2.0.0. This fixes issue with destroying connections and introduces support for connection pool event handlers. Please see tarn.js documentation for more details #3345
  • Pooling: Passing unsupported pooling configuration options now throws an error
  • Pooling: beforeDestroy configuration option was removed

0.18.4 - 10 July, 2019

New features

  • Seeds: Option to run specific seed file #3335
  • Implement "skipLocked()" and "noWait()" #2961

Bug fixes

  • CLI: Respect the knexfile stub option while generating a migration #3337
  • Fix mssql import not being ignored, breaking webpack builds #3336

0.18.3 - 04 July, 2019

New features

  • CLI: add --stub option to migration:make #3316

Bug fixes

  • Fix return duplicate transaction promise for standalone transactions #3328

0.18.2 - 03 July, 2019

Bug fixes

  • Fix remove duplicate transaction rejection #3324
  • Fix issues around specifying default values for columns #3318
  • CLI: Fix empty --version output #3312

0.18.1 - 30 June, 2019

Bug fixes

  • Do not reject duplicate promise on transaction rollback #3319

0.18.0 - 26 June, 2019

Bug fixes

  • Do not reject promise on transaction rollback (by default only for new, non-callback, style of transactions for now to avoid breaking old code) #3235

New features

  • Added doNotRejectOnRollback options for starting transactions, to prevent rejecting promises on rollback for callback-style transactions.
  • Use extension from knexfile for generating migrations unless overriden #3282
  • Use migrations.extension from config when generating migration #3242
  • Expose executionPromise for transactors #3297

Bug fixes

  • Oracle: Updated handling of connection errors for disposal #2608
  • Fix extension resolution from env configs #3294

Test / internal changes

Typings

  • Add workarounds for degraded inference when strictNullChecks is set to false #3275
  • Add stub type definition for Migrator config #3279
  • Add stub to seeds type #3296
  • Fix MSSQL config typings #3269
  • Add pgsql specific table builder method typings #3146

0.17.5 - 8 June, 2019

Typings

  • Include result.d.ts in published package #3271

0.17.4 - 8 June, 2019

Typings

  • Fix some cases of left-to-right inference causing type mismatch #3265
  • Improve count typings #3249

Bug fixes

  • Fix error message bubbling up on seed error #3248

0.17.3 - 2 June, 2019

Typings

  • Improve typings for aggregations #3245
  • Add decimalNumbers to MySqlConnectionConfig interface #3244

0.17.2 - 1 June, 2019

Typings

  • Improve count typings #3239

Bug fixes

  • "colorette" dependency breaks browserify builds #3238

0.17.1 - 31 May, 2019

New features

  • Add migrate:down functionality #3228

Typings

  • Update type of aggregation results to not be arrays when first has been invoked before #3237
  • Include undefined in type of single row results #3231
  • Fix incorrect type definitions for single row queries #3230

0.17.0 - 28 May, 2019

New features

  • Add support for returning started transaction without immediately executing it #3099
  • Add support for passing transaction around with only starting it when needed #3099
  • Add clearHaving function #3141
  • Add --all flag for rollback in CLI #3187
  • Add error detail log to knex CLI #3149
  • Support multi-column whereIn in sqlite through values clause #3220
  • Allow users to specify the migrations "tableName" parameter via the CLI #3214
  • Unify object options handling for datetime/timestamp across dialects #3181
  • Add "up" command for migrations #3205

Typings

  • Add default values for generic types (fixes backwards compatibility broken by 0.16.6) #3189
  • Make function types generic in type definitions #3168
  • Add missing types to MigratorConfig #3174
  • Add types for havingBetween, orHavingBetween, havingNotBetween and orHavingNotBetween #3144
  • Update Knex.Config types to include log #3221
  • Fix some more cases of missing typings #3223
  • Support type safe refs #3215
  • Expose some utility types #3211
  • Fix issues with typings of joins and some conflicts with Bluebird typings #3209

Bug fixes

  • Fix order of migration rollback #3172

Test / internal changes

  • Execute CI tests on Node.js 12 #3171
  • Docker-based test dbs #3157
  • Use cli-testlab for testing CLI #3191

0.16.5 - 11 Apr, 2019

  • Bundle polyfills with knex for 0.16.x line again #3139

0.16.4 - 11 Apr, 2019

New features

  • Boolean param for rollback() to rollback all migrations #2968
  • seed:run print the file name of the failing seed #2972 #2973
  • verbose option to CLI commands #2887
  • add intersect() #3023
  • Improved format for TS stubs #3080
  • MySQL: Support nullable timestamps #3100
  • MySQL: Warn .returning() does not have any effect #3039

Bug fixes

  • Respect "loadExtensions" configuration #2969
  • Fix event listener duplication when using Migrator #2982
  • Fix fs-migrations breaking docs #3022
  • Fix sqlite3 drop/renameColumn() breaks with postProcessResponse #3040
  • Fix transaction support for migrations #3084
  • Fix queryContext not being passed to raw queries #3111
  • Typings: Allow to pass query builders, identifiers and raw in various places as parameters #2960
  • Typings: toNative() definition #2996
  • Typings: asCallback() definition #2963
  • Typings: queryContext() type definition Knex.Raw #3002
  • Typings: Add "constraintName" arg to primary() definition #3006
  • Typings: Add missing schemaName in MigratorConfig #3016
  • Typings: Add missing supported parameter types and toSQL method #2960
  • Typings: Update enum arguments to reflect latest signature #3043
  • Typings: Add size parameter to integer method #3074
  • Typings: Add 'string' as accepted Knex constructor type definition #3105
  • Typings: Add boolean as a column name in join #3121
  • Typings: Add missing clearOrder & clearCounters types #3109
  • Dependencies: Fix security warning #3082
  • Do not use unsupported column width/length arguments on data types int and tinyint in MSSQL #2738

Other Changes

  • Make unionAll()'s call signature match union() #3055

Test / internal changes

  • Swap chalk\u2192colorette / minimist\u2192getopts #2718
  • Always use well documented pg client query() config argument #3004
  • Do not bundle polyfills with knex #3024

0.16.3 - 19 Dec, 2018

Bug fixes

  • @babel/polyfill loaded multiple times #2955
  • Resolve migrations and seeds relatively to knexfile directory when specified (the way it used to be before 0.16.1) #2952

0.16.2 - 10 Dec, 2018

Bug fixes

  • Add TypeScript types to the "files" entry so they are properly included in the release #2943

0.16.1 - 28 Nov, 2018

Breaking changes

  • Use datetime2 for MSSQL datetime + timestamp types. This change is incompatible with MSSQL older than 2008 #2757
  • Knex.VERSION() method was removed, run "require('knex/package').version" instead #2776
  • Knex transpilation now targets Node.js 6, meaning it will no longer run on older Node.js versions #2813
  • Add json type support for SQLite 3.9+ (tested to work with Node package 'sqlite3' 4.0.2+) #2814

New features

  • Support passing explicit connection to query builder (#2817)
  • Introduced abstraction for getting migrations to make migration bundling easier #2775
  • Allow timestamp with timezone on mssql databases #2724
  • Allow specifying multiple migration directories #2735
  • Allow cloning query builder with .userParams({}) assigned to it #2802
  • Allow chaining of increment, decrement, and update #2740
  • Allow table names with forUpdate/forShare #2834
  • Added whereColumn and the associated not / and / or methods for using columns on the right side of a where clause #2837
  • Added whereRecursive method to make self-referential CTEs possible #2889
  • Added support for named unique, primary and foreign keys to SQLite3 #2840
  • Added support for generating new migration and seed files without knexfile #2884 #2905 #2935
  • Added support for multiple columns in .orderBy() #2881
  • Added option of existingType to .enum() method to support repeated use of enums #2719
  • Added option to pass indexType for MySQL dialect #2890
  • Added onVal and the associated not / and / or methods for using values in on clauses within joins #2746
  • Kill queries after timeout for PostgreSQL #2636
  • Manage TypeScript types internally #2845
  • Support 5.0.0+ versions of mssql driver #2861
  • Typescript migration stub #2816
  • Options object for passing timestamp parameters + regression tests #2919

Bug fixes

  • Implement fail-fast logic for dialect resolution #2776
  • Fixed identifier wrapping for using(). Use columnize instead of wrap in using() #2713
  • Fix issues with warnPromise when migration does not return a promise #2730
  • Compile with before update so that bindings are put in correct order #2733
  • Fix join using builder withSchema #2744
  • Throw instead of process.exit when client module missing #2843
  • Display correct filename of a migration that failed #2910
  • Fixed support of knexSnakeCaseWrappers in migrations #2914
  • SQlite3 renameColunm quote fix #2833
  • Adjust typing for forUpdate()/forShare() variant with table names #2858
  • Fix execution of Oracle tests on Node 11 #2920
  • Fix failures in oracle test bench and added it back to mandatory CI tests #2924
  • Knex client knexfile resolution fix #2923
  • Add queryContext to type declarations #2931

Test / internal changes

0.16.0 - 27 Nov, 2018

Other Changes

  • THIS RELEASE WAS UNPUBLISHED FROM NPM BECAUSE IT HAD BROKEN MIGRATIONS USING postprocessResponse FEATURE (#2644)

0.15.2 - 19 Jul, 2018

Other Changes

  • Rolled back changes introduced by #2542, in favor of opt-in behavior by adding a precision option in date / timestamp / datetime / knex.fn.now (#2715, #2721)

0.15.1 - 12 Jul, 2018

Bug fixes

  • Fix warning erroneously displayed for mysql #2705

0.15.0 - 1 Jul, 2018

Breaking changes

  • Stop executing tests on Node 4 and 5. #2451 (not supported anymore)
  • json data type is no longer converted to text within a schema builder migration for MySQL databases (note that JSON data type is only supported for MySQL 5.7.8+) #2635
  • Removed WebSQL dialect #2461
  • Drop mariadb support #2681
  • Primary Key for Migration Lock Table #2569. This shouldn't affect to old loc tables, but if you like to have your locktable to have primary key, delete the old table and it will be recreated when migrations are ran next time.
  • Ensure knex.destroy() returns a bluebird promise #2589
  • Increment floats #2614
  • Testing removal of 'skim' #2520, Now rows are not converted to plain js objects, returned row objects might have changed type with oracle, mssql, mysql and sqlite3
  • Drop support for strong-oracle #2487
  • Timeout errors doesn't silently ignore the passed errors anymore #2626
  • Removed WebSQL dialect #2647
  • Various fixes to mssql dialect to make it compatible with other dialects #2653, Unique constraint now allow multiple null values, float type is now float instaed of decimal, rolling back transaction with undefined rejects with Error, select for update and select for share actually locks selected row, so basically old schema migrations will work a lot different and produce different schema like before. Also now MSSQL is included in CI tests.

Bug fixes

  • Fixes onIn with empty values array #2513
  • fix wrapIdentifier not being called in postgres alter column #2612
  • fixes wrapIdentifier to work with postgres returning statement 2630 #2642
  • Fix mssql driver crashing in certain cases when conneciton is closed unexpectedly #2637
  • Removed semicolon from rollback stmt for oracle #2564
  • Make the stream catch errors in the query #2638

New features

  • Create timestamp columns with microsecond precision on MySQL 5.6 and newer #2542
  • Allow storing stacktrace, where builder is initialized to be able trace back where certain query was created #2500 #2505
  • Added 'ref' function #2509, no need for knex.raw('??', ['id']) anymore, one can do knex.ref('id')
  • Support postgresql connection uri protocol #2609
  • Add support for native enums on Postgres #2632
  • Allow overwriting log functions #2625

Test / internal changes

  • chore: cache node_modules #2595
  • Remove babel-plugin-lodash #2634
  • Remove readable-stream and safe-buffer #2640
  • chore: add Node.js 10 #2594
  • add homepage field to package.json #2650

0.14.6 - 12 Apr, 2018

Bug fixes

  • Restored functionality of query event #2566 (#2549)

0.14.5 - 8 Apr, 2018

Bug fixes

  • Fix wrapping returning column on oracledb #2554

New features

  • Support passing DB schema name for migrations #2499 #2559
  • add clearOrder method #2360 #2553
  • Added knexTxId to query events and debug calls #2476
  • Support multi-column whereIn with query #1390
  • Added error if chaining update/insert/etc with first() #2506
  • Checks for an empty, undefined or null object on transacting #2494
  • countDistinct with multiple columns #2449

Test / internal changes

  • Added npm run test:oracledb command that runs oracledb tests in docker #2491
  • Runnin mssql tests in docker #2496
  • Update dependencies #2561

0.14.4 - 19 Feb, 2018

Bug fixes

  • containsUndefined only validate plain objects. Fixes #1898 (#2468)
  • Add warning when using .returning() in sqlite3. Fixes #1660 (#2471)
  • Throw an error if .update() results in an empty sql (#2472)
  • Removed unnecessary createTableIfNotExist and replaced with createTable (#2473)

New features

  • Allow calling lock procedures (such as forUpdate) outside of transaction. Fixes #2403. (#2475)
  • Added test and documentation for Event 'start' (#2488)

Test / internal changes

  • Added stress test, which uses TCP proxy to simulate flaky connection #2460
  • Removed old docker tests, new stress test setup (#2474)
  • Removed unused property __cid on the base client (#2481)
  • Changed rm to rimraf in 'npm run dev' (#2483)
  • Changed babel preset and use latest node as target when running dev (#2484)

0.14.3 - 8 Feb, 2018

Bug fixes

  • Use tarn as pool instead of generic-pool which has been given various problems #2450
  • Fixed mysql issue where add columns failed if using both after and collate #2432
  • CLI sets exit-code 1 if the command supplied was not parseable #2358
  • Set toNative() to be not enumerable #2388
  • Use wrapIdentifier in columnInfo. fixes #2402 #2405
  • Fixed a bug when using .returning (OUTPUT) in an update query with joins in MSSQL #2399
  • Better error message when running migrations fail before even starting run migrations #2373
  • Read oracle's UV_THREADPOOL_SIZE env variable correctly #2372
  • Added decimal variable precision / scale support #2353

New features

  • Added queryContext to schema and query builders #2314
  • Added redshift dialect #2233
  • Added warning when one uses .createTableIfNotExist and deprecated it from docs #2458

Test / internal changes

  • Update dependencies and fix ESLint warnings accordingly #2433
  • Disable oracledb tests from non LTS nodes #2407
  • Update dependencies #2422

0.14.2 - 24 Nov, 2017

Bug fixes

  • Fix sqlite3 truncate method to work again #2348

0.14.1 - 19 Nov, 2017

Bug fixes

  • Fix support for multiple schema names in in postgres searchPath #2340
  • Fix create new connection to pass errors to query instead of retry loop #2336
  • Fix recognition of connections closed by server #2341

0.14.0 - 6 Nov, 2017

Breaking changes

  • Remove sorting of statements from update queries #2171
  • Updated allowed operator list with some missing operators and make all to lower case #2239
  • Use node-mssql 4.0.0 #2029
  • Support for enum columns to SQlite3 dialect #2055
  • Better identifier quoting in Sqlite3 #2087
  • Migration Errors - Display filename of of failed migration #2272

Other Features

  • Post processing hook for query result #2261
  • Build native SQL where binding parameters are dialect specific #2237
  • Configuration option to allow override identifier wrapping #2217
  • Implemented select syntax: select({ alias: 'column' }) #2227
  • Allows to filter seeds and migrations by extensions #2168
  • Reconnecting after database server disconnect/reconnect + tests #2017
  • Removed filering from allowed configuration settings of mysql2 #2040
  • Allow raw expressions in query builder aggregate methods #2257
  • Throw error on non-string table comment #2126
  • Support for mysql stream query options #2301

Bug fixes

  • Allow update queries and passing query builder to with statements #2298
  • Fix escape table name in SQLite columnInfo call #2281
  • Preventing containsUndefined from going to recursion loop #1711
  • Fix error caused by call to knex.migrate.currentVersion #2123
  • Upgraded generic-pool to 3.1.7 (did resolve some memory issues) #2208
  • Allow using NOT ILIKE operator #2195
  • Fix postgres searchPath to be case-sensitive #2172
  • Fix drop of multiple columns in sqlite3 #2107
  • Fix adding multiple columns in Oracle #2115
  • Use selected schema when dropping indices in Postgres. #2105
  • Fix hasTable for MySQL to not do partial matches #2097
  • Fix setting autoTransaction in batchInsert #2113
  • Fix connection error propagation when streaming #2199
  • Fix comments not being applied to increments columns #2243
  • Fix mssql wrong binding order of queries that combine a limit with select raw or update #2066
  • Fixed mysql alter table attributes order #2062

Test / internal changes

  • Update each out-of-date dependency according to david-dm.org #2297
  • Update v8flags to version 3.0.0 #2288
  • Update interpret version #2283
  • Fix debug output typo #2187
  • Docker CI tests #2164
  • Unit test for right/rightOuterJoin combination #2117
  • Unit test for fullOuterJoin #2118
  • Unit tests for table comment #2098
  • Test referencing non-existent column with sqlite3 #2104
  • Unit test for renaming column in postgresql #2099
  • Unit test for cross-join #2102
  • Fix incorrect parameter name #2068

0.13.0 - 29 Apr, 2017

Breaking changes

  • Multiple concurrent migration runners blocks instead of throwing error when possible #1962
  • Fixed transaction promise mutation issue #1991

Other Changes

  • Allow passing version of connected db in configuration file #1993
  • Bugfixes on batchInsert and transactions for mysql/maria #1992
  • Add fetchAsString optional parameter to oracledb dialect #1998
  • fix: escapeObject parameter order for Postgres dialect. #2003

0.12.9 - 23 Mar, 2017

  • Fixed unhandled exception in batchInsert when the rows to be inserted resulted in duplicate key violation #1880

0.12.8 - 15 Mar, 2017

  • Added clearSelect and clearWhere to query builder #1912
  • Properly close Postgres query streams on error #1935
  • Transactions should never reject with undefined #1970
  • Clear acquireConnectionTimeout if an error occurs when acquiring a connection #1973

0.12.7 - 17 Feb, 2017

Accidental Breaking Change

  • Ensure that 'client' is provided in knex config object #1822

Other Changes

  • Support custom foreign key names #1311, #1726
  • Fixed named bindings to work with queries containing :-chars #1890
  • Exposed more promise functions #1896
  • Pass rollback errors to transaction promise in mssql #1885
  • ONLY keyword support for PostgreSQL (for table inheritance) #1874
  • Fixed Mssql update with join syntax #1777
  • Replace migrations and seed for react-native packager #1813
  • Support knexfile, migration and seeds in TypeScript #1769
  • Fix float to integer conversion of decimal fields in MSSQL #1781
  • External authentication capability when using oracledb driver #1716
  • Fixed MSSQL incorect query build when locks are used #1707
  • Allow to use first method as aliased select #1784
  • Alter column for nullability, type and default value #46, #1759
  • Add more having* methods / join clause on* methods #1674
  • Compatibility fixes and cleanups #1788, #1792, #1794, #1814, #1857, #1649

0.12.6 - 19 Oct, 2016

  • Address warnings mentioned in #1388 (#1740)
  • Remove postinstall script (#1746)

0.12.5 - 12 Oct, 2016

  • Fix broken 0.12.4 build (removed from npm)
  • Fix #1733, #920, incorrect postgres array bindings

0.12.3 - 9 Oct, 2016

  • Fix #1703, #1694 - connections should be returned to pool if acquireConnectionTimeout is triggered
  • Fix #1710 regression in postgres array escaping

0.12.2 - 27 Sep, 2016

  • Restore pool min: 1 for sqlite3, #1701
  • Fix for connection error after it's closed / released, #1691
  • Fix oracle prefetchRowCount setting, #1675

0.12.1 - 16 Sep, 2016

  • Fix MSSQL sql execution error, #1669
  • Added DEBUG=knex:bindings for debugging query bindings, #1557

0.12.0 - 13 Sep, 2016

  • Remove build / built files, #1616
  • Upgrade to Babel 6, #1617
  • Reference Bluebird module directly, remove deprecated .exec method, #1618
  • Remove documentation files from main repo
  • Fix broken behavior on WebSQL build, #1638
  • Oracle id sequence now handles manual inserts, #906
  • Cleanup PG escaping, fix #1602, #1548
  • Added with to builder for common table expressions, #1599
  • Fix #1619, pluck with explicit column names
  • Switching back to generic-pool for pooling resource management
  • Removed index.html, please direct all PR's for docs against the files in knex/documentation

0.11.10 - 9 Aug, 2016

0.11.9 - 21 Jul, 2016

  • Reverted knex client breaking change (commit b74cd69e906), fixes #1587

0.11.8 - 21 Jul, 2016

  • Oracledb dialect #990
  • Documentation fix #1532
  • Allow named bindings to be escaped. #1576
  • Several bugs with MS SQL schema creation and installing from gihub fix #1577
  • Fix incorrect escaping of backslashes in SqlString.escape #1545

0.11.7 - 19 Jun, 2016

  • Add missing dependency. #1516

0.11.6 - 18 Jun, 2016

  • Allow cancellation on timeout (MySQL) #1454
  • Better bigint support. (MSSQL) #1445
  • More consistent handling of undefined values in QueryBuilder#where and Raw. #1459
  • Fix Webpack build. #1447
  • Fix code that triggered Bluebird warnings. #1460, #1489
  • Fix ping function. (Oracle) #1486
  • Fix columnInfo. (MSSQL) #1464
  • Fix ColumnCompiler#binary. (MSSQL) #1464
  • Allow connection strings that do not contain a password. #1473
  • Fix race condition in seed stubs. #1493
  • Give each query a UUID. #1510

0.11.5 - 26 May, 2016

  • Bugfix: Using Raw or QueryBuilder as a binding to Raw now works as intended

0.11.4 - 22 May, 2016

  • Bugfix: Inconsistency of .primary() and .dropPrimary() between dialects #1430
  • Feature: Allow using custom Client/Dialect (you can pass your own client in knex config) #1428
  • Docs: Add documentation for .dropTimestamps #1432
  • Bugfix: Fixed passing undefined fields for insert/update inside transaction #1423
  • Feature: batchInsert with existing transaction #1354
  • Build: eslint instead of jshint #1416
  • Bugfix: Pooled connections not releasing #1382
  • Bugfix: Support passing knex.raw to .whereNot #1402
  • Docs: Fixed list of dialects which supports .returning #1398
  • Bugfix: rename table does not fail anymore even with schema defined #1403

0.11.3 - 14 May, 2016

  • Support nested joins. #1397

0.11.2 - 14 May, 2016

  • Prevent crash on knex seed:make. #1389
  • Improvements to batchInsert. #1391
  • Improvements to inserting DEFAULT with undefined binding. #1396
  • Correct generated code for adding/dropping multiple columns. (MSSQL) #1401

0.11.1 - 6 May, 2016

  • Fix error in CLI command migrate:make. #1386

0.11.0 - 5 May, 2016

Breaking changes

  • QueryBuilder#orWhere joins multiple arguments with AND. #1164

Other Changes

  • Collate for columns. (MySQL) #1147
  • Add QueryBuilder#timeout, Raw#timeout. #1201 #1260
  • Exit with error code when appropriate. #1238
  • MSSQL connection accepts host as an alias for server in accordance with other dialects. #1239
  • Add query-response event. #1231
  • Correct behaviour of sibling nested transactions. #1226
  • Support RETURNING with UPDATE. (Oracle) #1253
  • Throwing callbacks from transactions automatically rolls them back. #1257
  • Fixes to named Raw bindings. #1251
  • timestamps accepts an argument to set NOT NULL and default to current timestamp.
  • Add TableBuilder#inherits for PostgreSQL. #601
  • Wrap index names. #1289
  • Restore coffeescript knexfiles and configurations. #1292
  • Add andWhereBetween and andWhereNotBetween #1132
  • Fix valueForUndefined failure. #1269
  • renameColumn no longer drops default value or nullability. #1326
  • Correct MySQL2 error handling. #1315
  • Fix MSSQL createTableIfNotExists. #1362
  • Fix MSSQL URL parsing. #1342
  • Update Lodash to 4.6.0 #1242
  • Update Bluebird to 3.3.4 #1279

0.10.0 - 15 Feb, 2016

Breaking changes

  • insert and update now ignore undefined values. Back compatibility is provided through the option useNullAsDefault. #1174, #1043

Other Changes

  • countDistinct, avgDistinct and sumDistinct. #1046
  • Add schema.jsonb. Deprecated schema.json(column, true). #991
  • Support binding identifiers with ??. #1103
  • Restore query event when triggered by transactions. #855
  • Correct question mark escaping in rendered queries. #519, #1058
  • Add per-dialect escaping, allowing quotes to be escaped correctly. #886, #1095
  • Add MSSQL support. #1090
  • Add migration locking. #1094
  • Allow column aliases to contain .. #1181
  • Add batchInsert. #1182
  • Support non-array arguments to knex.raw.
  • Global query-error event. #1163
  • Add batchInsert. #1182
  • Better support for Mysql2 dialect options. #980
  • Support for acquireConnectionTimeout default 60 seconds preventing #1040 from happening. #1177
  • Fixed constraint name escaping when dropping a constraint. #1177
  • Show also .raw queries in debug output. #1169
  • Support for cli to use basic configuration without specific environment set. #1101

0.9.0 - Nov 2, 2015

  • Fix error when merging knex.raw instances without arguments. #853
  • Fix error that caused the connection to time out while streaming. #849
  • Correctly parse SSL query parameter for PostgreSQL. #852
  • Pass compress option to MySQL2. #843
  • Schema: Use timestamp with timezone by default for time, datetime and timestamp for Oracle. #876
  • Add QueryBuilder#modify #881
  • Add LiveScript and Early Gray support for seeds and migrations.
  • Add QueryBuilder#withSchema #518
  • Allow escaping of ? in knex.raw queries. #946
  • Allow 0 in join clause. #953
  • Add migration config to allow disabling/enabling transactions per migration. #834

0.8.6 - May 20, 2015

0.8.5 - May 14, 2015

  • Pool should be initialized if no pool options are specified

0.8.4 - May 13, 2015

  • Pool should not be initialized if {max: 0} is sent in config options

0.8.3 - May 2, 2015

  • Alias postgresql -> postgres in connection config options

0.8.2 - May 1, 2015

  • Fix regression in using query string in connection config

0.8.1 - May 1, 2015

  • Warn rather than error when implicit commits wipe out savepoints in mysql / mariadb, #805.
  • Fix for incorrect seed config reference, #804

0.8.0 - Apr 30, 2015

New features

  • Fixes several major outstanding bugs with the connection pool, switching to Pool2 in place of generic-pool-redux
  • strong-oracle module support
  • Nested transactions automatically become savepoints, with commit & rollback releasing or rolling back the current savepoint.
  • Database seed file support, #391
  • Improved support for sub-raw queries within raw statements
  • Migrations are now wrapped in transactions where possible
  • Subqueries supported in insert statements, #627
  • Support for nested having, #572
  • Support object syntax for joins, similar to "where" #743

Major Changes

  • Transactions are immediately invoked as A+ promises, #470 (this is a feature and should not actually break anything in practice)
  • Heavy refactoring internal APIs (public APIs should not be affected)

Other Changes

  • Allow mysql2 to use non-default port, #588
  • Support creating & dropping extensions in PostgreSQL, #540
  • CLI support for knexfiles that do not provide environment keys, #527
  • Added sqlite3 dialect version of whereRaw/andWhereRaw (#477)

0.7.5 - Mar 9, 2015

  • Fix bug in validateMigrationList, (#697)

0.7.4 - Feb 25, 2015

  • Fix incorrect order of query parameters when using subqueries, #704
  • Properly handle limit 0, (#655)
  • Apply promise args from then instead of explicitly passing.
  • Respect union parameter as last argument (#660).
  • Added sqlite3 dialect version of whereRaw/andWhereRaw (#477).
  • Fix SQLite dropColumn doesn't work for last column (#544).
  • Add POSIX operator support for Postgres (#562)
  • Sample seed files now correctly (#391)

0.7.3 - Oct 3, 2014

  • Support for join(table, rawOrBuilder) syntax.
  • Fix for regression in PostgreSQL connection (#516)

0.7.2 - Oct 1, 2014

  • Fix for regression in migrations

0.7.1 - Oct 1, 2014

  • Better disconnect handling & pool removal for MySQL clients, #452

0.7.0 - Oct 1, 2014

New features

  • Oracle support, #419
  • Database seed file support, #391
  • Improved support for sub-raw queries within raw statements

Breaking changes

  • "collate nocase" no longer used by default in sqlite3 #396

Other Changes

  • Bumping Bluebird to ^2.x
  • Transactions in websql are now a no-op (unsupported) #375
  • Improved test suite
  • knex.fn namespace as function helper (knex.fn.now), #372
  • Better handling of disconnect errors
  • Support for offset without limit, #446
  • Chainable first method for mysql schema, #406
  • Support for empty array in whereIn
  • Create/drop schema for postgres, #511
  • Inserting multiple rows with default values, #468
  • Join columns are optional for cross-join, #508
  • Flag for creating jsonb columns in Postgresql, #500

0.6.22 - July 10, 2014

  • Bug fix for properly binding postgresql streaming queries, (#363)

0.6.21 - July 9, 2014

  • Bug fix for raw queries not being transaction context aware, (#351).
  • Properly forward stream errors in sqlite3 runner, (#359)

0.6.20 - June 30, 2014

  • Allow case insensitive operators in sql clauses, (#344)

0.6.19 - June 27, 2014

  • Add groupByRaw / orderByRaw methods, better support for raw statements in group / order (#282).
  • Support more config options for node-mysql2 dialect (#341).
  • CLI help text fix, (#342)

0.6.18 - June 25, 2014

  • Patch for the method, calling without a handler should return the stream, not a promise (#337)

0.6.17 - June 23, 2014

  • Adding missing map / reduce proxies to bluebird's implementation

0.6.16 - June 18, 2014

  • Increment / decrement returns the number of affectedRows (#330).
  • Allow --cwd option flag to be passed to CLI tool (#326)

0.6.15 - June 14, 2014

  • Added the as method for aliasing subqueries

0.6.14 - June 14, 2014

  • whereExists / whereNotExists may now take a query builder instance as well as a callback

0.6.13 - June 12, 2014

  • Fix regression with onUpdate / onDelete in PostgreSQL, (#308).
  • Add missing Promise require to knex.js, unit test for knex.destroy (#314)

0.6.12 - June 10, 2014

  • Fix for regression with boolean default types in PostgreSQL

0.6.11 - June 10, 2014

  • Fix for regression with queries containing multiple order by statements in sqlite3

0.6.10 - June 10, 2014

  • Fix for big regression in memoization of column names from 0.5 -> 0.6

0.6.9 - June 9, 2014

  • Fix for regression in specificType method

0.6.8 - June 9, 2014

  • Package.json fix for CLI

0.6.7 - June 9, 2014

  • Adds support for node-mysql2 library.
  • Bundles CLI with the knex install, various related migrate CLI fixes

0.6.6 - June 9, 2014

  • console.warn rather than throw when adding foreignKeys in SQLite3.
  • Add support for dropColumn in SQLite3.
  • Document raw.wrap

0.6.5 - June 9, 2014

  • Add missing _ require to WebSQL builds

0.6.4 - June 9, 2014

  • Fix & document schema.raw method

0.6.3 - June 6, 2014

  • Schema methods on transaction object are now transaction aware (#301).
  • Fix for resolved value from transactions, (#298).
  • Undefined columns are not added to builder

0.6.2 - June 4, 2014

  • Fix regression in raw query output, (#297).
  • Fix regression in "pluck" method (#296).
  • Document first method

0.6.1 - June 4, 2014

  • Reverting to using .npmignore, the "files" syntax forgot the knex.js file

0.6.0 - June 4, 2014

Major Library refactor

  • Major internal overhaul to clean up the various dialect code.
  • Improved unit test suite.
  • Support for the mariasql driver.
  • More consistent use of raw query bindings throughout the library.
  • Queries are more composable, may be injected in various points throughout the builder.
  • Added streaming interface
  • Deprecated 5 argument join in favor of additional join methods.
  • The wrapValue function to allow for array column operations in PostgreSQL (#287).
  • An explicit connection can be passed for any query (#56).
  • Drop column support for sqlite3
  • All schema actions are run sequentially on the same connection if chained.
  • Schema actions can now be wrapped in a transaction
  • .references(tableName.columnName) as shorthand for .references(columnName).inTable(tableName)
  • .join('table.column', 'otherTable.column') as shorthand for .join('table.column', '=', 'otherTable.column')
  • Streams are supported for selects, passing through to the streaming capabilities of node-mysql and node-postgres
  • For More information, see this pull-request

0.5.15 - June 4, 2014

  • Dropped indexes feature now functions correctly, (#278)

0.5.14 - May 6, 2014

  • Remove the charset encoding if it's utf8 for mysql, as it's the default but also currently causes some issues in recent versions of node-mysql

0.5.13 - April 2, 2014

  • Fix regression in array bindings for postgresql (#228)

0.5.12 - Mar 31, 2014

  • Add more operators for where clauses, including && (#226)

0.5.11 - Mar 25, 2014

  • .where(col, 'is', null) or .where(col, 'is not', null) are not supported (#221).
  • Case insensitive where operators now allowed (#212).
  • Fix bug in increment/decrement truncating to an integer (#210).
  • Disconnected connections are now properly handled & removed from the pool (#206).
  • Internal tweaks to binding concatenations for performance (#207)

0.5.10 - Mar 19, 2014

  • Add the .exec method to the internal promise shim

0.5.9 - Mar 18, 2014

  • Remove error'ed connections from the connection pool (#206), added support for node-postgres-pure (pg.js) (#200)

0.5.8 - Feb 27, 2014

  • Fix for chaining on forUpdate / forShare, adding map & reduce from bluebird

0.5.7 - Feb 18, 2014

  • Fix for a null limit / offset breaking query chain (#182)

0.5.6 - Feb 5, 2014

  • Bump bluebird dependency to ~1.0.0, fixing regression in Bluebird 1.0.2 (#176)

0.5.5 - Jan 28, 2014

  • Fix for the exit code on the migrations cli (#151).
  • The init method in knex.migrate now uses this.config if one isn't passed in (#156)

0.5.4 - Jan 7, 2014

  • Fix for using raw statements in defaultTo schema builder methods (#146)

0.5.3 - Jan 2, 2014

  • Fix for incorrectly formed sql when aggregates are used with columns (#144)

0.5.2 - Dec 18, 2013

  • Adding passthrough "catch", "finally" to bluebird implementations, use bluebird's "nodeify" internally for exec

0.5.1 - Dec 12, 2013

  • The returning in PostgreSQL may now accept * or an array of columns to return. If either of these are passed, the response will be an array of objects rather than an array of values. Updates may also now use a returning value. (#132)
  • Added bigint and bigserial type to PostgreSQL. (#111)
  • Fix for the specificType schema call (#118)
  • Several fixes for migrations, including migration file path fixes, passing a Promise constructor to the migration up and down methods, allowing the "knex" module to be used globally, file ordering on migrations, and other small improvements. (#112-115, #125, #135)

0.5.0 - Nov 25, 2013

  • Initial pass at a migration api.
  • Aggregate methods are no longer aliased as "aggregate", but may now be aliased and have more than one aggregate in a query (#108, #110).
  • Adding bigint and bigserial to PostgreSQL (#111).
  • Bugfix on increment/decrement values (#100).
  • Bugfix with having method (#107).
  • Switched from when.js to bluebird for promise implementation, with shim for backward compatibility.
  • Switched from underscore to lodash, for semver reliability

0.4.13 - Oct 31, 2013

  • Fix for aggregate methods on toString and clone, (#98)

0.4.12 - Oct 29, 2013

  • Fix incorrect values passed to float in MySQL and decimal in PostgreSQL

0.4.11 - Oct 15, 2013

  • Fix potential sql injection vulnerability in orderBy, thanks to @sebgie

0.4.10 - Oct 14, 2013

  • Added forUpdate and forShare for select modes in transactions. (#84)
  • Fix bug where current query chain type is not copied on clone. (#90)
  • Charset and collate are now added as methods on the schema builder. (#89)
  • Added into as an alias of from, for builder syntax of: insert(value).into(tableName)
  • Internal pool fixes. (#90)

0.4.9 - Oct 7, 2013

  • Fix for documentation of hasColumn, ensure that hasColumn works with MySQL (#87).
  • More cleanup of error messages, showing the original error message concatenated with the sql and bindings

0.4.8 - Oct 2, 2013

  • Connections are no longer pushed back into the pool if they never existed to begin with (#85)

0.4.7 - Sep 27, 2013

  • The column is now a documented method on the builder api, and takes either an individual column or an array of columns to select

0.4.6 - Sep 25, 2013

  • Standardizing handling of errors for easier debugging, as noted in (#39)

0.4.5 - Sep 24, 2013

  • Fix for hasTable always returning true in MySQL (#82), fix where sql queries were duplicated with multiple calls on toSql with the schema builder

0.4.4 - Sep 22, 2013

  • Fix for debug method not properly debugging individual queries

0.4.3 - Sep 18, 2013

  • Fix for underscore not being defined in various grammar files

0.4.2 - Sep 17, 2013

  • Fix for an error being thrown when an initialized ClientBase instance was passed into Knex.initialize. pool.destroy now optionally accepts a callback to notify when it has completed draining and destroying all connections

0.4.1 - Sep 16, 2013

  • Cleanup from the 0.4.0 release, fix a potential exploit in "where" clauses pointed out by Andri M\xF6ll, fix for clients not being properly released from the pool #70, fix for where("foo", "<>", null) doing an "IS NULL" statement

0.4.0 - Sep 13, 2013

Breaking changes

  • Global state is no longer stored in the library, an instance is returned from Knex.initialize, so you will need to call this once and then reference this knex client elsewhere in your application.
  • Lowercasing of knex.raw, knex.transaction, and knex.schema.
  • Created columns are now nullable by default, unless notNullable is chained as an option.
  • Keys created with increments are now assumed to be unsigned (MySQL) by default.
  • The destroyAllNow is no longer called by the library on process.exit event. If you need to call it explicitly yourself, you may use knex.client.destroyPool

0.2.6 - Aug 29, 2013

  • Reject the transaction promise if the transaction "commit" fails, (#50)

0.2.5 - Aug 25, 2013

  • Fix error if a callback isn't specified for exec, (#49)

0.2.4 - Aug 22, 2013

  • Fix SQLite3 delete not returning affected row count, (#45)

0.2.3 - Aug 22, 2013

  • Fix insert with default values in PostgreSQL and SQLite3, (#44)

0.2.2 - Aug 20, 2013

  • Allowing Raw queries to be passed as the primary table names

0.2.1 - Aug 13, 2013

  • Fix for an array passed to insert being mutated

0.2.0 - Aug 7, 2013

Breaking changes

  • hasTable now returns a boolean rather than a failed promise.
  • Changed syntax for insert in postgresql, where the id is not assumed on inserts (#18). The second parameter of insert is now required to return an array of insert id's for the last insert.
  • The timestamp method on the schema builder now uses a dateTime rather than a timestamp

0.1.8 - July 7, 2013

  • Somehow missing the != operator. Using _.find rather than _.where in getCommandsByName(#22)

0.1.7 - June 12, 2013

  • Ensures unhandled errors in the exec callback interface are re-thrown

0.1.6 - June 9, 2013

  • Renaming beforeCreate to afterCreate. Better handling of errors in the connection pooling

0.1.5 - June 9, 2013

  • Added the ability to specify beforeCreate and beforeDestroy hooks on the initialize's options.pool to perform any necessary database setup/teardown on connections before use (#14). where and having may now accept Knex.Raw instances, for consistency (#15). Added an orHaving method to the builder. The ability to specify bindings on Raw queries has been removed

0.1.4 - May 22, 2013

  • defaultTo now accepts "false" for boolean columns, allows for empty strings as default values

0.1.3 - May 18, 2013

  • Enabling table aliases (#11). Fix for issues with transactions not functioning (#12)

0.1.2 - May 15, 2013

  • Bug fixes for groupBy (#7). Mysql using collation, charset config settings in createTable. Added engine on schemaBuilder specifier (#6). Other doc fixes, tests

0.1.1 - May 14, 2013

  • Bug fixes for sub-queries, minor changes to initializing "main" instance, adding "pg" as a valid parameter for the client name in the connection settings

0.1.0 - May 13, 2013

  • Initial Knex release
',820),i=[o];function s(l,h,u,p,g,c){return n(),r("div",null,i)}var k=e(a,[["render",s]]);export{d as __pageData,k as default}; diff --git a/assets/changelog.md.997a0d36.lean.js b/assets/changelog.md.997a0d36.lean.js new file mode 100644 index 00000000..daa6dd4c --- /dev/null +++ b/assets/changelog.md.997a0d36.lean.js @@ -0,0 +1 @@ +import{_ as e,c as r,o as n,a as t}from"./app.ef097145.js";const d='{"title":"Changelog","description":"","frontmatter":{},"headers":[{"level":2,"title":"Changelog","slug":"changelog"},{"level":3,"title":"2.5.1 - 12 July, 2023","slug":"_2-5-1-12-july-2023"},{"level":3,"title":"2.5.0 - 08 July, 2023","slug":"_2-5-0-08-july-2023"},{"level":3,"title":"2.4.2 - 22 January, 2023","slug":"_2-4-2-22-january-2023"},{"level":3,"title":"2.4.1 - 18 January, 2023","slug":"_2-4-1-18-january-2023"},{"level":3,"title":"2.4.0 - 6 January, 2023","slug":"_2-4-0-6-january-2023"},{"level":3,"title":"2.3.0 - 31 August, 2022","slug":"_2-3-0-31-august-2022"},{"level":3,"title":"2.2.0 - 18 July, 2022","slug":"_2-2-0-18-july-2022"},{"level":3,"title":"2.1.0 - 26 May, 2022","slug":"_2-1-0-26-may-2022"},{"level":3,"title":"2.0.0 - 21 April, 2022","slug":"_2-0-0-21-april-2022"},{"level":3,"title":"1.0.7 - 13 March, 2022","slug":"_1-0-7-13-march-2022"},{"level":3,"title":"1.0.6 - 12 March, 2022","slug":"_1-0-6-12-march-2022"},{"level":3,"title":"1.0.5 - 05 March, 2022","slug":"_1-0-5-05-march-2022"},{"level":3,"title":"1.0.4 - 13 March, 2022","slug":"_1-0-4-13-march-2022"},{"level":3,"title":"1.0.3 - 11 February, 2022","slug":"_1-0-3-11-february-2022"},{"level":3,"title":"1.0.2 - 02 February, 2022","slug":"_1-0-2-02-february-2022"},{"level":3,"title":"1.0.1 - 16 January, 2022","slug":"_1-0-1-16-january-2022"},{"level":3,"title":"1.0.0 - 16 January, 2022","slug":"_1-0-0-16-january-2022"},{"level":3,"title":"0.95.15 - 22 December, 2021","slug":"_0-95-15-22-december-2021"},{"level":3,"title":"0.95.14 - 09 November, 2021","slug":"_0-95-14-09-november-2021"},{"level":3,"title":"0.95.13 - 02 November, 2021","slug":"_0-95-13-02-november-2021"},{"level":3,"title":"0.95.12 - 28 October, 2021","slug":"_0-95-12-28-october-2021"},{"level":3,"title":"0.95.11 - 03 September, 2021","slug":"_0-95-11-03-september-2021"},{"level":3,"title":"0.95.10 - 20 August, 2021","slug":"_0-95-10-20-august-2021"},{"level":3,"title":"0.95.9 - 31 July, 2021","slug":"_0-95-9-31-july-2021"},{"level":3,"title":"0.95.8 - 25 July, 2021","slug":"_0-95-8-25-july-2021"},{"level":3,"title":"0.95.7 - 10 July, 2021","slug":"_0-95-7-10-july-2021"},{"level":3,"title":"0.95.6 - 17 May, 2021","slug":"_0-95-6-17-may-2021"},{"level":3,"title":"0.95.5 - 11 May, 2021","slug":"_0-95-5-11-may-2021"},{"level":3,"title":"0.95.4 - 26 March, 2021","slug":"_0-95-4-26-march-2021"},{"level":3,"title":"0.95.3 - 25 March, 2021","slug":"_0-95-3-25-march-2021"},{"level":3,"title":"0.95.2 - 11 March, 2021","slug":"_0-95-2-11-march-2021"},{"level":3,"title":"0.95.1 - 04 March, 2021","slug":"_0-95-1-04-march-2021"},{"level":3,"title":"0.95.0 - 03 March, 2021","slug":"_0-95-0-03-march-2021"},{"level":3,"title":"0.21.19 - 02 March, 2021","slug":"_0-21-19-02-march-2021"},{"level":3,"title":"0.21.18 - 22 February, 2021","slug":"_0-21-18-22-february-2021"},{"level":3,"title":"0.21.17 - 30 January, 2021","slug":"_0-21-17-30-january-2021"},{"level":3,"title":"0.21.16 - 17 January, 2021","slug":"_0-21-16-17-january-2021"},{"level":3,"title":"0.21.15 - 26 December, 2020","slug":"_0-21-15-26-december-2020"},{"level":3,"title":"0.21.14 - 18 December, 2020","slug":"_0-21-14-18-december-2020"},{"level":3,"title":"0.21.13 - 12 December, 2020","slug":"_0-21-13-12-december-2020"},{"level":3,"title":"0.21.12 - 02 November, 2020","slug":"_0-21-12-02-november-2020"},{"level":3,"title":"0.21.11 - 01 November, 2020","slug":"_0-21-11-01-november-2020"},{"level":3,"title":"0.21.10 - 31 October, 2020","slug":"_0-21-10-31-october-2020"},{"level":3,"title":"0.21.9 - 27 October, 2020","slug":"_0-21-9-27-october-2020"},{"level":3,"title":"0.21.8 - 27 October, 2020","slug":"_0-21-8-27-october-2020"},{"level":3,"title":"0.21.7 - 25 October, 2020","slug":"_0-21-7-25-october-2020"},{"level":3,"title":"0.21.6 - 27 September, 2020","slug":"_0-21-6-27-september-2020"},{"level":3,"title":"0.21.5 - 17 August, 2020","slug":"_0-21-5-17-august-2020"},{"level":3,"title":"0.21.4 - 10 August, 2020","slug":"_0-21-4-10-august-2020"},{"level":3,"title":"0.21.3 - 08 August, 2020","slug":"_0-21-3-08-august-2020"},{"level":3,"title":"0.21.2 - 10 July, 2020","slug":"_0-21-2-10-july-2020"},{"level":3,"title":"0.21.1 - 28 April, 2020","slug":"_0-21-1-28-april-2020"},{"level":3,"title":"0.21.0 - 18 April, 2020","slug":"_0-21-0-18-april-2020"},{"level":3,"title":"0.20.15 - 16 April, 2020","slug":"_0-20-15-16-april-2020"},{"level":3,"title":"0.20.14 - 13 April, 2020","slug":"_0-20-14-13-april-2020"},{"level":3,"title":"0.20.13 - 23 March, 2020","slug":"_0-20-13-23-march-2020"},{"level":3,"title":"0.20.12 - 19 March, 2020","slug":"_0-20-12-19-march-2020"},{"level":3,"title":"0.20.11 - 26 February, 2020","slug":"_0-20-11-26-february-2020"},{"level":3,"title":"0.20.10 - 13 February, 2020","slug":"_0-20-10-13-february-2020"},{"level":3,"title":"0.20.9 - 08 February, 2020","slug":"_0-20-9-08-february-2020"},{"level":3,"title":"0.20.8 - 14 January, 2020","slug":"_0-20-8-14-january-2020"},{"level":3,"title":"0.20.7 - 07 January, 2020","slug":"_0-20-7-07-january-2020"},{"level":3,"title":"0.20.6 - 29 December, 2019","slug":"_0-20-6-29-december-2019"},{"level":3,"title":"0.20.5 - 29 December, 2019","slug":"_0-20-5-29-december-2019"},{"level":3,"title":"0.20.4 - 08 December, 2019","slug":"_0-20-4-08-december-2019"},{"level":3,"title":"0.20.3 - 27 November, 2019","slug":"_0-20-3-27-november-2019"},{"level":3,"title":"0.20.2 - 14 November, 2019","slug":"_0-20-2-14-november-2019"},{"level":3,"title":"0.20.1 - 29 October, 2019","slug":"_0-20-1-29-october-2019"},{"level":3,"title":"0.20.0 - 25 October, 2019","slug":"_0-20-0-25-october-2019"},{"level":3,"title":"0.19.5 - 06 October, 2019","slug":"_0-19-5-06-october-2019"},{"level":3,"title":"0.19.4 - 09 September, 2019","slug":"_0-19-4-09-september-2019"},{"level":3,"title":"0.19.3 - 25 August, 2019","slug":"_0-19-3-25-august-2019"},{"level":3,"title":"0.19.2 - 17 August, 2019","slug":"_0-19-2-17-august-2019"},{"level":3,"title":"0.19.1 - 23 July, 2019","slug":"_0-19-1-23-july-2019"},{"level":3,"title":"0.19.0 - 11 July, 2019","slug":"_0-19-0-11-july-2019"},{"level":3,"title":"0.18.4 - 10 July, 2019","slug":"_0-18-4-10-july-2019"},{"level":3,"title":"0.18.3 - 04 July, 2019","slug":"_0-18-3-04-july-2019"},{"level":3,"title":"0.18.2 - 03 July, 2019","slug":"_0-18-2-03-july-2019"},{"level":3,"title":"0.18.1 - 30 June, 2019","slug":"_0-18-1-30-june-2019"},{"level":3,"title":"0.18.0 - 26 June, 2019","slug":"_0-18-0-26-june-2019"},{"level":3,"title":"0.17.5 - 8 June, 2019","slug":"_0-17-5-8-june-2019"},{"level":3,"title":"0.17.4 - 8 June, 2019","slug":"_0-17-4-8-june-2019"},{"level":3,"title":"0.17.3 - 2 June, 2019","slug":"_0-17-3-2-june-2019"},{"level":3,"title":"0.17.2 - 1 June, 2019","slug":"_0-17-2-1-june-2019"},{"level":3,"title":"0.17.1 - 31 May, 2019","slug":"_0-17-1-31-may-2019"},{"level":3,"title":"0.17.0 - 28 May, 2019","slug":"_0-17-0-28-may-2019"},{"level":3,"title":"0.16.5 - 11 Apr, 2019","slug":"_0-16-5-11-apr-2019"},{"level":3,"title":"0.16.4 - 11 Apr, 2019","slug":"_0-16-4-11-apr-2019"},{"level":3,"title":"0.16.3 - 19 Dec, 2018","slug":"_0-16-3-19-dec-2018"},{"level":3,"title":"0.16.2 - 10 Dec, 2018","slug":"_0-16-2-10-dec-2018"},{"level":3,"title":"0.16.1 - 28 Nov, 2018","slug":"_0-16-1-28-nov-2018"},{"level":3,"title":"0.16.0 - 27 Nov, 2018","slug":"_0-16-0-27-nov-2018"},{"level":3,"title":"0.15.2 - 19 Jul, 2018","slug":"_0-15-2-19-jul-2018"},{"level":3,"title":"0.15.1 - 12 Jul, 2018","slug":"_0-15-1-12-jul-2018"},{"level":3,"title":"0.15.0 - 1 Jul, 2018","slug":"_0-15-0-1-jul-2018"},{"level":3,"title":"0.14.6 - 12 Apr, 2018","slug":"_0-14-6-12-apr-2018"},{"level":3,"title":"0.14.5 - 8 Apr, 2018","slug":"_0-14-5-8-apr-2018"},{"level":3,"title":"0.14.4 - 19 Feb, 2018","slug":"_0-14-4-19-feb-2018"},{"level":3,"title":"0.14.3 - 8 Feb, 2018","slug":"_0-14-3-8-feb-2018"},{"level":3,"title":"0.14.2 - 24 Nov, 2017","slug":"_0-14-2-24-nov-2017"},{"level":3,"title":"0.14.1 - 19 Nov, 2017","slug":"_0-14-1-19-nov-2017"},{"level":3,"title":"0.14.0 - 6 Nov, 2017","slug":"_0-14-0-6-nov-2017"},{"level":3,"title":"0.13.0 - 29 Apr, 2017","slug":"_0-13-0-29-apr-2017"},{"level":3,"title":"0.12.9 - 23 Mar, 2017","slug":"_0-12-9-23-mar-2017"},{"level":3,"title":"0.12.8 - 15 Mar, 2017","slug":"_0-12-8-15-mar-2017"},{"level":3,"title":"0.12.7 - 17 Feb, 2017","slug":"_0-12-7-17-feb-2017"},{"level":3,"title":"0.12.6 - 19 Oct, 2016","slug":"_0-12-6-19-oct-2016"},{"level":3,"title":"0.12.5 - 12 Oct, 2016","slug":"_0-12-5-12-oct-2016"},{"level":3,"title":"0.12.3 - 9 Oct, 2016","slug":"_0-12-3-9-oct-2016"},{"level":3,"title":"0.12.2 - 27 Sep, 2016","slug":"_0-12-2-27-sep-2016"},{"level":3,"title":"0.12.1 - 16 Sep, 2016","slug":"_0-12-1-16-sep-2016"},{"level":3,"title":"0.12.0 - 13 Sep, 2016","slug":"_0-12-0-13-sep-2016"},{"level":3,"title":"0.11.10 - 9 Aug, 2016","slug":"_0-11-10-9-aug-2016"},{"level":3,"title":"0.11.9 - 21 Jul, 2016","slug":"_0-11-9-21-jul-2016"},{"level":3,"title":"0.11.8 - 21 Jul, 2016","slug":"_0-11-8-21-jul-2016"},{"level":3,"title":"0.11.7 - 19 Jun, 2016","slug":"_0-11-7-19-jun-2016"},{"level":3,"title":"0.11.6 - 18 Jun, 2016","slug":"_0-11-6-18-jun-2016"},{"level":3,"title":"0.11.5 - 26 May, 2016","slug":"_0-11-5-26-may-2016"},{"level":3,"title":"0.11.4 - 22 May, 2016","slug":"_0-11-4-22-may-2016"},{"level":3,"title":"0.11.3 - 14 May, 2016","slug":"_0-11-3-14-may-2016"},{"level":3,"title":"0.11.2 - 14 May, 2016","slug":"_0-11-2-14-may-2016"},{"level":3,"title":"0.11.1 - 6 May, 2016","slug":"_0-11-1-6-may-2016"},{"level":3,"title":"0.11.0 - 5 May, 2016","slug":"_0-11-0-5-may-2016"},{"level":3,"title":"0.10.0 - 15 Feb, 2016","slug":"_0-10-0-15-feb-2016"},{"level":3,"title":"0.9.0 - Nov 2, 2015","slug":"_0-9-0-nov-2-2015"},{"level":3,"title":"0.8.6 - May 20, 2015","slug":"_0-8-6-may-20-2015"},{"level":3,"title":"0.8.5 - May 14, 2015","slug":"_0-8-5-may-14-2015"},{"level":3,"title":"0.8.4 - May 13, 2015","slug":"_0-8-4-may-13-2015"},{"level":3,"title":"0.8.3 - May 2, 2015","slug":"_0-8-3-may-2-2015"},{"level":3,"title":"0.8.2 - May 1, 2015","slug":"_0-8-2-may-1-2015"},{"level":3,"title":"0.8.1 - May 1, 2015","slug":"_0-8-1-may-1-2015"},{"level":3,"title":"0.8.0 - Apr 30, 2015","slug":"_0-8-0-apr-30-2015"},{"level":3,"title":"0.7.5 - Mar 9, 2015","slug":"_0-7-5-mar-9-2015"},{"level":3,"title":"0.7.4 - Feb 25, 2015","slug":"_0-7-4-feb-25-2015"},{"level":3,"title":"0.7.3 - Oct 3, 2014","slug":"_0-7-3-oct-3-2014"},{"level":3,"title":"0.7.2 - Oct 1, 2014","slug":"_0-7-2-oct-1-2014"},{"level":3,"title":"0.7.1 - Oct 1, 2014","slug":"_0-7-1-oct-1-2014"},{"level":3,"title":"0.7.0 - Oct 1, 2014","slug":"_0-7-0-oct-1-2014"},{"level":3,"title":"0.6.22 - July 10, 2014","slug":"_0-6-22-july-10-2014"},{"level":3,"title":"0.6.21 - July 9, 2014","slug":"_0-6-21-july-9-2014"},{"level":3,"title":"0.6.20 - June 30, 2014","slug":"_0-6-20-june-30-2014"},{"level":3,"title":"0.6.19 - June 27, 2014","slug":"_0-6-19-june-27-2014"},{"level":3,"title":"0.6.18 - June 25, 2014","slug":"_0-6-18-june-25-2014"},{"level":3,"title":"0.6.17 - June 23, 2014","slug":"_0-6-17-june-23-2014"},{"level":3,"title":"0.6.16 - June 18, 2014","slug":"_0-6-16-june-18-2014"},{"level":3,"title":"0.6.15 - June 14, 2014","slug":"_0-6-15-june-14-2014"},{"level":3,"title":"0.6.14 - June 14, 2014","slug":"_0-6-14-june-14-2014"},{"level":3,"title":"0.6.13 - June 12, 2014","slug":"_0-6-13-june-12-2014"},{"level":3,"title":"0.6.12 - June 10, 2014","slug":"_0-6-12-june-10-2014"},{"level":3,"title":"0.6.11 - June 10, 2014","slug":"_0-6-11-june-10-2014"},{"level":3,"title":"0.6.10 - June 10, 2014","slug":"_0-6-10-june-10-2014"},{"level":3,"title":"0.6.9 - June 9, 2014","slug":"_0-6-9-june-9-2014"},{"level":3,"title":"0.6.8 - June 9, 2014","slug":"_0-6-8-june-9-2014"},{"level":3,"title":"0.6.7 - June 9, 2014","slug":"_0-6-7-june-9-2014"},{"level":3,"title":"0.6.6 - June 9, 2014","slug":"_0-6-6-june-9-2014"},{"level":3,"title":"0.6.5 - June 9, 2014","slug":"_0-6-5-june-9-2014"},{"level":3,"title":"0.6.4 - June 9, 2014","slug":"_0-6-4-june-9-2014"},{"level":3,"title":"0.6.3 - June 6, 2014","slug":"_0-6-3-june-6-2014"},{"level":3,"title":"0.6.2 - June 4, 2014","slug":"_0-6-2-june-4-2014"},{"level":3,"title":"0.6.1 - June 4, 2014","slug":"_0-6-1-june-4-2014"},{"level":3,"title":"0.6.0 - June 4, 2014","slug":"_0-6-0-june-4-2014"},{"level":3,"title":"0.5.15 - June 4, 2014","slug":"_0-5-15-june-4-2014"},{"level":3,"title":"0.5.14 - May 6, 2014","slug":"_0-5-14-may-6-2014"},{"level":3,"title":"0.5.13 - April 2, 2014","slug":"_0-5-13-april-2-2014"},{"level":3,"title":"0.5.12 - Mar 31, 2014","slug":"_0-5-12-mar-31-2014"},{"level":3,"title":"0.5.11 - Mar 25, 2014","slug":"_0-5-11-mar-25-2014"},{"level":3,"title":"0.5.10 - Mar 19, 2014","slug":"_0-5-10-mar-19-2014"},{"level":3,"title":"0.5.9 - Mar 18, 2014","slug":"_0-5-9-mar-18-2014"},{"level":3,"title":"0.5.8 - Feb 27, 2014","slug":"_0-5-8-feb-27-2014"},{"level":3,"title":"0.5.7 - Feb 18, 2014","slug":"_0-5-7-feb-18-2014"},{"level":3,"title":"0.5.6 - Feb 5, 2014","slug":"_0-5-6-feb-5-2014"},{"level":3,"title":"0.5.5 - Jan 28, 2014","slug":"_0-5-5-jan-28-2014"},{"level":3,"title":"0.5.4 - Jan 7, 2014","slug":"_0-5-4-jan-7-2014"},{"level":3,"title":"0.5.3 - Jan 2, 2014","slug":"_0-5-3-jan-2-2014"},{"level":3,"title":"0.5.2 - Dec 18, 2013","slug":"_0-5-2-dec-18-2013"},{"level":3,"title":"0.5.1 - Dec 12, 2013","slug":"_0-5-1-dec-12-2013"},{"level":3,"title":"0.5.0 - Nov 25, 2013","slug":"_0-5-0-nov-25-2013"},{"level":3,"title":"0.4.13 - Oct 31, 2013","slug":"_0-4-13-oct-31-2013"},{"level":3,"title":"0.4.12 - Oct 29, 2013","slug":"_0-4-12-oct-29-2013"},{"level":3,"title":"0.4.11 - Oct 15, 2013","slug":"_0-4-11-oct-15-2013"},{"level":3,"title":"0.4.10 - Oct 14, 2013","slug":"_0-4-10-oct-14-2013"},{"level":3,"title":"0.4.9 - Oct 7, 2013","slug":"_0-4-9-oct-7-2013"},{"level":3,"title":"0.4.8 - Oct 2, 2013","slug":"_0-4-8-oct-2-2013"},{"level":3,"title":"0.4.7 - Sep 27, 2013","slug":"_0-4-7-sep-27-2013"},{"level":3,"title":"0.4.6 - Sep 25, 2013","slug":"_0-4-6-sep-25-2013"},{"level":3,"title":"0.4.5 - Sep 24, 2013","slug":"_0-4-5-sep-24-2013"},{"level":3,"title":"0.4.4 - Sep 22, 2013","slug":"_0-4-4-sep-22-2013"},{"level":3,"title":"0.4.3 - Sep 18, 2013","slug":"_0-4-3-sep-18-2013"},{"level":3,"title":"0.4.2 - Sep 17, 2013","slug":"_0-4-2-sep-17-2013"},{"level":3,"title":"0.4.1 - Sep 16, 2013","slug":"_0-4-1-sep-16-2013"},{"level":3,"title":"0.4.0 - Sep 13, 2013","slug":"_0-4-0-sep-13-2013"},{"level":3,"title":"0.2.6 - Aug 29, 2013","slug":"_0-2-6-aug-29-2013"},{"level":3,"title":"0.2.5 - Aug 25, 2013","slug":"_0-2-5-aug-25-2013"},{"level":3,"title":"0.2.4 - Aug 22, 2013","slug":"_0-2-4-aug-22-2013"},{"level":3,"title":"0.2.3 - Aug 22, 2013","slug":"_0-2-3-aug-22-2013"},{"level":3,"title":"0.2.2 - Aug 20, 2013","slug":"_0-2-2-aug-20-2013"},{"level":3,"title":"0.2.1 - Aug 13, 2013","slug":"_0-2-1-aug-13-2013"},{"level":3,"title":"0.2.0 - Aug 7, 2013","slug":"_0-2-0-aug-7-2013"},{"level":3,"title":"0.1.8 - July 7, 2013","slug":"_0-1-8-july-7-2013"},{"level":3,"title":"0.1.7 - June 12, 2013","slug":"_0-1-7-june-12-2013"},{"level":3,"title":"0.1.6 - June 9, 2013","slug":"_0-1-6-june-9-2013"},{"level":3,"title":"0.1.5 - June 9, 2013","slug":"_0-1-5-june-9-2013"},{"level":3,"title":"0.1.4 - May 22, 2013","slug":"_0-1-4-may-22-2013"},{"level":3,"title":"0.1.3 - May 18, 2013","slug":"_0-1-3-may-18-2013"},{"level":3,"title":"0.1.2 - May 15, 2013","slug":"_0-1-2-may-15-2013"},{"level":3,"title":"0.1.1 - May 14, 2013","slug":"_0-1-1-may-14-2013"},{"level":3,"title":"0.1.0 - May 13, 2013","slug":"_0-1-0-may-13-2013"}],"relativePath":"changelog.md"}',a={},o=t("",820),i=[o];function s(l,h,u,p,g,c){return n(),r("div",null,i)}var k=e(a,[["render",s]]);export{d as __pageData,k as default}; diff --git a/assets/chunks/AlgoliaSearchBox.3caa7b39.js b/assets/chunks/AlgoliaSearchBox.3caa7b39.js new file mode 100644 index 00000000..71173a57 --- /dev/null +++ b/assets/chunks/AlgoliaSearchBox.3caa7b39.js @@ -0,0 +1 @@ +import{j as v}from"./index.d76f8212.js";import{d as w,u as y,b as x,w as h,e as b,f as P,o as j,c as k,g as C}from"../app.ef097145.js";const R={class:"algolia-search-box",id:"docsearch"},F=w({props:{options:null,multilang:{type:Boolean}},setup(g){var d;const n=g,r=C(),i=y(),c=x();h(()=>n.options,t=>{_(t)}),b(()=>{p(n.options)});function m(t){return t.button===1||t.altKey||t.ctrlKey||t.metaKey||t.shiftKey}function l(t){const{pathname:e,hash:a}=new URL(t);return e+a}function _(t){r&&r.vnode.el&&(r.vnode.el.innerHTML='',p(t))}const{lang:u}=P(),s=n.multilang?["lang:"+u.value]:[];(d=n.options.searchParameters)!=null&&d.facetFilters&&s.push(...n.options.searchParameters.facetFilters),h(u,(t,e)=>{const a=s.findIndex(o=>o==="lang:"+e);a>-1&&s.splice(a,1,"lang:"+t)});function p(t){v(Object.assign({},t,{container:"#docsearch",searchParameters:Object.assign({},t.searchParameters,{facetFilters:s}),navigator:{navigate:({itemUrl:e})=>{const{pathname:a}=new URL(window.location.origin+e);i.path===a?window.location.assign(window.location.origin+e):c.go(e)}},transformItems:e=>e.map(a=>Object.assign({},a,{url:l(a.url)})),hitComponent:({hit:e,children:a})=>{const o=e.url.startsWith("http")?l(e.url):e.url;return{type:"a",ref:void 0,constructor:void 0,key:void 0,props:{href:e.url,onClick:f=>{m(f)||i.path!==o&&(i.path!==o&&f.preventDefault(),c.go(o))},children:a},__v:null}}}))}return(t,e)=>(j(),k("div",R))}});export{F as default}; diff --git a/assets/chunks/AlgoliaSearchBox.8f0df019.js b/assets/chunks/AlgoliaSearchBox.8f0df019.js new file mode 100644 index 00000000..431d71ef --- /dev/null +++ b/assets/chunks/AlgoliaSearchBox.8f0df019.js @@ -0,0 +1 @@ +import{j as v}from"./index.d76f8212.js";import{d as w,u as x,b as y,w as d,e as b,f as k,o as P,c as R,g as j}from"../app.ef097145.js";const C={class:"algolia-search-box",id:"docsearch"},L=w({props:{options:null,multilang:{type:Boolean}},setup(g){var h;const o=g,r=j(),i=x(),c=y();d(()=>o.options,e=>{_(e)}),b(()=>{p(o.options)});function m(e){return e.button===1||e.altKey||e.ctrlKey||e.metaKey||e.shiftKey}function l(e){const{pathname:t,hash:a}=new URL(e);return t+a}function _(e){r&&r.vnode.el&&(r.vnode.el.innerHTML='',p(e))}const{lang:u}=k(),s=o.multilang?["lang:"+u.value]:[];(h=o.options.searchParameters)!=null&&h.facetFilters&&s.push(...o.options.searchParameters.facetFilters),d(u,(e,t)=>{const a=s.findIndex(n=>n==="lang:"+t);a>-1&&s.splice(a,1,"lang:"+e)});function p(e){v(Object.assign({},e,{container:"#docsearch",searchParameters:Object.assign({},e.searchParameters,{facetFilters:s}),navigator:{navigate:({itemUrl:t})=>{const{pathname:a}=new URL(window.location.origin+t);i.path===a?window.location.assign(window.location.origin+t):c.go(t)}},transformItems:t=>t.map(a=>{const n=new URL(a.url);return n.pathname.startsWith("/knex")&&(n.pathname=n.pathname.replace("/knex","")),Object.assign({},a,{url:l(n.href)})}),hitComponent:({hit:t,children:a})=>{const n=t.url.startsWith("http")?l(t.url):t.url;return{type:"a",ref:void 0,constructor:void 0,key:void 0,props:{href:t.url,onClick:f=>{m(f)||i.path!==n&&(i.path!==n&&f.preventDefault(),c.go(n))},children:a},__v:null}}}))}return(e,t)=>(P(),R("div",C))}});export{L as default}; diff --git a/assets/chunks/index.d76f8212.js b/assets/chunks/index.d76f8212.js new file mode 100644 index 00000000..85ec223b --- /dev/null +++ b/assets/chunks/index.d76f8212.js @@ -0,0 +1,13 @@ +/*! @docsearch/js 3.0.0 | MIT License | © Algolia, Inc. and contributors | https://docsearch.algolia.com */function Dt(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(o){return Object.getOwnPropertyDescriptor(e,o).enumerable})),n.push.apply(n,r)}return n}function P(e){for(var t=1;t=0||(i[l]=c[l]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function Fe(e,t){return function(n){if(Array.isArray(n))return n}(e)||function(n,r){var o=n==null?null:typeof Symbol!="undefined"&&n[Symbol.iterator]||n["@@iterator"];if(o!=null){var a,c,u=[],l=!0,s=!1;try{for(o=o.call(n);!(l=(a=o.next()).done)&&(u.push(a.value),!r||u.length!==r);l=!0);}catch(i){s=!0,c=i}finally{try{l||o.return==null||o.return()}finally{if(s)throw c}}return u}}(e,t)||Ln(e,t)||function(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}()}function Be(e){return function(t){if(Array.isArray(t))return lt(t)}(e)||function(t){if(typeof Symbol!="undefined"&&t[Symbol.iterator]!=null||t["@@iterator"]!=null)return Array.from(t)}(e)||Ln(e)||function(){throw new TypeError(`Invalid attempt to spread non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}()}function Ln(e,t){if(e){if(typeof e=="string")return lt(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return n==="Object"&&e.constructor&&(n=e.constructor.name),n==="Map"||n==="Set"?Array.from(e):n==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?lt(e,t):void 0}}function lt(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n3)for(n=[n],a=3;a0?ge(p.type,p.props,p.key,null,p.__v):p)!=null){if(p.__=n,p.__b=n.__b+1,(m=_[i])===null||m&&p.key==m.key&&p.type===m.type)_[i]=void 0;else for(d=0;d3)for(n=[n],a=3;a=n.__.length&&n.__.push({}),n.__[e]}function Ut(e,t,n){var r=we(ie++,2);return r.t=e,r.__c||(r.__=[n?n(t):$n(void 0,t),function(o){var a=r.t(r.__[0],o);r.__[0]!==a&&(r.__=[a,r.__[1]],r.__c.setState({}))}],r.__c=L),r.__}function Ft(e,t){var n=we(ie++,4);!E.__s&&Et(n.__H,t)&&(n.__=e,n.__H=t,L.__h.push(n))}function Le(e,t){var n=we(ie++,7);return Et(n.__H,t)&&(n.__=e(),n.__H=t,n.__h=e),n.__}function Ir(){ft.forEach(function(e){if(e.__P)try{e.__H.__h.forEach(qe),e.__H.__h.forEach(pt),e.__H.__h=[]}catch(t){e.__H.__h=[],E.__e(t,e.__v)}}),ft=[]}E.__b=function(e){L=null,Tt&&Tt(e)},E.__r=function(e){Lt&&Lt(e),ie=0;var t=(L=e.__c).__H;t&&(t.__h.forEach(qe),t.__h.forEach(pt),t.__h=[])},E.diffed=function(e){qt&&qt(e);var t=e.__c;t&&t.__H&&t.__H.__h.length&&(ft.push(t)!==1&&Rt===E.requestAnimationFrame||((Rt=E.requestAnimationFrame)||function(n){var r,o=function(){clearTimeout(a),Bt&&cancelAnimationFrame(r),setTimeout(n)},a=setTimeout(o,100);Bt&&(r=requestAnimationFrame(o))})(Ir)),L=void 0},E.__c=function(e,t){t.some(function(n){try{n.__h.forEach(qe),n.__h=n.__h.filter(function(r){return!r.__||pt(r)})}catch(r){t.some(function(o){o.__h&&(o.__h=[])}),t=[],E.__e(r,n.__v)}}),Mt&&Mt(e,t)},E.unmount=function(e){Ht&&Ht(e);var t=e.__c;if(t&&t.__H)try{t.__H.__.forEach(qe)}catch(n){E.__e(n,t.__v)}};var Bt=typeof requestAnimationFrame=="function";function qe(e){var t=L;typeof e.__c=="function"&&e.__c(),L=t}function pt(e){var t=L;e.__c=e.__(),L=t}function Et(e,t){return!e||e.length!==t.length||t.some(function(n,r){return n!==e[r]})}function $n(e,t){return typeof t=="function"?t(e):t}function Qn(e,t){for(var n in t)e[n]=t[n];return e}function mt(e,t){for(var n in e)if(n!=="__source"&&!(n in t))return!0;for(var r in t)if(r!=="__source"&&e[r]!==t[r])return!0;return!1}function dt(e){this.props=e}(dt.prototype=new B).isPureReactComponent=!0,dt.prototype.shouldComponentUpdate=function(e,t){return mt(this.props,e)||mt(this.state,t)};var Vt=E.__b;E.__b=function(e){e.type&&e.type.__f&&e.ref&&(e.props.ref=e.ref,e.ref=null),Vt&&Vt(e)};var kr=typeof Symbol!="undefined"&&Symbol.for&&Symbol.for("react.forward_ref")||3911,zt=function(e,t){return e==null?null:W(W(e).map(t))},Dr={map:zt,forEach:zt,count:function(e){return e?W(e).length:0},only:function(e){var t=W(e);if(t.length!==1)throw"Children.only";return t[0]},toArray:W},Cr=E.__e;function Me(){this.__u=0,this.t=null,this.__b=null}function Yn(e){var t=e.__.__c;return t&&t.__e&&t.__e(e)}function ve(){this.u=null,this.o=null}E.__e=function(e,t,n){if(e.then){for(var r,o=t;o=o.__;)if((r=o.__c)&&r.__c)return t.__e==null&&(t.__e=n.__e,t.__k=n.__k),r.__c(e,t)}Cr(e,t,n)},(Me.prototype=new B).__c=function(e,t){var n=t.__c,r=this;r.t==null&&(r.t=[]),r.t.push(n);var o=Yn(r.__v),a=!1,c=function(){a||(a=!0,n.componentWillUnmount=n.__c,o?o(u):u())};n.__c=n.componentWillUnmount,n.componentWillUnmount=function(){c(),n.__c&&n.__c()};var u=function(){if(!--r.__u){if(r.state.__e){var s=r.state.__e;r.__v.__k[0]=function d(m,p,v){return m&&(m.__v=null,m.__k=m.__k&&m.__k.map(function(h){return d(h,p,v)}),m.__c&&m.__c.__P===p&&(m.__e&&v.insertBefore(m.__e,m.__d),m.__c.__e=!0,m.__c.__P=v)),m}(s,s.__c.__P,s.__c.__O)}var i;for(r.setState({__e:r.__b=null});i=r.t.pop();)i.forceUpdate()}},l=t.__h===!0;r.__u++||l||r.setState({__e:r.__b=r.__v.__k[0]}),e.then(c,c)},Me.prototype.componentWillUnmount=function(){this.t=[]},Me.prototype.render=function(e,t){if(this.__b){if(this.__v.__k){var n=document.createElement("div"),r=this.__v.__k[0].__c;this.__v.__k[0]=function a(c,u,l){return c&&(c.__c&&c.__c.__H&&(c.__c.__H.__.forEach(function(s){typeof s.__c=="function"&&s.__c()}),c.__c.__H=null),(c=Qn({},c)).__c!=null&&(c.__c.__P===l&&(c.__c.__P=u),c.__c=null),c.__k=c.__k&&c.__k.map(function(s){return a(s,u,l)})),c}(this.__b,n,r.__O=r.__P)}this.__b=null}var o=t.__e&&F(Q,null,e.fallback);return o&&(o.__h=null),[F(Q,null,t.__e?null:e.children),o]};var Wt=function(e,t,n){if(++n[1]===n[0]&&e.o.delete(t),e.props.revealOrder&&(e.props.revealOrder[0]!=="t"||!e.o.size))for(n=e.u;n;){for(;n.length>3;)n.pop()();if(n[1]>>1,1),t.i.removeChild(r)}}),Ee(F(Ar,{context:t.context},e.__v),t.l)):t.l&&t.componentWillUnmount()}function Gn(e,t){return F(xr,{__v:e,i:t})}(ve.prototype=new B).__e=function(e){var t=this,n=Yn(t.__v),r=t.o.get(e);return r[0]++,function(o){var a=function(){t.props.revealOrder?(r.push(o),Wt(t,e,r)):o()};n?n(a):a()}},ve.prototype.render=function(e){this.u=null,this.o=new Map;var t=W(e.children);e.revealOrder&&e.revealOrder[0]==="b"&&t.reverse();for(var n=t.length;n--;)this.o.set(t[n],this.u=[1,0,this.u]);return e.children},ve.prototype.componentDidUpdate=ve.prototype.componentDidMount=function(){var e=this;this.o.forEach(function(t,n){Wt(e,n,t)})};var Zn=typeof Symbol!="undefined"&&Symbol.for&&Symbol.for("react.element")||60103,Nr=/^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|fill|flood|font|glyph(?!R)|horiz|marker(?!H|W|U)|overline|paint|stop|strikethrough|stroke|text(?!L)|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/,Rr=function(e){return(typeof Symbol!="undefined"&&Oe(Symbol())=="symbol"?/fil|che|rad/i:/fil|che|ra/i).test(e)};function Xn(e,t,n){return t.__k==null&&(t.textContent=""),Ee(e,t),typeof n=="function"&&n(),e?e.__c:null}B.prototype.isReactComponent={},["componentWillMount","componentWillReceiveProps","componentWillUpdate"].forEach(function(e){Object.defineProperty(B.prototype,e,{configurable:!0,get:function(){return this["UNSAFE_"+e]},set:function(t){Object.defineProperty(this,e,{configurable:!0,writable:!0,value:t})}})});var Kt=E.event;function Tr(){}function Lr(){return this.cancelBubble}function qr(){return this.defaultPrevented}E.event=function(e){return Kt&&(e=Kt(e)),e.persist=Tr,e.isPropagationStopped=Lr,e.isDefaultPrevented=qr,e.nativeEvent=e};var er,Jt={configurable:!0,get:function(){return this.class}},$t=E.vnode;E.vnode=function(e){var t=e.type,n=e.props,r=n;if(typeof t=="string"){for(var o in r={},n){var a=n[o];o==="value"&&"defaultValue"in n&&a==null||(o==="defaultValue"&&"value"in n&&n.value==null?o="value":o==="download"&&a===!0?a="":/ondoubleclick/i.test(o)?o="ondblclick":/^onchange(textarea|input)/i.test(o+t)&&!Rr(n.type)?o="oninput":/^on(Ani|Tra|Tou|BeforeInp)/.test(o)?o=o.toLowerCase():Nr.test(o)?o=o.replace(/[A-Z0-9]/,"-$&").toLowerCase():a===null&&(a=void 0),r[o]=a)}t=="select"&&r.multiple&&Array.isArray(r.value)&&(r.value=W(n.children).forEach(function(c){c.props.selected=r.value.indexOf(c.props.value)!=-1})),t=="select"&&r.defaultValue!=null&&(r.value=W(n.children).forEach(function(c){c.props.selected=r.multiple?r.defaultValue.indexOf(c.props.value)!=-1:r.defaultValue==c.props.value})),e.props=r}t&&n.class!=n.className&&(Jt.enumerable="className"in n,n.className!=null&&(r.class=n.className),Object.defineProperty(r,"className",Jt)),e.$$typeof=Zn,$t&&$t(e)};var Qt=E.__r;E.__r=function(e){Qt&&Qt(e),er=e.__c};var Mr={ReactCurrentDispatcher:{current:{readContext:function(e){return er.__n[e.__c].props.value}}}};(typeof performance=="undefined"?"undefined":Oe(performance))=="object"&&typeof performance.now=="function"&&performance.now.bind(performance);function Yt(e){return!!e&&e.$$typeof===Zn}var f={useState:function(e){return oe=1,Ut($n,e)},useReducer:Ut,useEffect:function(e,t){var n=we(ie++,3);!E.__s&&Et(n.__H,t)&&(n.__=e,n.__H=t,L.__H.__h.push(n))},useLayoutEffect:Ft,useRef:function(e){return oe=5,Le(function(){return{current:e}},[])},useImperativeHandle:function(e,t,n){oe=6,Ft(function(){typeof e=="function"?e(t()):e&&(e.current=t())},n==null?n:n.concat(e))},useMemo:Le,useCallback:function(e,t){return oe=8,Le(function(){return e},t)},useContext:function(e){var t=L.context[e.__c],n=we(ie++,9);return n.__c=e,t?(n.__==null&&(n.__=!0,t.sub(L)),t.props.value):e.__},useDebugValue:function(e,t){E.useDebugValue&&E.useDebugValue(t?t(e):e)},version:"16.8.0",Children:Dr,render:Xn,hydrate:function(e,t,n){return Jn(e,t),typeof n=="function"&&n(),e?e.__c:null},unmountComponentAtNode:function(e){return!!e.__k&&(Ee(null,e),!0)},createPortal:Gn,createElement:F,createContext:function(e,t){var n={__c:t="__cC"+Mn++,__:e,Consumer:function(r,o){return r.children(o)},Provider:function(r){var o,a;return this.getChildContext||(o=[],(a={})[t]=this,this.getChildContext=function(){return a},this.shouldComponentUpdate=function(c){this.props.value!==c.value&&o.some(st)},this.sub=function(c){o.push(c);var u=c.componentWillUnmount;c.componentWillUnmount=function(){o.splice(o.indexOf(c),1),u&&u.call(c)}}),r.children}};return n.Provider.__=n.Consumer.contextType=n},createFactory:function(e){return F.bind(null,e)},cloneElement:function(e){return Yt(e)?Pr.apply(null,arguments):e},createRef:function(){return{current:null}},Fragment:Q,isValidElement:Yt,findDOMNode:function(e){return e&&(e.base||e.nodeType===1&&e)||null},Component:B,PureComponent:dt,memo:function(e,t){function n(o){var a=this.props.ref,c=a==o.ref;return!c&&a&&(a.call?a(null):a.current=null),t?!t(this.props,o)||!c:mt(this.props,o)}function r(o){return this.shouldComponentUpdate=n,F(e,o)}return r.displayName="Memo("+(e.displayName||e.name)+")",r.prototype.isReactComponent=!0,r.__f=!0,r},forwardRef:function(e){function t(n,r){var o=Qn({},n);return delete o.ref,e(o,(r=n.ref||r)&&(Oe(r)!="object"||"current"in r)?r:null)}return t.$$typeof=kr,t.render=t,t.prototype.isReactComponent=t.__f=!0,t.displayName="ForwardRef("+(e.displayName||e.name)+")",t},unstable_batchedUpdates:function(e,t){return e(t)},StrictMode:Q,Suspense:Me,SuspenseList:ve,lazy:function(e){var t,n,r;function o(a){if(t||(t=e()).then(function(c){n=c.default||c},function(c){r=c}),r)throw r;if(!n)throw t;return F(n,a)}return o.displayName="Lazy",o.__f=!0,o},__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:Mr};function Hr(){return f.createElement("svg",{width:"15",height:"15",className:"DocSearch-Control-Key-Icon"},f.createElement("path",{d:"M4.505 4.496h2M5.505 5.496v5M8.216 4.496l.055 5.993M10 7.5c.333.333.5.667.5 1v2M12.326 4.5v5.996M8.384 4.496c1.674 0 2.116 0 2.116 1.5s-.442 1.5-2.116 1.5M3.205 9.303c-.09.448-.277 1.21-1.241 1.203C1 10.5.5 9.513.5 8V7c0-1.57.5-2.5 1.464-2.494.964.006 1.134.598 1.24 1.342M12.553 10.5h1.953",strokeWidth:"1.2",stroke:"currentColor",fill:"none",strokeLinecap:"square"}))}function tr(){return f.createElement("svg",{width:"20",height:"20",className:"DocSearch-Search-Icon",viewBox:"0 0 20 20"},f.createElement("path",{d:"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z",stroke:"currentColor",fill:"none",fillRule:"evenodd",strokeLinecap:"round",strokeLinejoin:"round"}))}var Ur=["translations"];function ht(){return ht=Object.assign||function(e){for(var t=1;t=0||(i[l]=c[l]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var Br=f.forwardRef(function(e,t){var n=e.translations,r=n===void 0?{}:n,o=Fr(e,Ur),a=r.buttonText,c=a===void 0?"Search":a,u=r.buttonAriaLabel,l=u===void 0?"Search":u,s=Le(function(){return typeof navigator!="undefined"?/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)?"\u2318":"Ctrl":null},[]);return f.createElement("button",ht({type:"button",className:"DocSearch DocSearch-Button","aria-label":l},o,{ref:t}),f.createElement("span",{className:"DocSearch-Button-Container"},f.createElement(tr,null),f.createElement("span",{className:"DocSearch-Button-Placeholder"},c)),f.createElement("span",{className:"DocSearch-Button-Keys"},s!==null&&f.createElement(f.Fragment,null,f.createElement("span",{className:"DocSearch-Button-Key"},s==="Ctrl"?f.createElement(Hr,null):s),f.createElement("span",{className:"DocSearch-Button-Key"},"K"))))});function je(e){return e.reduce(function(t,n){return t.concat(n)},[])}var Vr=0;function vt(e){return e.collections.length===0?0:e.collections.reduce(function(t,n){return t+n.items.length},0)}var zr=function(){},Wr=[{segment:"autocomplete-core",version:"1.5.2"}];function He(e,t){var n=t;return{then:function(r,o){return He(e.then(De(r,n,e),De(o,n,e)),n)},catch:function(r){return He(e.catch(De(r,n,e)),n)},finally:function(r){return r&&n.onCancelList.push(r),He(e.finally(De(r&&function(){return n.onCancelList=[],r()},n,e)),n)},cancel:function(){n.isCanceled=!0;var r=n.onCancelList;n.onCancelList=[],r.forEach(function(o){o()})},isCanceled:function(){return n.isCanceled===!0}}}function Gt(e){return He(e,{isCanceled:!1,onCancelList:[]})}function De(e,t,n){return e?function(r){return t.isCanceled?r:e(r)}:n}function Zt(e,t,n,r){if(!n)return null;if(e<0&&(t===null||r!==null&&t===0))return n+e;var o=(t===null?-1:t)+e;return o<=-1||o>=n?r===null?null:0:o}function Xt(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(o){return Object.getOwnPropertyDescriptor(e,o).enumerable})),n.push.apply(n,r)}return n}function Kr(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function Jr(e,t){var n=[];return Promise.resolve(e(t)).then(function(r){return Promise.all(r.filter(function(o){return Boolean(o)}).map(function(o){if(o.sourceId,n.includes(o.sourceId))throw new Error("[Autocomplete] The `sourceId` ".concat(JSON.stringify(o.sourceId)," is not unique."));n.push(o.sourceId);var a=function(c){for(var u=1;ue.length)&&(t=e.length);for(var n=0,r=new Array(t);ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=0||(i[l]=c[l]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var ln,ot,Re,de=null,sn=(ln=-1,ot=-1,Re=void 0,function(e){var t=++ln;return Promise.resolve(e).then(function(n){return Re&&t=0||(i[l]=c[l]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var fo=["props","refresh","store"],po=["inputElement","formElement","panelElement"],mo=["inputElement"],ho=["inputElement","maxLength"],vo=["item","source"];function pn(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(o){return Object.getOwnPropertyDescriptor(e,o).enumerable})),n.push.apply(n,r)}return n}function T(e){for(var t=1;t=0||(i[l]=c[l]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function _o(e){var t=e.props,n=e.refresh,r=e.store,o=he(e,fo);return{getEnvironmentProps:function(a){var c=a.inputElement,u=a.formElement,l=a.panelElement;return T({onTouchStart:function(s){!r.getState().isOpen&&r.pendingRequests.isEmpty()||s.target===c||[u,l].some(function(i){return d=i,m=s.target,d===m||d.contains(m);var d,m})===!1&&(r.dispatch("blur",null),t.debug||r.pendingRequests.cancelAll())},onTouchMove:function(s){r.getState().isOpen!==!1&&c===t.environment.document.activeElement&&s.target!==c&&c.blur()}},he(a,po))},getRootProps:function(a){return T({role:"combobox","aria-expanded":r.getState().isOpen,"aria-haspopup":"listbox","aria-owns":r.getState().isOpen?"".concat(t.id,"-list"):void 0,"aria-labelledby":"".concat(t.id,"-label")},a)},getFormProps:function(a){return a.inputElement,T({action:"",noValidate:!0,role:"search",onSubmit:function(c){var u;c.preventDefault(),t.onSubmit(T({event:c,refresh:n,state:r.getState()},o)),r.dispatch("submit",null),(u=a.inputElement)===null||u===void 0||u.blur()},onReset:function(c){var u;c.preventDefault(),t.onReset(T({event:c,refresh:n,state:r.getState()},o)),r.dispatch("reset",null),(u=a.inputElement)===null||u===void 0||u.focus()}},he(a,mo))},getLabelProps:function(a){return T({htmlFor:"".concat(t.id,"-input"),id:"".concat(t.id,"-label")},a)},getInputProps:function(a){function c(p){(t.openOnFocus||Boolean(r.getState().query))&&re(T({event:p,props:t,query:r.getState().completion||r.getState().query,refresh:n,store:r},o)),r.dispatch("focus",null)}var u="ontouchstart"in t.environment,l=a||{},s=(l.inputElement,l.maxLength),i=s===void 0?512:s,d=he(l,ho),m=ae(r.getState());return T({"aria-autocomplete":"both","aria-activedescendant":r.getState().isOpen&&r.getState().activeItemId!==null?"".concat(t.id,"-item-").concat(r.getState().activeItemId):void 0,"aria-controls":r.getState().isOpen?"".concat(t.id,"-list"):void 0,"aria-labelledby":"".concat(t.id,"-label"),value:r.getState().completion||r.getState().query,id:"".concat(t.id,"-input"),autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",enterKeyHint:m!=null&&m.itemUrl?"go":"search",spellCheck:"false",autoFocus:t.autoFocus,placeholder:t.placeholder,maxLength:i,type:"search",onChange:function(p){re(T({event:p,props:t,query:p.currentTarget.value.slice(0,i),refresh:n,store:r},o))},onKeyDown:function(p){(function(v){var h=v.event,y=v.props,_=v.refresh,g=v.store,b=so(v,uo);if(h.key==="ArrowUp"||h.key==="ArrowDown"){var O=function(){var R=y.environment.document.getElementById("".concat(y.id,"-item-").concat(g.getState().activeItemId));R&&(R.scrollIntoViewIfNeeded?R.scrollIntoViewIfNeeded(!1):R.scrollIntoView(!1))},S=function(){var R=ae(g.getState());if(g.getState().activeItemId!==null&&R){var Ge=R.item,Ze=R.itemInputValue,Pe=R.itemUrl,K=R.source;K.onActive(G({event:h,item:Ge,itemInputValue:Ze,itemUrl:Pe,refresh:_,source:K,state:g.getState()},b))}};h.preventDefault(),g.getState().isOpen===!1&&(y.openOnFocus||Boolean(g.getState().query))?re(G({event:h,props:y,query:g.getState().query,refresh:_,store:g},b)).then(function(){g.dispatch(h.key,{nextActiveItemId:y.defaultActiveItemId}),S(),setTimeout(O,0)}):(g.dispatch(h.key,{}),S(),O())}else if(h.key==="Escape")h.preventDefault(),g.dispatch(h.key,null),g.pendingRequests.cancelAll();else if(h.key==="Enter"){if(g.getState().activeItemId===null||g.getState().collections.every(function(R){return R.items.length===0}))return;h.preventDefault();var I=ae(g.getState()),C=I.item,x=I.itemInputValue,k=I.itemUrl,N=I.source;if(h.metaKey||h.ctrlKey)k!==void 0&&(N.onSelect(G({event:h,item:C,itemInputValue:x,itemUrl:k,refresh:_,source:N,state:g.getState()},b)),y.navigator.navigateNewTab({itemUrl:k,item:C,state:g.getState()}));else if(h.shiftKey)k!==void 0&&(N.onSelect(G({event:h,item:C,itemInputValue:x,itemUrl:k,refresh:_,source:N,state:g.getState()},b)),y.navigator.navigateNewWindow({itemUrl:k,item:C,state:g.getState()}));else if(!h.altKey){if(k!==void 0)return N.onSelect(G({event:h,item:C,itemInputValue:x,itemUrl:k,refresh:_,source:N,state:g.getState()},b)),void y.navigator.navigate({itemUrl:k,item:C,state:g.getState()});re(G({event:h,nextState:{isOpen:!1},props:y,query:x,refresh:_,store:g},b)).then(function(){N.onSelect(G({event:h,item:C,itemInputValue:x,itemUrl:k,refresh:_,source:N,state:g.getState()},b))})}}})(T({event:p,props:t,refresh:n,store:r},o))},onFocus:c,onBlur:function(){u||(r.dispatch("blur",null),t.debug||r.pendingRequests.cancelAll())},onClick:function(p){a.inputElement!==t.environment.document.activeElement||r.getState().isOpen||c(p)}},d)},getPanelProps:function(a){return T({onMouseDown:function(c){c.preventDefault()},onMouseLeave:function(){r.dispatch("mouseleave",null)}},a)},getListProps:function(a){return T({role:"listbox","aria-labelledby":"".concat(t.id,"-label"),id:"".concat(t.id,"-list")},a)},getItemProps:function(a){var c=a.item,u=a.source,l=he(a,vo);return T({id:"".concat(t.id,"-item-").concat(c.__autocomplete_id),role:"option","aria-selected":r.getState().activeItemId===c.__autocomplete_id,onMouseMove:function(s){if(c.__autocomplete_id!==r.getState().activeItemId){r.dispatch("mousemove",c.__autocomplete_id);var i=ae(r.getState());if(r.getState().activeItemId!==null&&i){var d=i.item,m=i.itemInputValue,p=i.itemUrl,v=i.source;v.onActive(T({event:s,item:d,itemInputValue:m,itemUrl:p,refresh:n,source:v,state:r.getState()},o))}}},onMouseDown:function(s){s.preventDefault()},onClick:function(s){var i=u.getItemInputValue({item:c,state:r.getState()}),d=u.getItemUrl({item:c,state:r.getState()});(d?Promise.resolve():re(T({event:s,nextState:{isOpen:!1},props:t,query:i,refresh:n,store:r},o))).then(function(){u.onSelect(T({event:s,item:c,itemInputValue:i,itemUrl:d,refresh:n,source:u,state:r.getState()},o))})}},l)}}}function mn(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(o){return Object.getOwnPropertyDescriptor(e,o).enumerable})),n.push.apply(n,r)}return n}function go(e){for(var t=1;t0},reshape:function(m){return m.sources}},u),{},{id:(s=u.id)!==null&&s!==void 0?s:"autocomplete-".concat(Vr++),plugins:d,initialState:te({activeItemId:null,query:"",completion:null,collections:[],isOpen:!1,status:"idle",context:{}},u.initialState),onStateChange:function(m){var p;(p=u.onStateChange)===null||p===void 0||p.call(u,m),d.forEach(function(v){var h;return(h=v.onStateChange)===null||h===void 0?void 0:h.call(v,m)})},onSubmit:function(m){var p;(p=u.onSubmit)===null||p===void 0||p.call(u,m),d.forEach(function(v){var h;return(h=v.onSubmit)===null||h===void 0?void 0:h.call(v,m)})},onReset:function(m){var p;(p=u.onReset)===null||p===void 0||p.call(u,m),d.forEach(function(v){var h;return(h=v.onReset)===null||h===void 0?void 0:h.call(v,m)})},getSources:function(m){return Promise.all([].concat(Zr(d.map(function(p){return p.getSources})),[u.getSources]).filter(Boolean).map(function(p){return Jr(p,m)})).then(function(p){return je(p)}).then(function(p){return p.map(function(v){return te(te({},v),{},{onSelect:function(h){v.onSelect(h),l.forEach(function(y){var _;return(_=y.onSelect)===null||_===void 0?void 0:_.call(y,h)})},onActive:function(h){v.onActive(h),l.forEach(function(y){var _;return(_=y.onActive)===null||_===void 0?void 0:_.call(y,h)})}})})})},navigator:te({navigate:function(m){var p=m.itemUrl;i.location.assign(p)},navigateNewTab:function(m){var p=m.itemUrl,v=i.open(p,"_blank","noopener");v==null||v.focus()},navigateNewWindow:function(m){var p=m.itemUrl;i.open(p,"_blank","noopener")}},u.navigator)})}(e,t),r=Yr(So,n,function(u){var l=u.prevState,s=u.state;n.onStateChange(Z({prevState:l,state:s,refresh:c},o))}),o=function(u){var l=u.store;return{setActiveItemId:function(s){l.dispatch("setActiveItemId",s)},setQuery:function(s){l.dispatch("setQuery",s)},setCollections:function(s){var i=0,d=s.map(function(m){return Ae(Ae({},m),{},{items:je(m.items).map(function(p){return Ae(Ae({},p),{},{__autocomplete_id:i++})})})});l.dispatch("setCollections",d)},setIsOpen:function(s){l.dispatch("setIsOpen",s)},setStatus:function(s){l.dispatch("setStatus",s)},setContext:function(s){l.dispatch("setContext",s)}}}({store:r}),a=_o(Z({props:n,refresh:c,store:r},o));function c(){return re(Z({event:new Event("input"),nextState:{isOpen:r.getState().isOpen},props:n,query:r.getState().query,refresh:c,store:r},o))}return n.plugins.forEach(function(u){var l;return(l=u.subscribe)===null||l===void 0?void 0:l.call(u,Z(Z({},o),{},{refresh:c,onSelect:function(s){t.push({onSelect:s})},onActive:function(s){t.push({onActive:s})}}))}),function(u){var l,s=u.metadata,i=u.environment;if(!((l=i.navigator)===null||l===void 0)&&l.userAgent.includes("Algolia Crawler")){var d=i.document.createElement("meta"),m=i.document.querySelector("head");d.name="algolia:metadata",setTimeout(function(){d.content=JSON.stringify(s),m.appendChild(d)},0)}}({metadata:bo({plugins:n.plugins,options:e}),environment:n.environment}),Z(Z({refresh:c},a),o)}function jo(e){var t=e.translations,n=(t===void 0?{}:t).searchByText,r=n===void 0?"Search by":n;return f.createElement("a",{href:"https://www.algolia.com/ref/docsearch/?utm_source=".concat(window.location.hostname,"&utm_medium=referral&utm_content=powered_by&utm_campaign=docsearch"),target:"_blank",rel:"noopener noreferrer"},f.createElement("span",{className:"DocSearch-Label"},r),f.createElement("svg",{width:"77",height:"19","aria-label":"Algolia",role:"img"},f.createElement("path",{d:"M2.5067 0h14.0245c1.384.001 2.5058 1.1205 2.5068 2.5017V16.5c-.0014 1.3808-1.1232 2.4995-2.5068 2.5H2.5067C1.1232 18.9995.0014 17.8808 0 16.5V2.4958A2.495 2.495 0 01.735.7294 2.505 2.505 0 012.5068 0zM37.95 15.0695c-3.7068.0168-3.7068-2.986-3.7068-3.4634L34.2372.3576 36.498 0v11.1794c0 .2715 0 1.9889 1.452 1.994v1.8961zm-9.1666-1.8388c.694 0 1.2086-.0397 1.5678-.1088v-2.2934a5.3639 5.3639 0 00-1.3303-.1679 4.8283 4.8283 0 00-.758.0582 2.2845 2.2845 0 00-.688.2024c-.2029.0979-.371.2362-.4919.4142-.1268.1788-.185.2826-.185.5533 0 .5297.185.8359.5205 1.0375.3355.2016.7928.3053 1.365.3053v-.0008zm-.1969-8.1817c.7463 0 1.3768.092 1.8856.2767.5088.1838.9195.4428 1.2204.7717.3068.334.5147.7777.6423 1.251.1327.4723.196.991.196 1.5603v5.798c-.5235.1036-1.05.192-1.5787.2649-.7048.1037-1.4976.156-2.3774.156-.5832 0-1.1215-.0582-1.6016-.167a3.385 3.385 0 01-1.2432-.5364 2.6034 2.6034 0 01-.8037-.9565c-.191-.3922-.29-.9447-.29-1.5208 0-.5533.11-.905.3246-1.2863a2.7351 2.7351 0 01.8849-.9329c.376-.242.8029-.415 1.2948-.5187a7.4517 7.4517 0 011.5381-.156 7.1162 7.1162 0 011.6667.2024V8.886c0-.259-.0296-.5061-.093-.7372a1.5847 1.5847 0 00-.3245-.6158 1.5079 1.5079 0 00-.6119-.4158 2.6788 2.6788 0 00-.966-.173c-.5206 0-.9948.0634-1.4283.1384a6.5481 6.5481 0 00-1.065.259l-.2712-1.849c.2831-.0986.7048-.1964 1.2491-.2943a9.2979 9.2979 0 011.752-.1501v.0008zm44.6597 8.1193c.6947 0 1.2086-.0405 1.567-.1097v-2.2942a5.3743 5.3743 0 00-1.3303-.1679c-.2485 0-.503.0177-.7573.0582a2.2853 2.2853 0 00-.688.2024 1.2333 1.2333 0 00-.4918.4142c-.1268.1788-.1843.2826-.1843.5533 0 .5297.1843.8359.5198 1.0375.3414.2066.7927.3053 1.365.3053v.0009zm-.191-8.1767c.7463 0 1.3768.0912 1.8856.2759.5087.1847.9195.4436 1.2204.7717.3.329.5147.7786.6414 1.251a5.7248 5.7248 0 01.197 1.562v5.7972c-.3466.0742-.874.1602-1.5788.2648-.7049.1038-1.4976.1552-2.3774.1552-.5832 0-1.1215-.0573-1.6016-.167a3.385 3.385 0 01-1.2432-.5356 2.6034 2.6034 0 01-.8038-.9565c-.191-.3922-.2898-.9447-.2898-1.5216 0-.5533.1098-.905.3245-1.2854a2.7373 2.7373 0 01.8849-.9338c.376-.2412.8029-.4141 1.2947-.5178a7.4545 7.4545 0 012.325-.1097c.2781.0287.5672.081.879.156v-.3686a2.7781 2.7781 0 00-.092-.738 1.5788 1.5788 0 00-.3246-.6166 1.5079 1.5079 0 00-.612-.415 2.6797 2.6797 0 00-.966-.1729c-.5205 0-.9947.0633-1.4282.1384a6.5608 6.5608 0 00-1.065.259l-.2712-1.8498c.283-.0979.7048-.1957 1.2491-.2935a9.8597 9.8597 0 011.752-.1494zm-6.79-1.072c-.7576.001-1.373-.6103-1.3759-1.3664 0-.755.6128-1.3664 1.376-1.3664.764 0 1.3775.6115 1.3775 1.3664s-.6195 1.3664-1.3776 1.3664zm1.1393 11.1507h-2.2726V5.3409l2.2734-.3568v10.0845l-.0008.0017zm-3.984 0c-3.707.0168-3.707-2.986-3.707-3.4642L59.7069.3576 61.9685 0v11.1794c0 .2715 0 1.9889 1.452 1.994V15.0703zm-7.3512-4.979c0-.975-.2138-1.7873-.6305-2.3516-.4167-.571-.9998-.852-1.747-.852-.7454 0-1.3302.281-1.7452.852-.4166.5702-.6195 1.3765-.6195 2.3516 0 .9851.208 1.6473.6254 2.2183.4158.576.9998.8587 1.7461.8587.7454 0 1.3303-.2885 1.747-.8595.4158-.5761.6237-1.2315.6237-2.2184v.0009zm2.3132-.006c0 .7609-.1099 1.3361-.3356 1.9654a4.654 4.654 0 01-.9533 1.6076A4.214 4.214 0 0155.613 14.69c-.579.2412-1.4697.3795-1.9143.3795-.4462-.005-1.3303-.1324-1.9033-.3795a4.307 4.307 0 01-1.474-1.0316c-.4115-.4445-.7293-.9801-.9609-1.6076a5.3423 5.3423 0 01-.3465-1.9653c0-.7608.104-1.493.3356-2.1155a4.683 4.683 0 01.9719-1.5958 4.3383 4.3383 0 011.479-1.0257c.5739-.242 1.2043-.3567 1.8864-.3567.6829 0 1.3125.1197 1.8906.3567a4.1245 4.1245 0 011.4816 1.0257 4.7587 4.7587 0 01.9592 1.5958c.2426.6225.3643 1.3547.3643 2.1155zm-17.0198 0c0 .9448.208 1.9932.6238 2.431.4166.4386.955.6579 1.6142.6579.3584 0 .6998-.0523 1.0176-.1502.3186-.0978.5721-.2134.775-.3517V7.0784a8.8706 8.8706 0 00-1.4926-.1906c-.8206-.0236-1.4452.312-1.8847.8468-.4335.5365-.6533 1.476-.6533 2.3516v-.0008zm6.2863 4.4485c0 1.5385-.3938 2.662-1.1866 3.3773-.791.7136-2.0005 1.0712-3.6308 1.0712-.5958 0-1.834-.1156-2.8228-.334l.3643-1.7865c.8282.173 1.9202.2193 2.4932.2193.9077 0 1.555-.1847 1.943-.5533.388-.3686.578-.916.578-1.643v-.3687a6.8289 6.8289 0 01-.8848.3349c-.3634.1096-.786.167-1.261.167-.6246 0-1.1917-.0979-1.7055-.2944a3.5554 3.5554 0 01-1.3244-.8645c-.3642-.3796-.6541-.8579-.8561-1.4289-.2028-.571-.3068-1.59-.3068-2.339 0-.7034.1099-1.5856.3245-2.1735.2198-.5871.5316-1.0949.9542-1.515.4167-.42.9255-.743 1.5213-.98a5.5923 5.5923 0 012.052-.3855c.7353 0 1.4114.092 2.0707.2024.6592.1088 1.2204.2236 1.6776.35v8.945-.0008zM11.5026 4.2418v-.6511c-.0005-.4553-.3704-.8241-.8266-.8241H8.749c-.4561 0-.826.3688-.8265.824v.669c0 .0742.0693.1264.1445.1096a6.0346 6.0346 0 011.6768-.2362 6.125 6.125 0 011.6202.2185.1116.1116 0 00.1386-.1097zm-5.2806.852l-.3296-.3282a.8266.8266 0 00-1.168 0l-.393.3922a.8199.8199 0 000 1.164l.3237.323c.0524.0515.1268.0397.1733-.0117.191-.259.3989-.507.6305-.7372.2374-.2362.48-.4437.7462-.6335.0575-.0354.0634-.1155.017-.1687zm3.5159 2.069v2.818c0 .081.0879.1392.1622.0987l2.5102-1.2964c.0574-.0287.0752-.0987.0464-.1552a3.1237 3.1237 0 00-2.603-1.574c-.0575 0-.115.0456-.115.1097l-.0008-.0009zm.0008 6.789c-2.0933.0005-3.7915-1.6912-3.7947-3.7804C5.9468 8.0821 7.6452 6.39 9.7387 6.391c2.0932-.0005 3.7911 1.6914 3.794 3.7804a3.7783 3.7783 0 01-1.1124 2.675 3.7936 3.7936 0 01-2.6824 1.1054h.0008zM9.738 4.8002c-1.9218 0-3.6975 1.0232-4.6584 2.6841a5.359 5.359 0 000 5.3683c.9609 1.661 2.7366 2.6841 4.6584 2.6841a5.3891 5.3891 0 003.8073-1.5725 5.3675 5.3675 0 001.578-3.7987 5.3574 5.3574 0 00-1.5771-3.797A5.379 5.379 0 009.7387 4.801l-.0008-.0008z",fill:"currentColor",fillRule:"evenodd"})))}function Te(e){return f.createElement("svg",{width:"15",height:"15","aria-label":e.ariaLabel,role:"img"},f.createElement("g",{fill:"none",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"1.2"},e.children))}function Po(e){var t=e.translations,n=t===void 0?{}:t,r=n.selectText,o=r===void 0?"to select":r,a=n.selectKeyAriaLabel,c=a===void 0?"Enter key":a,u=n.navigateText,l=u===void 0?"to navigate":u,s=n.navigateUpKeyAriaLabel,i=s===void 0?"Arrow up":s,d=n.navigateDownKeyAriaLabel,m=d===void 0?"Arrow down":d,p=n.closeText,v=p===void 0?"to close":p,h=n.closeKeyAriaLabel,y=h===void 0?"Escape key":h,_=n.searchByText,g=_===void 0?"Search by":_;return f.createElement(f.Fragment,null,f.createElement("div",{className:"DocSearch-Logo"},f.createElement(jo,{translations:{searchByText:g}})),f.createElement("ul",{className:"DocSearch-Commands"},f.createElement("li",null,f.createElement("span",{className:"DocSearch-Commands-Key"},f.createElement(Te,{ariaLabel:c},f.createElement("path",{d:"M12 3.53088v3c0 1-1 2-2 2H4M7 11.53088l-3-3 3-3"}))),f.createElement("span",{className:"DocSearch-Label"},o)),f.createElement("li",null,f.createElement("span",{className:"DocSearch-Commands-Key"},f.createElement(Te,{ariaLabel:m},f.createElement("path",{d:"M7.5 3.5v8M10.5 8.5l-3 3-3-3"}))),f.createElement("span",{className:"DocSearch-Commands-Key"},f.createElement(Te,{ariaLabel:i},f.createElement("path",{d:"M7.5 11.5v-8M10.5 6.5l-3-3-3 3"}))),f.createElement("span",{className:"DocSearch-Label"},l)),f.createElement("li",null,f.createElement("span",{className:"DocSearch-Commands-Key"},f.createElement(Te,{ariaLabel:y},f.createElement("path",{d:"M13.6167 8.936c-.1065.3583-.6883.962-1.4875.962-.7993 0-1.653-.9165-1.653-2.1258v-.5678c0-1.2548.7896-2.1016 1.653-2.1016.8634 0 1.3601.4778 1.4875 1.0724M9 6c-.1352-.4735-.7506-.9219-1.46-.8972-.7092.0246-1.344.57-1.344 1.2166s.4198.8812 1.3445.9805C8.465 7.3992 8.968 7.9337 9 8.5c.032.5663-.454 1.398-1.4595 1.398C6.6593 9.898 6 9 5.963 8.4851m-1.4748.5368c-.2635.5941-.8099.876-1.5443.876s-1.7073-.6248-1.7073-2.204v-.4603c0-1.0416.721-2.131 1.7073-2.131.9864 0 1.6425 1.031 1.5443 2.2492h-2.956"}))),f.createElement("span",{className:"DocSearch-Label"},v))))}function Io(e){var t=e.hit,n=e.children;return f.createElement("a",{href:t.url},n)}function ko(){return f.createElement("svg",{viewBox:"0 0 38 38",stroke:"currentColor",strokeOpacity:".5"},f.createElement("g",{fill:"none",fillRule:"evenodd"},f.createElement("g",{transform:"translate(1 1)",strokeWidth:"2"},f.createElement("circle",{strokeOpacity:".3",cx:"18",cy:"18",r:"18"}),f.createElement("path",{d:"M36 18c0-9.94-8.06-18-18-18"},f.createElement("animateTransform",{attributeName:"transform",type:"rotate",from:"0 18 18",to:"360 18 18",dur:"1s",repeatCount:"indefinite"})))))}function Do(){return f.createElement("svg",{width:"20",height:"20",viewBox:"0 0 20 20"},f.createElement("g",{stroke:"currentColor",fill:"none",fillRule:"evenodd",strokeLinecap:"round",strokeLinejoin:"round"},f.createElement("path",{d:"M3.18 6.6a8.23 8.23 0 1112.93 9.94h0a8.23 8.23 0 01-11.63 0"}),f.createElement("path",{d:"M6.44 7.25H2.55V3.36M10.45 6v5.6M10.45 11.6L13 13"})))}function yt(){return f.createElement("svg",{width:"20",height:"20",viewBox:"0 0 20 20"},f.createElement("path",{d:"M10 10l5.09-5.09L10 10l5.09 5.09L10 10zm0 0L4.91 4.91 10 10l-5.09 5.09L10 10z",stroke:"currentColor",fill:"none",fillRule:"evenodd",strokeLinecap:"round",strokeLinejoin:"round"}))}function Co(){return f.createElement("svg",{className:"DocSearch-Hit-Select-Icon",width:"20",height:"20",viewBox:"0 0 20 20"},f.createElement("g",{stroke:"currentColor",fill:"none",fillRule:"evenodd",strokeLinecap:"round",strokeLinejoin:"round"},f.createElement("path",{d:"M18 3v4c0 2-2 4-4 4H2"}),f.createElement("path",{d:"M8 17l-6-6 6-6"})))}var Ao=function(){return f.createElement("svg",{width:"20",height:"20",viewBox:"0 0 20 20"},f.createElement("path",{d:"M17 6v12c0 .52-.2 1-1 1H4c-.7 0-1-.33-1-1V2c0-.55.42-1 1-1h8l5 5zM14 8h-3.13c-.51 0-.87-.34-.87-.87V4",stroke:"currentColor",fill:"none",fillRule:"evenodd",strokeLinejoin:"round"}))};function xo(e){switch(e.type){case"lvl1":return f.createElement(Ao,null);case"content":return f.createElement(Ro,null);default:return f.createElement(No,null)}}function No(){return f.createElement("svg",{width:"20",height:"20",viewBox:"0 0 20 20"},f.createElement("path",{d:"M13 13h4-4V8H7v5h6v4-4H7V8H3h4V3v5h6V3v5h4-4v5zm-6 0v4-4H3h4z",stroke:"currentColor",fill:"none",fillRule:"evenodd",strokeLinecap:"round",strokeLinejoin:"round"}))}function Ro(){return f.createElement("svg",{width:"20",height:"20",viewBox:"0 0 20 20"},f.createElement("path",{d:"M17 5H3h14zm0 5H3h14zm0 5H3h14z",stroke:"currentColor",fill:"none",fillRule:"evenodd",strokeLinejoin:"round"}))}function yn(){return f.createElement("svg",{width:"20",height:"20",viewBox:"0 0 20 20"},f.createElement("path",{d:"M10 14.2L5 17l1-5.6-4-4 5.5-.7 2.5-5 2.5 5 5.6.8-4 4 .9 5.5z",stroke:"currentColor",fill:"none",fillRule:"evenodd",strokeLinejoin:"round"}))}function To(){return f.createElement("svg",{width:"40",height:"40",viewBox:"0 0 20 20",fill:"none",fillRule:"evenodd",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round"},f.createElement("path",{d:"M19 4.8a16 16 0 00-2-1.2m-3.3-1.2A16 16 0 001.1 4.7M16.7 8a12 12 0 00-2.8-1.4M10 6a12 12 0 00-6.7 2M12.3 14.7a4 4 0 00-4.5 0M14.5 11.4A8 8 0 0010 10M3 16L18 2M10 18h0"}))}function Lo(){return f.createElement("svg",{width:"40",height:"40",viewBox:"0 0 20 20",fill:"none",fillRule:"evenodd",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round"},f.createElement("path",{d:"M15.5 4.8c2 3 1.7 7-1 9.7h0l4.3 4.3-4.3-4.3a7.8 7.8 0 01-9.8 1m-2.2-2.2A7.8 7.8 0 0113.2 2.4M2 18L18 2"}))}function qo(e){var t=e.translations,n=t===void 0?{}:t,r=n.titleText,o=r===void 0?"Unable to fetch results":r,a=n.helpText,c=a===void 0?"You might want to check your network connection.":a;return f.createElement("div",{className:"DocSearch-ErrorScreen"},f.createElement("div",{className:"DocSearch-Screen-Icon"},f.createElement(To,null)),f.createElement("p",{className:"DocSearch-Title"},o),f.createElement("p",{className:"DocSearch-Help"},c))}var Mo=["translations"];function Ho(e){return function(t){if(Array.isArray(t))return at(t)}(e)||function(t){if(typeof Symbol!="undefined"&&t[Symbol.iterator]!=null||t["@@iterator"]!=null)return Array.from(t)}(e)||function(t,n){if(!!t){if(typeof t=="string")return at(t,n);var r=Object.prototype.toString.call(t).slice(8,-1);if(r==="Object"&&t.constructor&&(r=t.constructor.name),r==="Map"||r==="Set")return Array.from(t);if(r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return at(t,n)}}(e)||function(){throw new TypeError(`Invalid attempt to spread non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}()}function at(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=0||(i[l]=c[l]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function Fo(e){var t=e.translations,n=t===void 0?{}:t,r=Uo(e,Mo),o=n.noResultsText,a=o===void 0?"No results for":o,c=n.suggestedQueryText,u=c===void 0?"Try searching for":c,l=n.reportMissingResultsText,s=l===void 0?"Believe this query should return results?":l,i=n.reportMissingResultsLinkText,d=i===void 0?"Let us know.":i,m=r.state.context.searchSuggestions;return f.createElement("div",{className:"DocSearch-NoResults"},f.createElement("div",{className:"DocSearch-Screen-Icon"},f.createElement(Lo,null)),f.createElement("p",{className:"DocSearch-Title"},a,' "',f.createElement("strong",null,r.state.query),'"'),m&&m.length>0&&f.createElement("div",{className:"DocSearch-NoResults-Prefill-List"},f.createElement("p",{className:"DocSearch-Help"},u,":"),f.createElement("ul",null,m.slice(0,3).reduce(function(p,v){return[].concat(Ho(p),[f.createElement("li",{key:v},f.createElement("button",{className:"DocSearch-Prefill",key:v,type:"button",onClick:function(){r.setQuery(v.toLowerCase()+" "),r.refresh(),r.inputRef.current.focus()}},v))])},[]))),r.getMissingResultsUrl&&f.createElement("p",{className:"DocSearch-Help"},"".concat(s," "),f.createElement("a",{href:r.getMissingResultsUrl({query:r.state.query}),target:"_blank",rel:"noopener noreferrer"},d)))}var Bo=["hit","attribute","tagName"];function _n(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(o){return Object.getOwnPropertyDescriptor(e,o).enumerable})),n.push.apply(n,r)}return n}function gn(e){for(var t=1;t=0||(i[l]=c[l]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function bn(e,t){return t.split(".").reduce(function(n,r){return n!=null&&n[r]?n[r]:null},e)}function ne(e){var t=e.hit,n=e.attribute,r=e.tagName;return F(r===void 0?"span":r,gn(gn({},zo(e,Bo)),{},{dangerouslySetInnerHTML:{__html:bn(t,"_snippetResult.".concat(n,".value"))||bn(t,n)}}))}function On(e,t){return function(n){if(Array.isArray(n))return n}(e)||function(n,r){var o=n==null?null:typeof Symbol!="undefined"&&n[Symbol.iterator]||n["@@iterator"];if(o!=null){var a,c,u=[],l=!0,s=!1;try{for(o=o.call(n);!(l=(a=o.next()).done)&&(u.push(a.value),!r||u.length!==r);l=!0);}catch(i){s=!0,c=i}finally{try{l||o.return==null||o.return()}finally{if(s)throw c}}return u}}(e,t)||function(n,r){if(!!n){if(typeof n=="string")return Sn(n,r);var o=Object.prototype.toString.call(n).slice(8,-1);if(o==="Object"&&n.constructor&&(o=n.constructor.name),o==="Map"||o==="Set")return Array.from(n);if(o==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(o))return Sn(n,r)}}(e,t)||function(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}()}function Sn(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n|<\/mark>)/g,Jo=RegExp(or.source);function ar(e){var t,n,r,o,a,c=e;if(!c.__docsearch_parent&&!e._highlightResult)return e.hierarchy.lvl0;var u=((c.__docsearch_parent?(t=c.__docsearch_parent)===null||t===void 0||(n=t._highlightResult)===null||n===void 0||(r=n.hierarchy)===null||r===void 0?void 0:r.lvl0:(o=e._highlightResult)===null||o===void 0||(a=o.hierarchy)===null||a===void 0?void 0:a.lvl0)||{}).value;return u&&Jo.test(u)?u.replace(or,""):u}function gt(){return gt=Object.assign||function(e){for(var t=1;t=0||(i[l]=c[l]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function Go(e){var t=e.translations,n=t===void 0?{}:t,r=Yo(e,Qo),o=n.recentSearchesTitle,a=o===void 0?"Recent":o,c=n.noRecentSearchesText,u=c===void 0?"No recent searches":c,l=n.saveRecentSearchButtonTitle,s=l===void 0?"Save this search":l,i=n.removeRecentSearchButtonTitle,d=i===void 0?"Remove this search from history":i,m=n.favoriteSearchesTitle,p=m===void 0?"Favorite":m,v=n.removeFavoriteSearchButtonTitle,h=v===void 0?"Remove this search from favorites":v;return r.state.status==="idle"&&r.hasCollections===!1?r.disableUserPersonalization?null:f.createElement("div",{className:"DocSearch-StartScreen"},f.createElement("p",{className:"DocSearch-Help"},u)):r.hasCollections===!1?null:f.createElement("div",{className:"DocSearch-Dropdown-Container"},f.createElement(_t,Ke({},r,{title:a,collection:r.state.collections[0],renderIcon:function(){return f.createElement("div",{className:"DocSearch-Hit-icon"},f.createElement(Do,null))},renderAction:function(y){var _=y.item,g=y.runFavoriteTransition,b=y.runDeleteTransition;return f.createElement(f.Fragment,null,f.createElement("div",{className:"DocSearch-Hit-action"},f.createElement("button",{className:"DocSearch-Hit-action-button",title:s,type:"submit",onClick:function(O){O.preventDefault(),O.stopPropagation(),g(function(){r.favoriteSearches.add(_),r.recentSearches.remove(_),r.refresh()})}},f.createElement(yn,null))),f.createElement("div",{className:"DocSearch-Hit-action"},f.createElement("button",{className:"DocSearch-Hit-action-button",title:d,type:"submit",onClick:function(O){O.preventDefault(),O.stopPropagation(),b(function(){r.recentSearches.remove(_),r.refresh()})}},f.createElement(yt,null))))}})),f.createElement(_t,Ke({},r,{title:p,collection:r.state.collections[1],renderIcon:function(){return f.createElement("div",{className:"DocSearch-Hit-icon"},f.createElement(yn,null))},renderAction:function(y){var _=y.item,g=y.runDeleteTransition;return f.createElement("div",{className:"DocSearch-Hit-action"},f.createElement("button",{className:"DocSearch-Hit-action-button",title:h,type:"submit",onClick:function(b){b.preventDefault(),b.stopPropagation(),g(function(){r.favoriteSearches.remove(_),r.refresh()})}},f.createElement(yt,null)))}})))}var Zo=["translations"];function Je(){return Je=Object.assign||function(e){for(var t=1;t=0||(i[l]=c[l]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var ea=f.memo(function(e){var t=e.translations,n=t===void 0?{}:t,r=Xo(e,Zo);if(r.state.status==="error")return f.createElement(qo,{translations:n==null?void 0:n.errorScreen});var o=r.state.collections.some(function(a){return a.items.length>0});return r.state.query?o===!1?f.createElement(Fo,Je({},r,{translations:n==null?void 0:n.noResultsScreen})):f.createElement($o,r):f.createElement(Go,Je({},r,{hasCollections:o,translations:n==null?void 0:n.startScreen}))},function(e,t){return t.state.status==="loading"||t.state.status==="stalled"}),ta=["translations"];function $e(){return $e=Object.assign||function(e){for(var t=1;t=0||(i[l]=c[l]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function ra(e){var t=e.translations,n=t===void 0?{}:t,r=na(e,ta),o=n.resetButtonTitle,a=o===void 0?"Clear the query":o,c=n.resetButtonAriaLabel,u=c===void 0?"Clear the query":c,l=n.cancelButtonText,s=l===void 0?"Cancel":l,i=n.cancelButtonAriaLabel,d=i===void 0?"Cancel":i,m=r.getFormProps({inputElement:r.inputRef.current}).onReset;return f.useEffect(function(){r.autoFocus&&r.inputRef.current&&r.inputRef.current.focus()},[r.autoFocus,r.inputRef]),f.useEffect(function(){r.isFromSelection&&r.inputRef.current&&r.inputRef.current.select()},[r.isFromSelection,r.inputRef]),f.createElement(f.Fragment,null,f.createElement("form",{className:"DocSearch-Form",onSubmit:function(p){p.preventDefault()},onReset:m},f.createElement("label",$e({className:"DocSearch-MagnifierLabel"},r.getLabelProps()),f.createElement(tr,null)),f.createElement("div",{className:"DocSearch-LoadingIndicator"},f.createElement(ko,null)),f.createElement("input",$e({className:"DocSearch-Input",ref:r.inputRef},r.getInputProps({inputElement:r.inputRef.current,autoFocus:r.autoFocus,maxLength:64}))),f.createElement("button",{type:"reset",title:a,className:"DocSearch-Reset","aria-label":u,hidden:!r.state.query},f.createElement(yt,null))),f.createElement("button",{className:"DocSearch-Cancel",type:"reset","aria-label":d,onClick:r.onClose},s))}var oa=["_highlightResult","_snippetResult"];function aa(e,t){if(e==null)return{};var n,r,o=function(c,u){if(c==null)return{};var l,s,i={},d=Object.keys(c);for(s=0;s=0||(i[l]=c[l]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function ca(e){return function(){var t="__TEST_KEY__";try{return localStorage.setItem(t,""),localStorage.removeItem(t),!0}catch{return!1}}()===!1?{setItem:function(){},getItem:function(){return[]}}:{setItem:function(t){return window.localStorage.setItem(e,JSON.stringify(t))},getItem:function(){var t=window.localStorage.getItem(e);return t?JSON.parse(t):[]}}}function jn(e){var t=e.key,n=e.limit,r=n===void 0?5:n,o=ca(t),a=o.getItem().slice(0,r);return{add:function(c){var u=c,l=(u._highlightResult,u._snippetResult,aa(u,oa)),s=a.findIndex(function(i){return i.objectID===l.objectID});s>-1&&a.splice(s,1),a.unshift(l),a=a.slice(0,r),o.setItem(a)},remove:function(c){a=a.filter(function(u){return u.objectID!==c.objectID}),o.setItem(a)},getAll:function(){return a}}}var ia=["facetName","facetQuery"];function ua(e){var t,n="algoliasearch-client-js-".concat(e.key),r=function(){return t===void 0&&(t=e.localStorage||window.localStorage),t},o=function(){return JSON.parse(r().getItem(n)||"{}")};return{get:function(a,c){var u=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{miss:function(){return Promise.resolve()}};return Promise.resolve().then(function(){var l=JSON.stringify(a),s=o()[l];return Promise.all([s||c(),s!==void 0])}).then(function(l){var s=Fe(l,2),i=s[0],d=s[1];return Promise.all([i,d||u.miss(i)])}).then(function(l){return Fe(l,1)[0]})},set:function(a,c){return Promise.resolve().then(function(){var u=o();return u[JSON.stringify(a)]=c,r().setItem(n,JSON.stringify(u)),c})},delete:function(a){return Promise.resolve().then(function(){var c=o();delete c[JSON.stringify(a)],r().setItem(n,JSON.stringify(c))})},clear:function(){return Promise.resolve().then(function(){r().removeItem(n)})}}}function ye(e){var t=Be(e.caches),n=t.shift();return n===void 0?{get:function(r,o){var a=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{miss:function(){return Promise.resolve()}};return o().then(function(c){return Promise.all([c,a.miss(c)])}).then(function(c){return Fe(c,1)[0]})},set:function(r,o){return Promise.resolve(o)},delete:function(r){return Promise.resolve()},clear:function(){return Promise.resolve()}}:{get:function(r,o){var a=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{miss:function(){return Promise.resolve()}};return n.get(r,o,a).catch(function(){return ye({caches:t}).get(r,o,a)})},set:function(r,o){return n.set(r,o).catch(function(){return ye({caches:t}).set(r,o)})},delete:function(r){return n.delete(r).catch(function(){return ye({caches:t}).delete(r)})},clear:function(){return n.clear().catch(function(){return ye({caches:t}).clear()})}}}function ct(){var e=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{serializable:!0},t={};return{get:function(n,r){var o=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{miss:function(){return Promise.resolve()}},a=JSON.stringify(n);if(a in t)return Promise.resolve(e.serializable?JSON.parse(t[a]):t[a]);var c=r(),u=o&&o.miss||function(){return Promise.resolve()};return c.then(function(l){return u(l)}).then(function(){return c})},set:function(n,r){return t[JSON.stringify(n)]=e.serializable?JSON.stringify(r):r,Promise.resolve(r)},delete:function(n){return delete t[JSON.stringify(n)],Promise.resolve()},clear:function(){return t={},Promise.resolve()}}}function la(e){for(var t=e.length-1;t>0;t--){var n=Math.floor(Math.random()*(t+1)),r=e[t];e[t]=e[n],e[n]=r}return e}function cr(e,t){return t&&Object.keys(t).forEach(function(n){e[n]=t[n](e)}),e}function Qe(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r0?r:void 0,timeout:n.timeout||t,headers:n.headers||{},queryParameters:n.queryParameters||{},cacheable:n.cacheable}}var ce={Read:1,Write:2,Any:3},ir=1,sa=2,ur=3;function lr(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:ir;return P(P({},e),{},{status:t,lastUpdate:Date.now()})}function sr(e){return typeof e=="string"?{protocol:"https",url:e,accept:ce.Any}:{protocol:e.protocol||"https",url:e.url,accept:e.accept||ce.Any}}var In="GET",Ye="POST";function fa(e,t){return Promise.all(t.map(function(n){return e.get(n,function(){return Promise.resolve(lr(n))})})).then(function(n){var r=n.filter(function(c){return function(u){return u.status===ir||Date.now()-u.lastUpdate>12e4}(c)}),o=n.filter(function(c){return function(u){return u.status===ur&&Date.now()-u.lastUpdate<=12e4}(c)}),a=[].concat(Be(r),Be(o));return{getTimeout:function(c,u){return(o.length===0&&c===0?1:o.length+3+c)*u},statelessHosts:a.length>0?a.map(function(c){return sr(c)}):t}})}function kn(e,t,n,r){var o=[],a=function(m,p){if(!(m.method===In||m.data===void 0&&p.data===void 0)){var v=Array.isArray(m.data)?m.data:P(P({},m.data),p.data);return JSON.stringify(v)}}(n,r),c=function(m,p){var v=P(P({},m.headers),p.headers),h={};return Object.keys(v).forEach(function(y){var _=v[y];h[y.toLowerCase()]=_}),h}(e,r),u=n.method,l=n.method!==In?{}:P(P({},n.data),r.data),s=P(P(P({"x-algolia-agent":e.userAgent.value},e.queryParameters),l),r.queryParameters),i=0,d=function m(p,v){var h=p.pop();if(h===void 0)throw{name:"RetryError",message:"Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.",transporterStackTrace:Dn(o)};var y={data:a,headers:c,method:u,url:ma(h,n.path,s),connectTimeout:v(i,e.timeouts.connect),responseTimeout:v(i,r.timeout)},_=function(b){var O={request:y,response:b,host:h,triesLeft:p.length};return o.push(O),O},g={onSucess:function(b){return function(O){try{return JSON.parse(O.content)}catch(S){throw function(I,C){return{name:"DeserializationError",message:I,response:C}}(S.message,O)}}(b)},onRetry:function(b){var O=_(b);return b.isTimedOut&&i++,Promise.all([e.logger.info("Retryable failure",pr(O)),e.hostsCache.set(h,lr(h,b.isTimedOut?ur:sa))]).then(function(){return m(p,v)})},onFail:function(b){throw _(b),function(O,S){var I=O.content,C=O.status,x=I;try{x=JSON.parse(I).message}catch{}return function(k,N,R){return{name:"ApiError",message:k,status:N,transporterStackTrace:R}}(x,C,S)}(b,Dn(o))}};return e.requester.send(y).then(function(b){return function(O,S){return function(I){var C=I.status;return I.isTimedOut||function(x){var k=x.isTimedOut,N=x.status;return!k&&~~N==0}(I)||~~(C/100)!=2&&~~(C/100)!=4}(O)?S.onRetry(O):~~(O.status/100)==2?S.onSucess(O):S.onFail(O)}(b,g)})};return fa(e.hostsCache,t).then(function(m){return d(Be(m.statelessHosts).reverse(),m.getTimeout)})}function pa(e){var t={value:"Algolia for JavaScript (".concat(e,")"),add:function(n){var r="; ".concat(n.segment).concat(n.version!==void 0?" (".concat(n.version,")"):"");return t.value.indexOf(r)===-1&&(t.value="".concat(t.value).concat(r)),t}};return t}function ma(e,t,n){var r=fr(n),o="".concat(e.protocol,"://").concat(e.url,"/").concat(t.charAt(0)==="/"?t.substr(1):t);return r.length&&(o+="?".concat(r)),o}function fr(e){return Object.keys(e).map(function(t){return Qe("%s=%s",t,(n=e[t],Object.prototype.toString.call(n)==="[object Object]"||Object.prototype.toString.call(n)==="[object Array]"?JSON.stringify(e[t]):e[t]));var n}).join("&")}function Dn(e){return e.map(function(t){return pr(t)})}function pr(e){var t=e.request.headers["x-algolia-api-key"]?{"x-algolia-api-key":"*****"}:{};return P(P({},e),{},{request:P(P({},e.request),{},{headers:P(P({},e.request.headers),t)})})}var da=function(e){var t=e.appId,n=function(a,c,u){var l={"x-algolia-api-key":u,"x-algolia-application-id":c};return{headers:function(){return a===Ue.WithinHeaders?l:{}},queryParameters:function(){return a===Ue.WithinQueryParameters?l:{}}}}(e.authMode!==void 0?e.authMode:Ue.WithinHeaders,t,e.apiKey),r=function(a){var c=a.hostsCache,u=a.logger,l=a.requester,s=a.requestsCache,i=a.responsesCache,d=a.timeouts,m=a.userAgent,p=a.hosts,v=a.queryParameters,h={hostsCache:c,logger:u,requester:l,requestsCache:s,responsesCache:i,timeouts:d,userAgent:m,headers:a.headers,queryParameters:v,hosts:p.map(function(y){return sr(y)}),read:function(y,_){var g=Pn(_,h.timeouts.read),b=function(){return kn(h,h.hosts.filter(function(S){return(S.accept&ce.Read)!=0}),y,g)};if((g.cacheable!==void 0?g.cacheable:y.cacheable)!==!0)return b();var O={request:y,mappedRequestOptions:g,transporter:{queryParameters:h.queryParameters,headers:h.headers}};return h.responsesCache.get(O,function(){return h.requestsCache.get(O,function(){return h.requestsCache.set(O,b()).then(function(S){return Promise.all([h.requestsCache.delete(O),S])},function(S){return Promise.all([h.requestsCache.delete(O),Promise.reject(S)])}).then(function(S){var I=Fe(S,2);return I[0],I[1]})})},{miss:function(S){return h.responsesCache.set(O,S)}})},write:function(y,_){return kn(h,h.hosts.filter(function(g){return(g.accept&ce.Write)!=0}),y,Pn(_,h.timeouts.write))}};return h}(P(P({hosts:[{url:"".concat(t,"-dsn.algolia.net"),accept:ce.Read},{url:"".concat(t,".algolia.net"),accept:ce.Write}].concat(la([{url:"".concat(t,"-1.algolianet.com")},{url:"".concat(t,"-2.algolianet.com")},{url:"".concat(t,"-3.algolianet.com")}]))},e),{},{headers:P(P(P({},n.headers()),{"content-type":"application/x-www-form-urlencoded"}),e.headers),queryParameters:P(P({},n.queryParameters()),e.queryParameters)})),o={transporter:r,appId:t,addAlgoliaAgent:function(a,c){r.userAgent.add({segment:a,version:c})},clearCache:function(){return Promise.all([r.requestsCache.clear(),r.responsesCache.clear()]).then(function(){})}};return cr(o,e.methods)},mr=function(e){return function(t){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},r={transporter:e.transporter,appId:e.appId,indexName:t};return cr(r,n.methods)}},Cn=function(e){return function(t,n){var r=t.map(function(o){return P(P({},o),{},{params:fr(o.params||{})})});return e.transporter.read({method:Ye,path:"1/indexes/*/queries",data:{requests:r},cacheable:!0},n)}},An=function(e){return function(t,n){return Promise.all(t.map(function(r){var o=r.params,a=o.facetName,c=o.facetQuery,u=Sr(o,ia);return mr(e)(r.indexName,{methods:{searchForFacetValues:dr}}).searchForFacetValues(a,c,P(P({},n),u))}))}},ha=function(e){return function(t,n,r){return e.transporter.read({method:Ye,path:Qe("1/answers/%s/prediction",e.indexName),data:{query:t,queryLanguages:n},cacheable:!0},r)}},va=function(e){return function(t,n){return e.transporter.read({method:Ye,path:Qe("1/indexes/%s/query",e.indexName),data:{query:t},cacheable:!0},n)}},dr=function(e){return function(t,n,r){return e.transporter.read({method:Ye,path:Qe("1/indexes/%s/facets/%s/query",e.indexName,t),data:{facetQuery:n},cacheable:!0},r)}},ya=1,_a=2,ga=3;function hr(e,t,n){var r,o={appId:e,apiKey:t,timeouts:{connect:1,read:2,write:30},requester:{send:function(a){return new Promise(function(c){var u=new XMLHttpRequest;u.open(a.method,a.url,!0),Object.keys(a.headers).forEach(function(d){return u.setRequestHeader(d,a.headers[d])});var l,s=function(d,m){return setTimeout(function(){u.abort(),c({status:0,content:m,isTimedOut:!0})},1e3*d)},i=s(a.connectTimeout,"Connection timeout");u.onreadystatechange=function(){u.readyState>u.OPENED&&l===void 0&&(clearTimeout(i),l=s(a.responseTimeout,"Socket timeout"))},u.onerror=function(){u.status===0&&(clearTimeout(i),clearTimeout(l),c({content:u.responseText||"Network request failed",status:u.status,isTimedOut:!1}))},u.onload=function(){clearTimeout(i),clearTimeout(l),c({content:u.responseText,status:u.status,isTimedOut:!1})},u.send(a.data)})}},logger:(r=ga,{debug:function(a,c){return ya>=r&&console.debug(a,c),Promise.resolve()},info:function(a,c){return _a>=r&&console.info(a,c),Promise.resolve()},error:function(a,c){return console.error(a,c),Promise.resolve()}}),responsesCache:ct(),requestsCache:ct({serializable:!1}),hostsCache:ye({caches:[ua({key:"".concat("4.8.5","-").concat(e)}),ct()]}),userAgent:pa("4.8.5").add({segment:"Browser",version:"lite"}),authMode:Ue.WithinQueryParameters};return da(P(P(P({},o),n),{},{methods:{search:Cn,searchForFacetValues:An,multipleQueries:Cn,multipleSearchForFacetValues:An,initIndex:function(a){return function(c){return mr(a)(c,{methods:{search:va,searchForFacetValues:dr,findAnswers:ha}})}}}}))}hr.version="4.8.5";var ba=["footer","searchBox"];function be(){return be=Object.assign||function(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=new Array(t);n=0||(i[l]=c[l]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function wa(e){var t=e.appId,n=e.apiKey,r=e.indexName,o=e.placeholder,a=o===void 0?"Search docs":o,c=e.searchParameters,u=e.onClose,l=u===void 0?Ko:u,s=e.transformItems,i=s===void 0?wn:s,d=e.hitComponent,m=d===void 0?Io:d,p=e.resultsFooterComponent,v=p===void 0?function(){return null}:p,h=e.navigator,y=e.initialScrollY,_=y===void 0?0:y,g=e.transformSearchClient,b=g===void 0?wn:g,O=e.disableUserPersonalization,S=O!==void 0&&O,I=e.initialQuery,C=I===void 0?"":I,x=e.translations,k=x===void 0?{}:x,N=e.getMissingResultsUrl,R=k.footer,Ge=k.searchBox,Ze=Ea(k,ba),Pe=Sa(f.useState({query:"",collections:[],completion:null,context:{},isOpen:!1,activeItemId:null,status:"idle"}),2),K=Pe[0],vr=Pe[1],wt=f.useRef(null),Xe=f.useRef(null),jt=f.useRef(null),Ie=f.useRef(null),ue=f.useRef(null),J=f.useRef(10),Pt=f.useRef(typeof window!="undefined"?window.getSelection().toString().slice(0,64):"").current,Y=f.useRef(C||Pt).current,It=function(j,D,q){return f.useMemo(function(){var M=hr(j,D);return M.addAlgoliaAgent("docsearch","3.0.0"),/docsearch.js \(.*\)/.test(M.transporter.userAgent.value)===!1&&M.addAlgoliaAgent("docsearch-react","3.0.0"),q(M)},[j,D,q])}(t,n,b),X=f.useRef(jn({key:"__DOCSEARCH_FAVORITE_SEARCHES__".concat(r),limit:10})).current,le=f.useRef(jn({key:"__DOCSEARCH_RECENT_SEARCHES__".concat(r),limit:X.getAll().length===0?7:4})).current,se=f.useCallback(function(j){if(!S){var D=j.type==="content"?j.__docsearch_parent:j;D&&X.getAll().findIndex(function(q){return q.objectID===D.objectID})===-1&&le.add(D)}},[X,le,S]),fe=f.useMemo(function(){return wo({id:"docsearch",defaultActiveItemId:0,placeholder:a,openOnFocus:!0,initialState:{query:Y,context:{searchSuggestions:[]}},navigator:h,onStateChange:function(j){vr(j.state)},getSources:function(j){var D=j.query,q=j.state,M=j.setContext,$=j.setStatus;return D?It.search([{query:D,indexName:r,params:it({attributesToRetrieve:["hierarchy.lvl0","hierarchy.lvl1","hierarchy.lvl2","hierarchy.lvl3","hierarchy.lvl4","hierarchy.lvl5","hierarchy.lvl6","content","type","url"],attributesToSnippet:["hierarchy.lvl1:".concat(J.current),"hierarchy.lvl2:".concat(J.current),"hierarchy.lvl3:".concat(J.current),"hierarchy.lvl4:".concat(J.current),"hierarchy.lvl5:".concat(J.current),"hierarchy.lvl6:".concat(J.current),"content:".concat(J.current)],snippetEllipsisText:"\u2026",highlightPreTag:"",highlightPostTag:"",hitsPerPage:20},c)}]).catch(function(A){throw A.name==="RetryError"&&$("error"),A}).then(function(A){var H=A.results[0],U=H.hits,gr=H.nbHits,et=En(U,function(tt){return ar(tt)});return q.context.searchSuggestions.length0&&(kt(),ue.current&&ue.current.focus())},[Y,kt]),f.useEffect(function(){function j(){if(Xe.current){var D=.01*window.innerHeight;Xe.current.style.setProperty("--docsearch-vh","".concat(D,"px"))}}return j(),window.addEventListener("resize",j),function(){window.removeEventListener("resize",j)}},[]),f.createElement("div",be({ref:wt},_r({"aria-expanded":!0}),{className:["DocSearch","DocSearch-Container",K.status==="stalled"&&"DocSearch-Container--Stalled",K.status==="error"&&"DocSearch-Container--Errored"].filter(Boolean).join(" "),role:"button",tabIndex:0,onMouseDown:function(j){j.target===j.currentTarget&&l()}}),f.createElement("div",{className:"DocSearch-Modal",ref:Xe},f.createElement("header",{className:"DocSearch-SearchBar",ref:jt},f.createElement(ra,be({},fe,{state:K,autoFocus:Y.length===0,inputRef:ue,isFromSelection:Boolean(Y)&&Y===Pt,translations:Ge,onClose:l}))),f.createElement("div",{className:"DocSearch-Dropdown",ref:Ie},f.createElement(ea,be({},fe,{indexName:r,state:K,hitComponent:m,resultsFooterComponent:v,disableUserPersonalization:S,recentSearches:le,favoriteSearches:X,inputRef:ue,translations:Ze,getMissingResultsUrl:N,onItemClick:function(j){se(j),l()}}))),f.createElement("footer",{className:"DocSearch-Footer"},f.createElement(Po,{translations:R}))))}function bt(){return bt=Object.assign||function(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=new Array(t);n1&&arguments[1]!==void 0?arguments[1]:window;return typeof t=="string"?n.document.querySelector(t):t}(e.container,e.environment))}export{Pa as j}; diff --git a/assets/faq_index.md.98b8e957.js b/assets/faq_index.md.98b8e957.js new file mode 100644 index 00000000..a88a04a6 --- /dev/null +++ b/assets/faq_index.md.98b8e957.js @@ -0,0 +1,5 @@ +import{_ as e,c as t,o,a as n}from"./app.ef097145.js";const b='{"title":"F.A.Q.","description":"","frontmatter":{},"headers":[{"level":2,"title":"How do I help contribute?","slug":"how-do-i-help-contribute"},{"level":2,"title":"How do I debug?","slug":"how-do-i-debug"},{"level":2,"title":"How do I run the test suite?","slug":"how-do-i-run-the-test-suite"},{"level":2,"title":"My tests are failing because slow DB connection and short test timeouts! How to extend test timeouts?","slug":"my-tests-are-failing-because-slow-db-connection-and-short-test-timeouts-how-to-extend-test-timeouts"},{"level":2,"title":"I found something broken with Amazon Redshift! Can you help?","slug":"i-found-something-broken-with-amazon-redshift-can-you-help"}],"relativePath":"faq/index.md"}',a={},s=n(`

F.A.Q.

How do I help contribute?

Glad you asked! Pull requests, or feature requests, though not always implemented, are a great way to help make Knex even better than it is now. If you're looking for something specific to help out with, there's a number of unit tests that aren't implemented yet, the library could never have too many of those. If you want to submit a fix or feature, take a look at the Contributing readme in the Github and go ahead and open a ticket.

How do I debug?

Knex is beginning to make use of the debug module internally, so you can set the DEBUG environment variable to knex:* to see all debugging, or select individual namespaces DEBUG=knex:query,knex:tx to constrain a bit.

If you pass {debug: true} as one of the options in your initialize settings, you can see all of the query calls being made. Sometimes you need to dive a bit further into the various calls and see what all is going on behind the scenes. I'd recommend node-inspector, which allows you to debug code with debugger statements like you would in the browser.

At the start of your application code will catch any errors not otherwise caught in the normal promise chain handlers, which is very helpful in debugging.

How do I run the test suite?

The test suite looks for an environment variable called KNEX_TEST for the path to the database configuration. If you run the following command:

$ export KNEX_TEST='/path/to/your/knex_config.js'
+$ npm test
+

replacing with the path to your config file, and the config file is valid, the test suite should run properly.

My tests are failing because slow DB connection and short test timeouts! How to extend test timeouts?

Sometimes, e.g. when running CI on travis, test suite's default timeout of 5 seconds might be too short. In such cases an alternative test timeout value in milliseconds can be specified using the KNEX_TEST_TIMEOUT environment variable.

$ export KNEX_TEST_TIMEOUT=30000
+$ npm test
+

I found something broken with Amazon Redshift! Can you help?

Because there is no testing platform available for Amazon Redshift, be aware that it is included as a dialect but is unsupported. With that said, please file an issue if something is found to be broken that is not noted in the documentation, and we will do our best.

`,16),i=[s];function r(d,h,l,u,c,p){return o(),t("div",null,i)}var f=e(a,[["render",r]]);export{b as __pageData,f as default}; diff --git a/assets/faq_index.md.98b8e957.lean.js b/assets/faq_index.md.98b8e957.lean.js new file mode 100644 index 00000000..17d4bcca --- /dev/null +++ b/assets/faq_index.md.98b8e957.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o,a as n}from"./app.ef097145.js";const b='{"title":"F.A.Q.","description":"","frontmatter":{},"headers":[{"level":2,"title":"How do I help contribute?","slug":"how-do-i-help-contribute"},{"level":2,"title":"How do I debug?","slug":"how-do-i-debug"},{"level":2,"title":"How do I run the test suite?","slug":"how-do-i-run-the-test-suite"},{"level":2,"title":"My tests are failing because slow DB connection and short test timeouts! How to extend test timeouts?","slug":"my-tests-are-failing-because-slow-db-connection-and-short-test-timeouts-how-to-extend-test-timeouts"},{"level":2,"title":"I found something broken with Amazon Redshift! Can you help?","slug":"i-found-something-broken-with-amazon-redshift-can-you-help"}],"relativePath":"faq/index.md"}',a={},s=n("",16),i=[s];function r(d,h,l,u,c,p){return o(),t("div",null,i)}var f=e(a,[["render",r]]);export{b as __pageData,f as default}; diff --git a/assets/faq_recipes.md.71463d9b.js b/assets/faq_recipes.md.71463d9b.js new file mode 100644 index 00000000..96c60a8d --- /dev/null +++ b/assets/faq_recipes.md.71463d9b.js @@ -0,0 +1,132 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const h=`{"title":"Recipes","description":"","frontmatter":{},"headers":[{"level":2,"title":"Using non-standard database that is compatible with PostgreSQL wire protocol (such as CockroachDB)","slug":"using-non-standard-database-that-is-compatible-with-postgresql-wire-protocol-such-as-cockroachdb"},{"level":2,"title":"Connecting to MSSQL on Azure SQL Database","slug":"connecting-to-mssql-on-azure-sql-database"},{"level":2,"title":"Adding a full-text index for PostgreSQL","slug":"adding-a-full-text-index-for-postgresql"},{"level":2,"title":"DB access using SQLite and SQLCipher","slug":"db-access-using-sqlite-and-sqlcipher"},{"level":2,"title":"Maintaining changelog for seeds (version >= 0.16.0-next1)","slug":"maintaining-changelog-for-seeds-version-0-16-0-next1"},{"level":2,"title":"Using explicit transaction management together with async code","slug":"using-explicit-transaction-management-together-with-async-code"},{"level":2,"title":"Using parentheses with AND operator","slug":"using-parentheses-with-and-operator"},{"level":2,"title":"Calling an oracle stored procedure with bindout variables","slug":"calling-an-oracle-stored-procedure-with-bindout-variables"},{"level":2,"title":"Node instance doesn't stop after using knex","slug":"node-instance-doesn-t-stop-after-using-knex"},{"level":2,"title":"Manually Closing Streams","slug":"manually-closing-streams"}],"relativePath":"faq/recipes.md"}`,p={},e=t(`

Recipes

Using non-standard database that is compatible with PostgreSQL wire protocol (such as CockroachDB)

Specify PostgreSQL version that database you are using is compatible with protocol-wise using version option, e. g.:

const knex = require('knex')({
+  client: 'pg',
+  version: '7.2',
+  connection: {
+    host: '127.0.0.1',
+    user: 'your_database_user',
+    password: 'your_database_password',
+    database: 'myapp_test'
+  }
+});
+

Note that value of version option should be not the version of the database that you are using, but version of PostgreSQL that most closely matches functionality of the database that you are using. If not provided by database vendor, try using '7.2' as a baseline and keep increasing (within the range of existing PostgreSQL versions) until it starts (or stops) working.

There are also known incompatibilities with migrations for databases that do not support select for update. See https://github.com/tgriesser/knex/issues/2002 for a workaround.

Connecting to MSSQL on Azure SQL Database

{encrypt: true} should be included in options branch of connection configuration:

knex({
+  client : 'mssql',
+  connection: {
+    database: 'mydatabase',
+    server: 'myserver.database.windows.net',
+    user: 'myuser',
+    password: 'mypass',
+    port: 1433,
+    connectionTimeout: 30000,
+    options: {
+      encrypt: true
+    }
+  }
+});
+

See all of node-mssql's connection options

Adding a full-text index for PostgreSQL

exports.up = (knex) => {
+  return knex.schema.createTable('foo', (table) => {
+    table.increments('id');
+    table.specificType('fulltext', 'tsvector');
+    table.index('fulltext', null, 'gin');
+  })
+};
+

DB access using SQLite and SQLCipher

After you build the SQLCipher source and the npm SQLite3 package, and encrypt your DB (look elsewhere for these things), then anytime you open your database, you need to provide your encryption key using the SQL statement:

PRAGMA KEY = 'secret'
+

This PRAGMA is more completely documented in the SQLCipher site. When working with Knex this is best done when opening the DB, via the following:

const myDBConfig = {
+  client: "sqlite3",
+  connection: {
+    filename: "myEncryptedSQLiteDbFile.db"
+  },
+  pool: {
+    afterCreate: function(conn, done) {
+      conn.run("PRAGMA KEY = 'secret'");
+      done();
+    }  
+  } 
+};
+const knex = require('knex')(myDBConfig);
+

Of course embedding the key value in your code is a poor security practice. Instead, retrieve the 'secret' from elsewhere.

The key Knex thing to note here is the "afterCreate" function. This is documented in the knexjs.org site, but is not in the Table of Contents at this time, so do a browser find when on the site to get to it. It allows auto-updating DB settings when creating any new pool connections (of which there will only ever be one per file for Knex-SQLite).

If you don't use the "afterCreate" configuration, then you will need to run a knex.raw statement with each and every SQL you execute, something like as follows:

return knex.raw("PRAGMA KEY = 'secret'")
+  .then(() => knex('some_table')
+    .select()
+    .on('query-error', function(ex, obj) {
+      console.log(
+        "KNEX select from some_table ERR ex:", 
+        ex, 
+        "obj:", 
+        obj
+      );
+    })
+  );
+

Maintaining changelog for seeds (version >= 0.16.0-next1)

In case you would like to use Knex.js changelog functionality to ensure your environments are only seeded once, but don't want to mix seed files with migration files, you can specify multiple directories as a source for your migrations:

await knex.migrate.latest({
+    directory: [
+      'src/services/orders/database/migrations',
+      'src/services/orders/database/seeds'
+    ],
+    sortDirsSeparately: true,
+    tableName: 'orders_migrations',
+    schemaName: 'orders',  
+})
+

Using explicit transaction management together with async code

await knex.transaction(trx => {
+  async function stuff() {
+    trx.rollback(new Error('Foo'));
+  };
+  stuff()
+    .then(() => {
+      // do something
+    });
+});
+

Or alternatively:

try {
+  await knex.transaction(trx => {
+    async function stuff() {
+      trx.rollback(new Error('always explicit rollback this time'));
+    }
+    stuff();
+  }); 
+  // transaction was committed
+  } catch (err) {
+    // transaction was rolled back 
+  }
+

(note that promise for knex.transaction resolves after transaction is rolled back or committed)

Using parentheses with AND operator

In order to generate query along the lines of

SELECT "firstName", "lastName", "status"
+FROM "userInfo" 
+WHERE "status" = 'active'
+AND ("firstName" ILIKE '%Ali%' OR "lastName" ILIKE '%Ali%');
+

you need to use following approach:

queryBuilder
+  .where('status', status.uuid)
+  .andWhere((qB) => qB
+    .where('firstName', 'ilike', \`%\${q}%\`)
+    .orWhere('lastName', 'ilike', \`%\${q}%\`)
+  )
+

Calling an oracle stored procedure with bindout variables

How to call and retrieve output from an oracle stored procedure

const oracle = require('oracledb');
+const bindVars = {
+  input_var1: 6,
+  input_var2: 7,
+  output_var: {
+    dir: oracle.BIND_OUT
+  },
+  output_message: {
+    dir: oracle.BIND_OUT
+  }
+};
+
+const sp = 'BEGIN MULTIPLY_STORED_PROCEDURE(:input_var1, :input_var2, :output_var, :output_message); END;';
+const results = await knex.raw(sp, bindVars);
+console.log(results[0]); // 42
+console.log(results[1]); // 6 * 7 is the answer to life
+

Node instance doesn't stop after using knex

Make sure to close knex instance after execution to avoid Node process hanging due to open connections:

async function migrate() {
+  try {
+    await knex.migrate.latest({/**config**/})
+  } catch (e) {
+    process.exit(1)
+  } finally {
+    try {
+      knex.destroy()
+    } catch (e) {
+      // ignore
+    }
+  }
+}
+
+migrate()
+

Manually Closing Streams

When using Knex's stream interface, you can typically just pipe the return stream to any writable stream. However, with HTTPIncomingMessage, you'll need to take special care to handle aborted requests.

An HTTPIncomingMessage object is typically called request. This is the first argument in 'request' events emitted on http.Server instances. Express's req implements a compatible interface and Hapi exposes this object on its request objects as request.raw.req.

You need to explicitly handle the case where an HTTPIncomingMessage is closed prematurely when streaming from a database with Knex. The easiest way to cause this is:

  1. Visit an endpoint that takes several seconds to fully transmit a response
  2. Close the browser window immediately after beginning the request

When this happens while you are streaming a query to a client, you need to manually tell Knex that it can release the database connection in use back to the connection pool.

server.on('request', function (request, response) {
+  const stream = knex.select('*').from('items').stream();
+  request.on('close', stream.end.bind(stream));
+});
+
`,47),o=[e];function c(i,l,u,r,k,d){return a(),s("div",null,o)}var f=n(p,[["render",c]]);export{h as __pageData,f as default}; diff --git a/assets/faq_recipes.md.71463d9b.lean.js b/assets/faq_recipes.md.71463d9b.lean.js new file mode 100644 index 00000000..b245f1fe --- /dev/null +++ b/assets/faq_recipes.md.71463d9b.lean.js @@ -0,0 +1 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const h=`{"title":"Recipes","description":"","frontmatter":{},"headers":[{"level":2,"title":"Using non-standard database that is compatible with PostgreSQL wire protocol (such as CockroachDB)","slug":"using-non-standard-database-that-is-compatible-with-postgresql-wire-protocol-such-as-cockroachdb"},{"level":2,"title":"Connecting to MSSQL on Azure SQL Database","slug":"connecting-to-mssql-on-azure-sql-database"},{"level":2,"title":"Adding a full-text index for PostgreSQL","slug":"adding-a-full-text-index-for-postgresql"},{"level":2,"title":"DB access using SQLite and SQLCipher","slug":"db-access-using-sqlite-and-sqlcipher"},{"level":2,"title":"Maintaining changelog for seeds (version >= 0.16.0-next1)","slug":"maintaining-changelog-for-seeds-version-0-16-0-next1"},{"level":2,"title":"Using explicit transaction management together with async code","slug":"using-explicit-transaction-management-together-with-async-code"},{"level":2,"title":"Using parentheses with AND operator","slug":"using-parentheses-with-and-operator"},{"level":2,"title":"Calling an oracle stored procedure with bindout variables","slug":"calling-an-oracle-stored-procedure-with-bindout-variables"},{"level":2,"title":"Node instance doesn't stop after using knex","slug":"node-instance-doesn-t-stop-after-using-knex"},{"level":2,"title":"Manually Closing Streams","slug":"manually-closing-streams"}],"relativePath":"faq/recipes.md"}`,p={},e=t("",47),o=[e];function c(i,l,u,r,k,d){return a(),s("div",null,o)}var f=n(p,[["render",c]]);export{h as __pageData,f as default}; diff --git a/assets/faq_support.md.3a43ae3b.js b/assets/faq_support.md.3a43ae3b.js new file mode 100644 index 00000000..ab480a9f --- /dev/null +++ b/assets/faq_support.md.3a43ae3b.js @@ -0,0 +1 @@ +import{_ as e,c as r,o as t,a as o}from"./app.ef097145.js";const d='{"title":"Support","description":"","frontmatter":{},"headers":[],"relativePath":"faq/support.md"}',a={},n=o('

Support

Have questions about the library? Come join us in the #bookshelf freenode IRC channel for support on knex.js and bookshelf.js, or post an issue on Stack Overflow or in the GitHub issue tracker.

',2),s=[n];function p(_,h,c,i,f,l){return t(),r("div",null,s)}var k=e(a,[["render",p]]);export{d as __pageData,k as default}; diff --git a/assets/faq_support.md.3a43ae3b.lean.js b/assets/faq_support.md.3a43ae3b.lean.js new file mode 100644 index 00000000..aa32df6d --- /dev/null +++ b/assets/faq_support.md.3a43ae3b.lean.js @@ -0,0 +1 @@ +import{_ as e,c as r,o as t,a as o}from"./app.ef097145.js";const d='{"title":"Support","description":"","frontmatter":{},"headers":[],"relativePath":"faq/support.md"}',a={},n=o("",2),s=[n];function p(_,h,c,i,f,l){return t(),r("div",null,s)}var k=e(a,[["render",p]]);export{d as __pageData,k as default}; diff --git a/assets/guide_extending.md.c10412b7.js b/assets/guide_extending.md.c10412b7.js new file mode 100644 index 00000000..f4a95078 --- /dev/null +++ b/assets/guide_extending.md.c10412b7.js @@ -0,0 +1,34 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const m='{"title":"Extending","description":"","frontmatter":{},"headers":[],"relativePath":"guide/extending.md"}',p={},o=t(`

Extending

To extend knex's builders, we have the following methods

knex.SchemaBuilder.extend("functionName", function() {
+    console.log('Custom Schema Builder Function');
+    return this;
+});
+knex.TableBuilder.extend("functionName", function() {
+    console.log('Custom Table Builder Function');
+    return this;
+});
+knex.ViewBuilder.extend("functionName", function() {
+    console.log('Custom View Builder Function');
+    return this;
+});
+knex.ColumnBuilder.extend("functionName", function() {
+    console.log('Custom Column Builder Function');
+    return this;
+});
+

To add typescript support you can add the following (.d.ts):

import "knex";
+declare module "knex" {
+    namespace Knex {
+        interface SchemaBuilder {
+            functionName (): Knex.SchemaBuilder;
+        }
+        interface TableBuilder {
+            functionName (): Knex.TableBuilder;
+        }
+        interface ViewBuilder {
+            functionName (): Knex.ViewBuilder;
+        }
+        interface ColumnBuilder {
+            functionName (): Knex.ColumnBuilder;
+        }
+    }
+}
+
`,5),e=[o];function c(u,l,i,k,r,d){return a(),s("div",null,e)}var g=n(p,[["render",c]]);export{m as __pageData,g as default}; diff --git a/assets/guide_extending.md.c10412b7.lean.js b/assets/guide_extending.md.c10412b7.lean.js new file mode 100644 index 00000000..f54cd29c --- /dev/null +++ b/assets/guide_extending.md.c10412b7.lean.js @@ -0,0 +1 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const m='{"title":"Extending","description":"","frontmatter":{},"headers":[],"relativePath":"guide/extending.md"}',p={},o=t("",5),e=[o];function c(u,l,i,k,r,d){return a(),s("div",null,e)}var g=n(p,[["render",c]]);export{m as __pageData,g as default}; diff --git a/assets/guide_index.md.895872e8.js b/assets/guide_index.md.895872e8.js new file mode 100644 index 00000000..76156304 --- /dev/null +++ b/assets/guide_index.md.895872e8.js @@ -0,0 +1,357 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const g='{"title":"Installation","description":"","frontmatter":{},"headers":[{"level":2,"title":"Node.js","slug":"node-js"},{"level":2,"title":"Browser","slug":"browser"},{"level":2,"title":"Configuration Options","slug":"configuration-options"},{"level":3,"title":"withUserParams","slug":"withuserparams"},{"level":3,"title":"debug","slug":"debug"},{"level":3,"title":"asyncStackTraces","slug":"asyncstacktraces"},{"level":3,"title":"pool","slug":"pool"},{"level":3,"title":"afterCreate","slug":"aftercreate"},{"level":3,"title":"acquireConnectionTimeout","slug":"acquireconnectiontimeout"},{"level":3,"title":"fetchAsString","slug":"fetchasstring"},{"level":3,"title":"migrations","slug":"migrations"},{"level":3,"title":"postProcessResponse","slug":"postprocessresponse"},{"level":3,"title":"wrapIdentifier","slug":"wrapidentifier"},{"level":3,"title":"log","slug":"log"},{"level":3,"title":"compileSqlOnError","slug":"compilesqlonerror"},{"level":2,"title":"TypeScript","slug":"typescript"}],"relativePath":"guide/index.md"}',p={},e=t(`

Installation

Knex can be used as an SQL query builder in both Node.JS and the browser, limited to WebSQL's constraints (like the inability to drop tables or read schemas). Composing SQL queries in the browser for execution on the server is highly discouraged, as this can be the cause of serious security vulnerabilities. The browser builds outside of WebSQL are primarily for learning purposes - for example, you can pop open the console and build queries on this page using the knex object.

Node.js

The primary target environment for Knex is Node.js, you will need to install the knex library, and then install the appropriate database library: pg for PostgreSQL, CockroachDB and Amazon Redshift, pg-native for PostgreSQL with native C++ libpq bindings (requires PostgresSQL installed to link against), mysql for MySQL or MariaDB, sqlite3 for SQLite3, or tedious for MSSQL.

$ npm install knex --save
+
+# Then add one of the following (adding a --save) flag:
+$ npm install pg
+$ npm install pg-native
+$ npm install sqlite3
+$ npm install better-sqlite3
+$ npm install mysql
+$ npm install mysql2
+$ npm install oracledb
+$ npm install tedious
+

If you want to use CockroachDB or Redshift instance, you can use the pg driver.

If you want to use a MariaDB instance, you can use the mysql driver.

Browser

Knex can be built using a JavaScript build tool such as browserify or webpack. In fact, this documentation uses a webpack build which includes knex. View source on this page to see the browser build in-action (the global knex variable).

Configuration Options

The knex module is itself a function which takes a configuration object for Knex, accepting a few parameters. The client parameter is required and determines which client adapter will be used with the library.

const knex = require('knex')({
+  client: 'mysql',
+  connection: {
+    host : '127.0.0.1',
+    port : 3306,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  }
+});
+

The connection options are passed directly to the appropriate database client to create the connection, and may be either an object, a connection string, or a function returning an object:

PostgreSQL

Knex's PostgreSQL client allows you to set the initial search path for each connection automatically using an additional option "searchPath" as shown below.

const pg = require('knex')({
+  client: 'pg',
+  connection: process.env.PG_CONNECTION_STRING,
+  searchPath: ['knex', 'public'],
+});
+

When using the PostgreSQL driver, another usage pattern for instantiating the Knex configuration object could be to use a connection: {} object details to specify various flags such as enabling SSL, a connection string, and individual connection configuration fields all in the same object. Consider the following example:

PostgreSQL

If connectionString is highest priority to use. If left unspecified then connection details will be determined using the individual connection fields (host, port, etc), and finally an SSL configuration will be enabled based on a truthy value of config["DB_SSL"] which will also accept self-signed certificates.

const pg = require('knex')({
+  client: 'pg',
+  connection: {
+    connectionString: config.DATABASE_URL,
+    host: config["DB_HOST"],
+    port: config["DB_PORT"],
+    user: config["DB_USER"],
+    database: config["DB_NAME"],
+    password: config["DB_PASSWORD"],
+    ssl: config["DB_SSL"] ? { rejectUnauthorized: false } : false,
+  }
+});
+

The following are SQLite usage patterns for instantiating the Knex configuration object:

SQLite3 or Better-SQLite3

When you use the SQLite3 or Better-SQLite3 adapter, there is a filename required, not a network connection. For example:

const knex = require('knex')({
+  client: 'sqlite3', // or 'better-sqlite3'
+  connection: {
+    filename: "./mydb.sqlite"
+  }
+});
+

You can also run either SQLite3 or Better-SQLite3 with an in-memory database by providing :memory: as the filename. For example:

const knex = require('knex')({
+  client: 'sqlite3', // or 'better-sqlite3'
+  connection: {
+    filename: ":memory:"
+  }
+});
+

SQLite3

When you use the SQLite3 adapter, you can set flags used to open the connection. For example:

const knex = require('knex')({
+  client: 'sqlite3',
+  connection: {
+    filename: "file:memDb1?mode=memory&cache=shared",
+    flags: ['OPEN_URI', 'OPEN_SHAREDCACHE']
+  }
+});
+

Better-SQLite3

With the Better-SQLite3 adapter, you can use options.nativeBinding to specify the location of the adapter's compiled C++ addon. This can be useful when your build system does a lot of transformation/relocation of files.

Example use:

const knex = require('knex')({
+  client: 'better-sqlite3',
+  connection: {
+    filename: ":memory:",
+    options: {
+      nativeBinding: "/path/to/better_sqlite3.node",
+    },
+  },
+});
+

Additionally, you can open the database in read-only mode using options.readonly:

const knex = require('knex')({
+  client: 'better-sqlite3',
+  connection: {
+    filename: "/path/to/db.sqlite3",
+    options: {
+      readonly: true,
+    },
+  },
+});
+

For more information, see the Better-SQLite3 documentation on database connection options.

MSSQL

When you use the MSSQL client, you can define a mapBinding function to define your own logic for mapping from knex query parameters to tedious types. Returning undefined from the function will fallback to the default mapping.

import { TYPES } from 'tedious';
+
+const knex = require('knex')({
+  client: 'mssql',
+  connection: {
+    options: {
+      mapBinding: value => {
+        // bind all strings to varchar instead of nvarchar
+        if (typeof value === 'string') {
+          return {
+            type: TYPES.VarChar,
+            value
+          };
+        }
+
+        // allow devs to pass tedious type at query time
+        if (value != null && value.type) {
+          return {
+            type: value.type,
+            value: value.value
+          };
+        }
+
+        // undefined is returned; falling back to default mapping function
+      }
+    }
+  }
+});
+

INFO

The database version can be added in knex configuration, when you use the PostgreSQL adapter to connect a non-standard database.

const knex = require('knex')({
+  client: 'pg',
+  version: '7.2',
+  connection: {
+    host : '127.0.0.1',
+    port : 5432,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  }
+});
+
const knex = require('knex')({
+  client: 'mysql',
+  version: '5.7',
+  connection: {
+    host : '127.0.0.1',
+    port : 3306,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  }
+});
+

INFO

When using a custom PostgreSQL client like knex-aurora-data-api-client, you can explicitly state if it supports jsonb column types

const knex = require('knex')({
+    client: require('knex-aurora-data-api-client').postgres,
+    connection: { resourceArn, secretArn, database: \`mydb\` },
+    version: 'data-api',
+    jsonbSupport: true
+})
+

A function can be used to determine the connection configuration dynamically. This function receives no parameters, and returns either a configuration object or a promise for a configuration object.

const knex = require('knex')({
+  client: 'sqlite3',
+  connection: () => ({
+    filename: process.env.SQLITE_FILENAME
+  })
+});
+

By default, the configuration object received via a function is cached and reused for all connections. To change this behavior, an expirationChecker function can be returned as part of the configuration object. The expirationChecker is consulted before trying to create new connections, and in case it returns true, a new configuration object is retrieved. For example, to work with an authentication token that has a limited lifespan:

const knex = require('knex')({
+  client: 'postgres',
+  connection: async () => {
+    const { 
+      token, 
+      tokenExpiration 
+    } = await someCallToGetTheToken();
+
+    return {
+      host : 'your_host',
+      port : 5432,
+      user : 'your_database_user',
+      password : token,
+      database : 'myapp_test',
+      expirationChecker: () => {
+        return tokenExpiration <= Date.now();
+      }
+    };
+  }
+});
+

You can also connect via a unix domain socket, which will ignore host and port.

const knex = require('knex')({
+  client: 'mysql',
+  connection: {
+    socketPath : '/path/to/socket.sock',
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  }
+});
+

userParams is an optional parameter that allows you to pass arbitrary parameters which will be accessible via knex.userParams property:

const knex = require('knex')({
+  client: 'mysql',
+  connection: {
+    host : '127.0.0.1',
+    port : 3306,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  },
+  userParams: {
+    userParam1: '451'
+  }
+});
+

Initializing the library should normally only ever happen once in your application, as it creates a connection pool for the current database, you should use the instance returned from the initialize call throughout your library.

Specify the client for the particular flavour of SQL you are interested in.

const pg = require('knex')({client: 'pg'});
+
+knex('table')
+  .insert({a: 'b'})
+  .returning('*')
+  .toString();
+// "insert into "table" ("a") values ('b')"
+
+pg('table')
+  .insert({a: 'b'})
+  .returning('*')
+  .toString();
+// "insert into "table" ("a") values ('b') returning *"
+

withUserParams

You can call method withUserParams on a Knex instance if you want to get a copy (with same connections) with custom parameters (e. g. to execute same migrations with different parameters)

const knex = require('knex')({
+  // Params
+});
+
+const knexWithParams = knex.withUserParams({ 
+  customUserParam: 'table1'
+});
+const customUserParam = knexWithParams
+  .userParams
+  .customUserParam;
+

debug

Passing a debug: true flag on your initialization object will turn on debugging for all queries.

asyncStackTraces

Passing an asyncStackTraces: true flag on your initialization object will turn on stack trace capture for all query builders, raw queries and schema builders. When a DB driver returns an error, this previously captured stack trace is thrown instead of a new one. This helps to mitigate default behaviour of await in node.js/V8 which blows the stack away. This has small performance overhead, so it is advised to use only for development. Turned off by default.

pool

The client created by the configuration initializes a connection pool, using the tarn.js library. This connection pool has a default setting of a min: 2, max: 10 for the MySQL and PG libraries, and a single connection for sqlite3 (due to issues with utilizing multiple connections on a single file). To change the config settings for the pool, pass a pool option as one of the keys in the initialize block.

Note that the default value of min is 2 only for historical reasons. It can result in problems with stale connections, despite tarn's default idle connection timeout of 30 seconds, which is only applied when there are more than min active connections. It is recommended to set min: 0 so all idle connections can be terminated.

Checkout the tarn.js library for more information.

const knex = require('knex')({
+  client: 'mysql',
+  connection: {
+    host : '127.0.0.1',
+    port : 3306,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  },
+  pool: { min: 0, max: 7 }
+});
+

If you ever need to explicitly teardown the connection pool, you may use knex.destroy([callback]). You may use knex.destroy by passing a callback, or by chaining as a promise, just not both. To manually initialize a destroyed connection pool, you may use knex.initialize([config]), if no config is passed, it will use the first knex configuration used.

afterCreate

afterCreate callback (rawDriverConnection, done) is called when the pool aquires a new connection from the database server. done(err, connection) callback must be called for knex to be able to decide if the connection is ok or if it should be discarded right away from the pool.

const knex = require('knex')({
+  client: 'pg',
+  connection: {/*...*/},
+  pool: {
+    afterCreate: function (conn, done) {
+      // in this example we use pg driver's connection API
+      conn.query('SET timezone="UTC";', function (err) {
+        if (err) {
+          // first query failed, 
+          // return error and don't try to make next query
+          done(err, conn);
+        } else {
+          // do the second query...
+          conn.query(
+            'SELECT set_limit(0.01);', 
+            function (err) {
+              // if err is not falsy, 
+              //  connection is discarded from pool
+              // if connection aquire was triggered by a 
+              // query the error is passed to query promise
+              done(err, conn);
+            });
+        }
+      });
+    }
+  }
+});
+

acquireConnectionTimeout

acquireConnectionTimeout defaults to 60000ms and is used to determine how long knex should wait before throwing a timeout error when acquiring a connection is not possible. The most common cause for this is using up all the pool for transaction connections and then attempting to run queries outside of transactions while the pool is still full. The error thrown will provide information on the query the connection was for to simplify the job of locating the culprit.

const knex = require('knex')({
+  client: 'pg',
+  connection: {/*...*/},
+  pool: {/*...*/},
+  acquireConnectionTimeout: 10000
+});
+

fetchAsString

Utilized by Oracledb. An array of types. The valid types are 'DATE', 'NUMBER' and 'CLOB'. When any column having one of the specified types is queried, the column data is returned as a string instead of the default representation.

const knex = require('knex')({
+  client: 'oracledb',
+  connection: {/*...*/},
+  fetchAsString: [ 'number', 'clob' ]
+});
+

migrations

For convenience, any migration configuration may be specified when initializing the library. Read the Migrations section for more information and a full list of configuration options.

const knex = require('knex')({
+  client: 'mysql',
+  connection: {
+    host : '127.0.0.1',
+    port : 3306,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  },
+  migrations: {
+    tableName: 'migrations'
+  }
+});
+

postProcessResponse

Hook for modifying returned rows, before passing them forward to user. One can do for example snake_case -> camelCase conversion for returned columns with this hook. The queryContext is only available if configured for a query builder instance via queryContext.

const knex = require('knex')({
+  client: 'mysql',
+  // overly simplified snake_case -> camelCase converter
+  postProcessResponse: (result, queryContext) => {
+    // TODO: add special case for raw results 
+    // (depends on dialect)
+    if (Array.isArray(result)) {
+      return result.map(row => convertToCamel(row));
+    } else {
+      return convertToCamel(result);
+    }
+  }
+});
+

wrapIdentifier

Knex supports transforming identifier names automatically to quoted versions for each dialect. For example 'Table.columnName as foo' for PostgreSQL is converted to "Table"."columnName" as "foo".

With wrapIdentifier one may override the way how identifiers are transformed. It can be used to override default functionality and for example to help doing camelCase -> snake_case conversion.

Conversion function wrapIdentifier(value, dialectImpl, context): string gets each part of the identifier as a single value, the original conversion function from the dialect implementation and the queryContext, which is only available if configured for a query builder instance via builder.queryContext, and for schema builder instances via schema.queryContext or table.queryContext. For example, with the query builder, knex('table').withSchema('foo').select('table.field as otherName').where('id', 1) will call wrapIdentifier converter for following values 'table', 'foo', 'table', 'field', 'otherName' and 'id'.

const knex = require('knex')({
+  client: 'mysql',
+  // overly simplified camelCase -> snake_case converter
+  wrapIdentifier: (
+    value, 
+    origImpl, 
+    queryContext
+  ) => origImpl(convertToSnakeCase(value))
+});
+

log

Knex contains some internal log functions for printing warnings, errors, deprecations, and debug information when applicable. These log functions typically log to the console, but can be overwritten using the log option and providing alternative functions. Different log functions can be used for separate knex instances.

const knex = require('knex')({
+  log: {
+    warn(message) {
+    },
+    error(message) {
+    },
+    deprecate(message) {
+    },
+    debug(message) {
+    },
+  }
+});
+

compileSqlOnError

Knex builds an error message in case of query error. By default Knex adds compiled SQL (SELECT * FROM users WHERE password = 'myPassword') to the error message. This can be changed to parameterized SQL (SELECT * FROM users WHERE password = ?) by setting compileSqlOnError to false.

const knex = require('knex')({
+  compileSqlOnError: false
+});
+

TypeScript

While knex is written in JavaScript, officially supported TypeScript bindings are available (within the knex npm package).

However it is to be noted that TypeScript support is currently best-effort. Knex has a very flexible API and not all usage patterns can be type-checked and in most such cases we err on the side of flexibility. In particular, lack of type errors doesn't currently guarantee that the generated queries will be correct and therefore writing tests for them is recommended even if you are using TypeScript.

Many of the APIs accept TRecord and TResult type parameters, using which we can specify the type of a row in the database table and the type of the result of the query respectively. This is helpful for auto-completion when using TypeScript-aware editors like VSCode.

To reduce boilerplate and add inferred types, you can augment Tables interface in 'knex/types/tables' module.

import { Knex } from 'knex';
+
+declare module 'knex/types/tables' {
+  interface User {
+    id: number;
+    name: string;
+    created_at: string;
+    updated_at: string;
+  }
+  
+  interface Tables {
+    // This is same as specifying \`knex<User>('users')\`
+    users: User;
+    // For more advanced types, you can specify separate type
+    // for base model, "insert" type and "update" type.
+    // But first: notice that if you choose to use this, 
+    // the basic typing showed above can be ignored.
+    // So, this is like specifying
+    //    knex
+    //    .insert<{ name: string }>({ name: 'name' })
+    //    .into<{ name: string, id: number }>('users')
+    users_composite: Knex.CompositeTableType<
+      // This interface will be used for return type and 
+      // \`where\`, \`having\` etc where full type is required 
+      User,
+      // Specifying "insert" type will also make sure
+      // data matches interface in full. Meaning
+      // if interface is \`{ a: string, b: string }\`,
+      // \`insert({ a: '' })\` will complain about missing fields.
+      // 
+      // For example, this will require only "name" field when inserting
+      // and make created_at and updated_at optional.
+      // And "id" can't be provided at all.
+      // Defaults to "base" type.
+      Pick<User, 'name'> & Partial<Pick<User, 'created_at' | 'updated_at'>>,
+      // This interface is used for "update()" calls.
+      // As opposed to regular specifying interface only once,
+      // when specifying separate update interface, user will be
+      // required to match it  exactly. So it's recommended to
+      // provide partial interfaces for "update". Unless you want to always
+      // require some field (e.g., \`Partial<User> & { updated_at: string }\`
+      // will allow updating any field for User but require updated_at to be
+      // always provided as well.
+      // 
+      // For example, this wil allow updating all fields except "id".
+      // "id" will still be usable for \`where\` clauses so
+      //      knex('users_composite')
+      //      .update({ name: 'name2' })
+      //      .where('id', 10)\`
+      // will still work.
+      // Defaults to Partial "insert" type
+      Partial<Omit<User, 'id'>>
+    >;
+  }
+}
+
`,79),o=[e];function c(r,l,i,u,k,d){return a(),s("div",null,o)}var m=n(p,[["render",c]]);export{g as __pageData,m as default}; diff --git a/assets/guide_index.md.895872e8.lean.js b/assets/guide_index.md.895872e8.lean.js new file mode 100644 index 00000000..9e050723 --- /dev/null +++ b/assets/guide_index.md.895872e8.lean.js @@ -0,0 +1 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const g='{"title":"Installation","description":"","frontmatter":{},"headers":[{"level":2,"title":"Node.js","slug":"node-js"},{"level":2,"title":"Browser","slug":"browser"},{"level":2,"title":"Configuration Options","slug":"configuration-options"},{"level":3,"title":"withUserParams","slug":"withuserparams"},{"level":3,"title":"debug","slug":"debug"},{"level":3,"title":"asyncStackTraces","slug":"asyncstacktraces"},{"level":3,"title":"pool","slug":"pool"},{"level":3,"title":"afterCreate","slug":"aftercreate"},{"level":3,"title":"acquireConnectionTimeout","slug":"acquireconnectiontimeout"},{"level":3,"title":"fetchAsString","slug":"fetchasstring"},{"level":3,"title":"migrations","slug":"migrations"},{"level":3,"title":"postProcessResponse","slug":"postprocessresponse"},{"level":3,"title":"wrapIdentifier","slug":"wrapidentifier"},{"level":3,"title":"log","slug":"log"},{"level":3,"title":"compileSqlOnError","slug":"compilesqlonerror"},{"level":2,"title":"TypeScript","slug":"typescript"}],"relativePath":"guide/index.md"}',p={},e=t("",79),o=[e];function c(r,l,i,u,k,d){return a(),s("div",null,o)}var m=n(p,[["render",c]]);export{g as __pageData,m as default}; diff --git a/assets/guide_interfaces.md.da877da3.js b/assets/guide_interfaces.md.da877da3.js new file mode 100644 index 00000000..c126d606 --- /dev/null +++ b/assets/guide_interfaces.md.da877da3.js @@ -0,0 +1,160 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const f='{"title":"Interfaces","description":"","frontmatter":{},"headers":[{"level":2,"title":"Promises","slug":"promises"},{"level":3,"title":"then","slug":"then"},{"level":3,"title":"catch","slug":"catch"},{"level":2,"title":"Callbacks","slug":"callbacks"},{"level":3,"title":"asCallback","slug":"ascallback"},{"level":2,"title":"Streams","slug":"streams"},{"level":3,"title":"stream","slug":"stream"},{"level":3,"title":"pipe","slug":"pipe"},{"level":2,"title":"Events","slug":"events"},{"level":3,"title":"query","slug":"query"},{"level":3,"title":"query-error","slug":"query-error"},{"level":3,"title":"query-response","slug":"query-response"},{"level":3,"title":"start","slug":"start"},{"level":2,"title":"Other","slug":"other"},{"level":3,"title":"toString","slug":"tostring"},{"level":3,"title":"toSQL","slug":"tosql"}],"relativePath":"guide/interfaces.md"}',p={},o=t(`

Interfaces

Knex.js provides several options to deal with query output. The following methods are present on the query builder, schema builder, and the raw builder:

Promises

Promises are the preferred way of dealing with queries in knex, as they allow you to return values from a fulfillment handler, which in turn become the value of the promise. The main benefit of promises are the ability to catch thrown errors without crashing the node app, making your code behave like a .try / .catch / .finally in synchronous code.

knex.select('name')
+  .from('users')
+  .where('id', '>', 20)
+  .andWhere('id', '<', 200)
+  .limit(10)
+  .offset(x)
+  .then(function(rows) {
+    return _.pluck(rows, 'name');
+  })
+  .then(function(names) {
+    return knex.select('id')
+      .from('nicknames')
+      .whereIn('nickname', names);
+  })
+  .then(function(rows) {
+    console.log(rows);
+  })
+  .catch(function(error) {
+    console.error(error)
+  });
+

then

.then(onFulfilled, [onRejected])

Coerces the current query builder chain into a promise state, accepting the resolve and reject handlers as specified by the Promises/A+ spec. As stated in the spec, more than one call to the then method for the current query chain will resolve with the same value, in the order they were called; the query will not be executed multiple times.

knex.select('*')
+  .from('users')
+  .where({name: 'Tim'})
+  .then(function(rows) {
+    return knex
+      .insert({user_id: rows[0].id, name: 'Test'}, 'id')
+      .into('accounts');
+  })
+  .then(function(id) {
+    console.log('Inserted Account ' + id);
+  })
+  .catch(function(error) { console.error(error); });
+

catch

.catch(onRejected)

Coerces the current query builder into a promise state, catching any error thrown by the query, the same as calling .then(null, onRejected).

return knex.insert({id: 1, name: 'Test'}, 'id')
+  .into('accounts')
+  .catch(function(error) {
+    console.error(error);
+  })
+  .then(function() {
+    return knex.select('*')
+      .from('accounts')
+      .where('id', 1);
+  })
+  .then(function(rows) {
+    console.log(rows[0]);
+  })
+  .catch(function(error) {
+    console.error(error);
+  });
+

Callbacks

asCallback

.asCallback(callback)

If you'd prefer a callback interface over promises, the asCallback function accepts a standard node style callback for executing the query chain. Note that as with the then method, subsequent calls to the same query chain will return the same result.

knex.select('name').from('users')
+  .where('id', '>', 20)
+  .andWhere('id', '<', 200)
+  .limit(10)
+  .offset(x)
+  .asCallback(function(err, rows) {
+    if (err) return console.error(err);
+    knex.select('id')
+      .from('nicknames')
+      .whereIn('nickname', _.pluck(rows, 'name'))
+      .asCallback(function(err, rows) {
+        if (err) return console.error(err);
+        console.log(rows);
+      });
+  });
+

Streams

Streams are a powerful way of piping data through as it comes in, rather than all at once. You can read more about streams here at substack's stream handbook. See the following for example uses of stream & pipe. If you wish to use streams with PostgreSQL, you must also install the pg-query-stream module. If you wish to use streams with the pgnative dialect, please be aware that the results will not be streamed as they are received, but rather streamed after the entire result set has returned. On an HTTP server, make sure to manually close your streams if a request is aborted.

stream

.stream([options], [callback])

If called with a callback, the callback is passed the stream and a promise is returned. Otherwise, the readable stream is returned.
When the stream is consumed as an iterator, if the loop terminates with a break, return, or a throw, the stream will be destroyed. In other terms, iterating over a stream will consume the stream fully.

// Retrieve the stream:
+const stream = knex.select('*')
+  .from('users')
+  .stream();
+stream.pipe(writableStream);
+
// With options:
+const stream = knex.select('*')
+  .from('users')
+  .stream({highWaterMark: 5});
+stream.pipe(writableStream);
+
// Use as an iterator
+const stream = knex.select('*')
+  .from('users')
+  .stream();
+
+for await (const row of stream) {
+  /* ... */
+}
+
// Use as a promise:
+const stream = knex.select('*')
+  .from('users')
+  .where(knex.raw('id = ?', [1]))
+  .stream(function(stream) {
+    stream.pipe(writableStream);
+  })
+  .then(function() { /* ... */ })
+  .catch(function(e) { console.error(e); });
+

pipe

.pipe(writableStream)

Pipe a stream for the current query to a writableStream.

const stream = knex.select('*')
+  .from('users')
+  .pipe(writableStream);
+

Events

query

A query event is fired just before a query takes place, providing data about the query, including the connection's __knexUid / __knexTxId properties and any other information about the query as described in toSQL. Useful for logging all queries throughout your application.

knex.select('*')
+  .from('users')
+  .on('query', function(data) {
+    app.log(data);
+  })
+  .then(function() {
+    // ...
+  });
+

query-error

A query-error event is fired when an error occurs when running a query, providing the error object and data about the query, including the connection's __knexUid / __knexTxId properties and any other information about the query as described in toSQL. Useful for logging all query errors throughout your application.

knex.select(['NonExistentColumn'])
+  .from('users')
+  .on('query-error', function(error, obj) {
+    app.log(error);
+  })
+  .then(function() { /* ... */ })
+  .catch(function(error) {
+    // Same error object as the query-error event provides.
+  });
+

query-response

A query-response event is fired when a successful query has been run, providing the response of the query and data about the query, including the connection's __knexUid / __knexTxId properties and any other information about the query as described in toSQL, and finally the query builder used for the query.

knex.select('*')
+  .from('users')
+  .on('query-response', function(response, obj, builder) {
+    // ...
+  })
+  .then(function(response) {
+    // Same response as the emitted event
+  })
+  .catch(function(error) { });
+

start

A start event is fired right before a query-builder is compiled.

INFO

While this event can be used to alter a builders state prior to compilation it is not to be recommended. Future goals include ways of doing this in a different manner such as hooks.

knex.select('*')
+  .from('users')
+  .on('start', function(builder) {
+    builder
+    .where('IsPrivate', 0)
+  })
+  .then(function(Rows) {
+    //Only contains Rows where IsPrivate = 0
+  })
+  .catch(function(error) { });
+

Other

toString

.toString()

Returns an array of query strings filled out with the correct values based on bindings, etc. Useful for debugging, but should not be used to create queries for running them against DB.

const toStringQuery = knex.select('*')
+  .from('users')
+  .where('id', 1)
+  .toString();
+
+// Outputs: console.log(toStringQuery); 
+// select * from "users" where "id" = 1
+

toSQL

.toSQL()
.toSQL().toNative()

Returns an array of query strings filled out with the correct values based on bindings, etc. Useful for debugging and building queries for running them manually with DB driver. .toSQL().toNative() outputs object with sql string and bindings in a dialects format in the same way that knex internally sends them to underlying DB driver.

knex.select('*')
+  .from('users')
+  .where(knex.raw('id = ?', [1]))
+  .toSQL()
+// Outputs:
+// {
+//   bindings: [1],
+//   method: 'select',
+//   sql: 'select * from "users" where id = ?',
+//   options: undefined,
+//   toNative: function () {}
+// }
+
+knex.select('*')
+  .from('users')
+  .where(knex.raw('id = ?', [1]))
+  .toSQL()
+  .toNative()
+// Outputs for postgresql dialect:
+// {
+//   bindings: [1],
+//   sql: 'select * from "users" where id = $1',
+// }
+
`,54),e=[o];function c(u,l,i,r,k,d){return a(),s("div",null,e)}var m=n(p,[["render",c]]);export{f as __pageData,m as default}; diff --git a/assets/guide_interfaces.md.da877da3.lean.js b/assets/guide_interfaces.md.da877da3.lean.js new file mode 100644 index 00000000..c0a2dab3 --- /dev/null +++ b/assets/guide_interfaces.md.da877da3.lean.js @@ -0,0 +1 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const f='{"title":"Interfaces","description":"","frontmatter":{},"headers":[{"level":2,"title":"Promises","slug":"promises"},{"level":3,"title":"then","slug":"then"},{"level":3,"title":"catch","slug":"catch"},{"level":2,"title":"Callbacks","slug":"callbacks"},{"level":3,"title":"asCallback","slug":"ascallback"},{"level":2,"title":"Streams","slug":"streams"},{"level":3,"title":"stream","slug":"stream"},{"level":3,"title":"pipe","slug":"pipe"},{"level":2,"title":"Events","slug":"events"},{"level":3,"title":"query","slug":"query"},{"level":3,"title":"query-error","slug":"query-error"},{"level":3,"title":"query-response","slug":"query-response"},{"level":3,"title":"start","slug":"start"},{"level":2,"title":"Other","slug":"other"},{"level":3,"title":"toString","slug":"tostring"},{"level":3,"title":"toSQL","slug":"tosql"}],"relativePath":"guide/interfaces.md"}',p={},o=t("",54),e=[o];function c(u,l,i,r,k,d){return a(),s("div",null,e)}var m=n(p,[["render",c]]);export{f as __pageData,m as default}; diff --git a/assets/guide_migrations.md.8f3715e4.js b/assets/guide_migrations.md.8f3715e4.js new file mode 100644 index 00000000..4d2ecf59 --- /dev/null +++ b/assets/guide_migrations.md.8f3715e4.js @@ -0,0 +1,292 @@ +import{_ as n,c as s,o as a,a as e}from"./app.ef097145.js";const g='{"title":"Migrations","description":"","frontmatter":{},"headers":[{"level":2,"title":"Migration CLI","slug":"migration-cli"},{"level":2,"title":"Seed files","slug":"seed-files"},{"level":3,"title":"Seed CLI","slug":"seed-cli"},{"level":2,"title":"knexfile.js","slug":"knexfile-js"},{"level":3,"title":"Basic configuration","slug":"basic-configuration"},{"level":3,"title":"Environment configuration","slug":"environment-configuration"},{"level":3,"title":"Custom migration","slug":"custom-migration"},{"level":3,"title":"Custom migration name","slug":"custom-migration-name"},{"level":3,"title":"Generated migration extension","slug":"generated-migration-extension"},{"level":3,"title":"Knexfile in other languages","slug":"knexfile-in-other-languages"},{"level":2,"title":"Migration API","slug":"migration-api"},{"level":3,"title":"Transactions in migrations","slug":"transactions-in-migrations"},{"level":3,"title":"make","slug":"make"},{"level":3,"title":"latest","slug":"latest"},{"level":3,"title":"rollback","slug":"rollback"},{"level":3,"title":"up","slug":"up"},{"level":3,"title":"down","slug":"down"},{"level":3,"title":"currentVersion","slug":"currentversion"},{"level":3,"title":"list","slug":"list"},{"level":3,"title":"unlock","slug":"unlock"},{"level":2,"title":"Notes about locks","slug":"notes-about-locks"},{"level":2,"title":"Custom migration sources","slug":"custom-migration-sources"},{"level":3,"title":"Webpack migration source example","slug":"webpack-migration-source-example"},{"level":2,"title":"ECMAScript modules (ESM) Interoperability","slug":"ecmascript-modules-esm-interoperability"},{"level":2,"title":"Seed API","slug":"seed-api"},{"level":3,"title":"make","slug":"make-1"},{"level":3,"title":"run","slug":"run"},{"level":2,"title":"Custom seed sources","slug":"custom-seed-sources"}],"relativePath":"guide/migrations.md"}',t={},o=e(`

Migrations

Migrations allow for you to define sets of schema changes so upgrading a database is a breeze.

Migration CLI

The migration CLI is bundled with the knex install, and is driven by the node-liftoff module. To install globally, run:

$ npm install knex -g
+

The migration CLI accepts the following general command-line options. You can view help text and additional options for each command using --help. E.g. knex migrate:latest --help.

  • --debug: Run with debugging
  • --knexfile [path]: Specify the knexfile path
  • --knexpath [path]: Specify the path to the knex instance
  • --cwd [path]: Specify the working directory
  • --client [name]: Set the DB client
  • --connection [address]: Set the DB connection
  • --migrations-table-name: Set the migration table name
  • --migrations-directory: Set the migrations directory
  • --env: environment, default: process.env.NODE_ENV || development
  • --esm: Enables ESM module interoperability
  • --help: Display help text for a particular command and exit.

Migrations use a knexfile, which specify various configuration settings for the module. To create a new knexfile, run the following:

$ knex init
+
+# or for .ts
+
+$ knex init -x ts
+

will create a sample knexfile.js - the file which contains our various database configurations. Once you have a knexfile.js, you can use the migration tool to create migration files to the specified directory (default migrations). Creating new migration files can be achieved by running:

$ knex migrate:make migration_name 
+
+# or for .ts
+
+$ knex migrate:make migration_name -x ts
+
  • you can also create your migration using a specific stub file, this serves as a migration template to speed up development for common migration operations
  • if the --stub option is not passed, the CLI will use either the knex default stub for the chosen extension, or the config.stub file
$ knex migrate:make --stub 
+
+# or
+
+$ knex migrate:make --stub 
+
  • if a stub path is provided, it must be relative to the knexfile.[js, ts, etc] location
  • if a is used, the stub is selected by its file name. The CLI will look for this file in the config.migrations.directory folder. If the config.migrations.directory is not defined, this operation will fail

Once you have finished writing the migrations, you can update the database matching your NODE_ENV by running:

$ knex migrate:latest
+

You can also pass the --env flag or set NODE_ENV to select an alternative environment:

$ knex migrate:latest --env production
+
+# or
+
+$ NODE_ENV=production knex migrate:latest
+

To rollback the last batch of migrations:

$ knex migrate:rollback
+

To rollback all the completed migrations:

$ knex migrate:rollback --all
+

To run the next migration that has not yet been run

$ knex migrate:up
+

To run the specified migration that has not yet been run

$ knex migrate:up 001_migration_name.js
+

To undo the last migration that was run

$ knex migrate:down
+

To undo the specified migration that was run

$ knex migrate:down 001_migration_name.js
+

To list both completed and pending migrations:

$ knex migrate:list
+

Seed files

Seed files allow you to populate your database with test or seed data independent of your migration files.

Seed CLI

To create a seed file, run:

$ knex seed:make seed_name
+

Seed files are created in the directory specified in your knexfile.js for the current environment. A sample seed configuration looks like:

module.exports = {
+  // ...
+  development: {
+    client: {/* ... */},
+    connection: {/* ... */},
+    seeds: {
+        directory: './seeds/dev'
+    }
+  }
+  // ...
+  }
+

If no seeds.directory is defined, files are created in ./seeds. Note that the seed directory needs to be a relative path. Absolute paths are not supported (nor is it good practice).

To run seed files, execute:

$ knex seed:run
+

Seed files are executed in alphabetical order. Unlike migrations, every seed file will be executed when you run the command. You should design your seed files to reset tables as needed before inserting data.

To run specific seed files, execute:

$ knex seed:run --specific=seed-filename.js --specific=another-seed-filename.js
+

knexfile.js

A knexfile.js generally contains all of the configuration for your database. It can optionally provide different configuration for different environments. You may pass a --knexfile option to any of the command line statements to specify an alternate path to your knexfile.

Basic configuration

module.exports = {
+  client: 'pg',
+  connection: process.env.DATABASE_URL || { 
+    user: 'me', 
+    database: 'my_app' 
+  }
+};
+

You can also use an async function to get connection details for your configuration. This is useful when you need to fetch credentials from a secure location like vault.

const getPassword = async () => {
+  // TODO: implement me
+  return 'my_pass'
+}
+
+module.exports = {
+  client: 'pg',
+  connection: async () => {
+    const password = await getPassword()
+    return { user: 'me', password }
+  },
+  migrations: {}
+};
+

Environment configuration

module.exports = {
+  development: {
+    client: 'pg',
+    connection: { user: 'me', database: 'my_app' }
+  },
+  production: { 
+    client: 'pg', 
+    connection: process.env.DATABASE_URL 
+  }
+};
+

Custom migration

You may provide a custom migration stub to be used in place of the default option.

module.exports = {
+  client: 'pg',
+  migrations: {
+    stub: 'migration.stub'
+  }
+};
+

Custom migration name

You may provide a custom migration name to be used in place of the default option.

module.exports = {
+  client: 'pg',
+  migrations: {
+    getNewMigrationName: (name) => {
+      return \`\${+new Date()}-\${name}.js\`;
+    }
+  }
+};
+

Generated migration extension

You can control extension of generated migrations.

module.exports = {
+  client: 'pg',
+  migrations: {
+    extension: 'ts'
+  }
+};
+

Knexfile in other languages

Knex uses Liftoff to support knexfile written in other compile-to-js languages.

Depending on the language, this may require you to install additional dependencies. The complete list of dependencies for each supported language can be found here.

Most common cases are typescript (for which typescript and ts-node packages are recommended), and coffeescript (for which coffeescript dependency is required).

If you don't specify the extension explicitly, the extension of generated migrations/seed files will be inferred from the knexfile extension

Migration API

knex.migrate is the class utilized by the knex migrations cli.

Each method takes an optional config object, which may specify the following properties:

  • directory: a relative path to the directory containing the migration files. Can be an array of paths (default ./migrations)
  • extension: the file extension used for the generated migration files (default js)
  • tableName: the table name used for storing the migration state (default knex_migrations)
  • schemaName: the schema name used for storing the table with migration state (optional parameter, only works on DBs that support multiple schemas in a single DB, such as PostgreSQL)
  • disableTransactions: don't run migrations inside transactions (default false)
  • disableMigrationsListValidation: do not validate that all the already executed migrations are still present in migration directories (default false)
  • sortDirsSeparately: if true and multiple directories are specified, all migrations from a single directory will be executed before executing migrations in the next folder (default false)
  • loadExtensions: array of file extensions which knex will treat as migrations. For example, if you have typescript transpiled into javascript in the same folder, you want to execute only javascript migrations. In this case, set loadExtensions to ['.js'] (Notice the dot!) (default ['.co', '.coffee', '.eg', '.iced', '.js', '.litcoffee', '.ls', '.ts'])
  • migrationSource: specify a custom migration source, see Custom Migration Source for more info (default filesystem)

Transactions in migrations

By default, each migration is run inside a transaction. Whenever needed, one can disable transactions for all migrations via the common migration config option config.disableTransactions or per-migration, via exposing a boolean property config.transaction from a migration file:

exports.up = function(knex) {
+  return knex.schema
+    .createTable('users', function (table) {
+        table.increments('id');
+        table.string('first_name', 255).notNullable();
+        table.string('last_name', 255).notNullable();
+    })
+    .createTable('products', function (table) {
+        table.increments('id');
+        table.decimal('price').notNullable();
+        table.string('name', 1000).notNullable();
+    });
+};
+
+exports.down = function(knex) {
+  return knex.schema
+      .dropTable("products")
+      .dropTable("users");
+};
+
+exports.config = { transaction: false };
+

The same config property can be used for enabling transaction per-migration in case the common configuration has disableTransactions: true.

make

knex.migrate.make(name, [config])

Creates a new migration, with the name of the migration being added.

latest

knex.migrate.latest([config])

Runs all migrations that have not yet been run.

If you need to run something only after all migrations have finished their execution, you can do something like this:

knex.migrate.latest()
+  .then(function() {
+    return knex.seed.run();
+  })
+  .then(function() {
+    // migrations are finished
+  });
+

rollback

knex.migrate.rollback([config], [all])

Rolls back the latest migration group. If the all parameter is truthy, all applied migrations will be rolled back instead of just the last batch. The default value for this parameter is false.

up

knex.migrate.up([config])

Runs the specified (by config.name parameter) or the next chronological migration that has not yet be run.

down

knex.migrate.down([config])

Will undo the specified (by config.name parameter) or the last migration that was run.

currentVersion

knex.migrate.currentVersion([config])

Retrieves and returns the current migration version, as a promise. If there aren't any migrations run yet, returns "none" as the value for the currentVersion.

list

knex.migrate.list([config])

Will return list of completed and pending migrations

unlock

knex.migrate.forceFreeMigrationsLock([config])

Forcibly unlocks the migrations lock table, and ensures that there is only one row in it.

Notes about locks

A lock system is there to prevent multiple processes from running the same migration batch in the same time. When a batch of migrations is about to be run, the migration system first tries to get a lock using a SELECT ... FOR UPDATE statement (preventing race conditions from happening). If it can get a lock, the migration batch will run. If it can't, it will wait until the lock is released.

Please note that if your process unfortunately crashes, the lock will have to be manually removed with knex migrate:unlock in order to let migrations run again.

The locks are saved in a table called "tableName_lock"; it has a column called is_locked that knex migrate:unlock sets to 0 in order to release the lock. The index column in the lock table exists for compatibility with some database clusters that require a primary key, but is otherwise unused. There must be only one row in this table, or an error will be thrown when running migrations: "Migration table is already locked". Run knex migrate:unlock to ensure that there is only one row in the table.

Custom migration sources

Knex supports custom migration sources, allowing you full control of where your migrations come from. This can be useful for custom folder structures, when bundling with webpack/browserify and other scenarios.

// Create a custom migration source class
+class MyMigrationSource {
+  // Must return a Promise containing a list of migrations. 
+  // Migrations can be whatever you want, 
+  // they will be passed as arguments to getMigrationName 
+  // and getMigration
+  getMigrations() {
+    // In this example we are just returning migration names
+    return Promise.resolve(['migration1'])
+  }
+
+  getMigrationName(migration) {
+    return migration;
+  }
+
+  getMigration(migration) {
+    switch(migration) {
+      case 'migration1':
+        return {
+          up(knex)   { /* ... */ },
+          down(knex) { /* ... */ },
+        }
+    }
+  }
+}
+
+// pass an instance of your migration source as knex config
+knex.migrate.latest({ 
+  migrationSource: new MyMigrationSource() 
+})
+

Webpack migration source example

An example of how to create a migration source where migrations are included in a webpack bundle.

const path = require('path')
+
+class WebpackMigrationSource {
+  constructor(migrationContext) {
+    this.migrationContext = migrationContext
+  }
+
+  getMigrations() {
+    return Promise.resolve(
+      this.migrationContext.keys().sort()
+    )
+  }
+
+  getMigrationName(migration) {
+    return path.parse(migration).base
+  }
+
+  getMigration(migration) {
+    return this.migrationContext(migration)
+  }
+}
+
+// pass an instance of your migration source as knex config
+knex.migrate.latest({
+  migrationSource: new WebpackMigrationSource(
+    require.context('./migrations', false, /.js$/)
+  )
+})
+
+// with webpack >=5, require.context will add 
+// both the relative and absolute paths to the context
+// to avoid duplicate migration errors, you'll need 
+// to filter out one or the other this example filters 
+// out absolute paths, leaving only the relative 
+// ones(./migrations/*.js):
+knex.migrate.latest({
+  migrationSource: new WebpackMigrationSource(
+    require.context('./migrations', false, /^\\.\\/.*\\.js$/)
+  )
+})
+

ECMAScript modules (ESM) Interoperability

ECMAScript Module support for knex CLI's configuration, migration and seeds
enabled by the --esm flag, ECMAScript Interoperability is provided by the 'esm' module.
You can find here more information about 'esm' superpowers.

Node 'mjs' files are handled by NodeJS own import mechanics
and do not require the use of the '--esm' flag.
But you might need it anyway for Node v10 under certain scenarios.
You can find details about NodeJS ECMAScript modules here

While it is possible to mix and match different module formats (extensions)
between your knexfile, seeds and migrations,
some format combinations will require specific NodeJS versions,
Notably mjs/cjs files will follow NodeJS import and require restrictions.
You can see here many possible scenarios,
and here some sample configurations

Node v10.* require the use of the '--experimental-module' flag in order to use the 'mjs' or 'cjs' extension.

# launching knex on Node v10 to use mjs/cjs modules
+node --experimental-modules ./node_modules/.bin/knex $@
+

When using migration and seed files with '.cjs' or '.mjs' extensions, you will need to specify that explicitly:

/** 
+ * knexfile.mjs
+ */
+export default {      
+  migrations: {
+    // ... client, connection,etc .... 
+    directory: './migrations',
+    loadExtensions: ['.mjs'] // 
+  }
+}
+

When using '.mjs' extensions for your knexfile and '.js' for the seeds/migrations, you will need to specify that explicitly.

/** 
+ * knexfile.mjs
+ */
+export default {      
+  migrations: {
+    // ... client, connection,etc .... 
+    directory: './migrations',
+    loadExtensions: ['.js'] // knex will search for 'mjs' file by default
+  }
+}
+

For the knexfile you can use a default export,
it will take precedence over named export.

/**
+ * filename: knexfile.js
+ * For the knexfile you can use a default export
+ **/        
+export default {
+  client: 'sqlite3',
+  connection: {
+    filename: '../test.sqlite3',
+  },
+  migrations: {
+    directory: './migrations',
+  },
+  seeds: {
+    directory: './seeds',
+  },
+}
+
+/**
+ * filename: knexfile.js
+ * Let knex find the configuration by providing named exports,
+ * but if exported a default, it will take precedence, and it will be used instead
+ **/
+const config = {
+  client: 'sqlite3',
+  connection: {
+    filename: '../test.sqlite3',
+  },
+  migrations: {
+    directory: './migrations',
+  },
+  seeds: {
+    directory: './seeds',
+  },
+};
+/** this will be used, it has precedence over named export */
+export default config;
+/** Named exports, will be used if you didn't provide a default export */
+export const { client, connection, migrations, seeds } = config;
+

Seed and migration files need to follow Knex conventions

// file: seed.js
+/** 
+ * Same as with the CommonJS modules
+ * You will need to export a "seed" named function.
+ * */
+export function seed(knex) {
+  // ... seed logic here
+}
+
+// file: migration.js
+/** 
+ * Same as the CommonJS version, the miration file should export 
+ * "up" and "down" named functions
+ */
+export function up(knex) {
+  // ... migration logic here
+}
+export function down(knex) {
+// ... migration logic here
+}
+

Seed API

knex.seed is the class utilized by the knex seed CLI.

Each method takes an optional config object, which may specify the following properties:

  • directory: a relative path to the directory containing the seed files. Can be an array of paths (default ./seeds)
  • loadExtensions: array of file extensions which knex will treat as seeds. For example, if you have typescript transpiled into javascript in the same folder, you want to execute only javascript seeds. In this case, set loadExtensions to ['.js'] (Notice the dot!) (default ['.co', '.coffee', '.eg', '.iced', '.js', '.litcoffee', '.ls', '.ts'])
  • recursive: if true, will find seed files recursively in the directory / directories specified
  • specific: a specific seed file or an array of seed files to run from the seeds directory, if its value is undefined it will run all the seeds (default undefined). If an array is specified, seed files will be run in the same order as the array
  • sortDirsSeparately: if true and multiple directories are specified, all seeds from a single directory will be executed before executing seeds in the next folder (default false)
  • seedSource: specify a custom seed source, see Custom Seed Source for more info (default filesystem)
  • extension: extension to be used for newly generated seeds (default js)
  • timestampFilenamePrefix: whether timestamp should be added as a prefix for newly generated seeds (default false)

make

knex.seed.make(name, [config])

Creates a new seed file, with the name of the seed file being added. If the seed directory config is an array of paths, the seed file will be generated in the latest specified.

run

knex.seed.run([config])

Runs all seed files for the current environment.

Custom seed sources

Knex supports custom seed sources, allowing you full control of where your seeds come from. This can be useful for custom folder structures, when bundling with webpack/browserify and other scenarios.

// Create a custom seed source class
+class MySeedSource {
+  // Must return a Promise containing a list of seeds. 
+  // Seeds can be whatever you want, they will be passed as
+  // arguments to getSeed
+  getSeeds() {
+    // In this example we are just returning seed names
+    return Promise.resolve(['seed1'])
+  }
+
+  getSeed(seed) {
+    switch(seed) {
+      case 'seed1':
+        return (knex) => { /* ... */ }
+    }
+  }
+}
+
+// pass an instance of your seed source as knex config
+knex.seed.run({ seedSource: new MySeedSource() })
+
`,138),p=[o];function c(i,l,r,u,k,d){return a(),s("div",null,p)}var h=n(t,[["render",c]]);export{g as __pageData,h as default}; diff --git a/assets/guide_migrations.md.8f3715e4.lean.js b/assets/guide_migrations.md.8f3715e4.lean.js new file mode 100644 index 00000000..18fa3a34 --- /dev/null +++ b/assets/guide_migrations.md.8f3715e4.lean.js @@ -0,0 +1 @@ +import{_ as n,c as s,o as a,a as e}from"./app.ef097145.js";const g='{"title":"Migrations","description":"","frontmatter":{},"headers":[{"level":2,"title":"Migration CLI","slug":"migration-cli"},{"level":2,"title":"Seed files","slug":"seed-files"},{"level":3,"title":"Seed CLI","slug":"seed-cli"},{"level":2,"title":"knexfile.js","slug":"knexfile-js"},{"level":3,"title":"Basic configuration","slug":"basic-configuration"},{"level":3,"title":"Environment configuration","slug":"environment-configuration"},{"level":3,"title":"Custom migration","slug":"custom-migration"},{"level":3,"title":"Custom migration name","slug":"custom-migration-name"},{"level":3,"title":"Generated migration extension","slug":"generated-migration-extension"},{"level":3,"title":"Knexfile in other languages","slug":"knexfile-in-other-languages"},{"level":2,"title":"Migration API","slug":"migration-api"},{"level":3,"title":"Transactions in migrations","slug":"transactions-in-migrations"},{"level":3,"title":"make","slug":"make"},{"level":3,"title":"latest","slug":"latest"},{"level":3,"title":"rollback","slug":"rollback"},{"level":3,"title":"up","slug":"up"},{"level":3,"title":"down","slug":"down"},{"level":3,"title":"currentVersion","slug":"currentversion"},{"level":3,"title":"list","slug":"list"},{"level":3,"title":"unlock","slug":"unlock"},{"level":2,"title":"Notes about locks","slug":"notes-about-locks"},{"level":2,"title":"Custom migration sources","slug":"custom-migration-sources"},{"level":3,"title":"Webpack migration source example","slug":"webpack-migration-source-example"},{"level":2,"title":"ECMAScript modules (ESM) Interoperability","slug":"ecmascript-modules-esm-interoperability"},{"level":2,"title":"Seed API","slug":"seed-api"},{"level":3,"title":"make","slug":"make-1"},{"level":3,"title":"run","slug":"run"},{"level":2,"title":"Custom seed sources","slug":"custom-seed-sources"}],"relativePath":"guide/migrations.md"}',t={},o=e("",138),p=[o];function c(i,l,r,u,k,d){return a(),s("div",null,p)}var h=n(t,[["render",c]]);export{g as __pageData,h as default}; diff --git a/assets/guide_query-builder.md.c2c99abe.js b/assets/guide_query-builder.md.c2c99abe.js new file mode 100644 index 00000000..cfc28e4e --- /dev/null +++ b/assets/guide_query-builder.md.c2c99abe.js @@ -0,0 +1,1412 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const h='{"title":"Knex Query Builder","description":"","frontmatter":{},"headers":[{"level":2,"title":"Identifier Syntax","slug":"identifier-syntax"},{"level":2,"title":"Common","slug":"common"},{"level":3,"title":"knex","slug":"knex"},{"level":3,"title":"select","slug":"select"},{"level":3,"title":"as","slug":"as"},{"level":3,"title":"column","slug":"column"},{"level":3,"title":"from","slug":"from"},{"level":3,"title":"fromRaw","slug":"fromraw"},{"level":3,"title":"with","slug":"with"},{"level":3,"title":"withRecursive","slug":"withrecursive"},{"level":3,"title":"withMaterialized","slug":"withmaterialized"},{"level":3,"title":"withNotMaterialized","slug":"withnotmaterialized"},{"level":3,"title":"withSchema","slug":"withschema"},{"level":3,"title":"jsonExtract","slug":"jsonextract"},{"level":3,"title":"jsonSet","slug":"jsonset"},{"level":3,"title":"jsonInsert","slug":"jsoninsert"},{"level":3,"title":"jsonRemove","slug":"jsonremove"},{"level":3,"title":"offset","slug":"offset"},{"level":3,"title":"limit","slug":"limit"},{"level":3,"title":"union","slug":"union"},{"level":3,"title":"unionAll","slug":"unionall"},{"level":3,"title":"intersect","slug":"intersect"},{"level":3,"title":"except","slug":"except"},{"level":3,"title":"insert","slug":"insert"},{"level":3,"title":"onConflict","slug":"onconflict"},{"level":3,"title":"upsert","slug":"upsert"},{"level":3,"title":"update","slug":"update"},{"level":3,"title":"updateFrom","slug":"updatefrom"},{"level":3,"title":"del / delete","slug":"del-delete"},{"level":3,"title":"using","slug":"using"},{"level":3,"title":"returning","slug":"returning"},{"level":3,"title":"transacting","slug":"transacting"},{"level":3,"title":"skipLocked","slug":"skiplocked"},{"level":3,"title":"noWait","slug":"nowait"},{"level":3,"title":"count","slug":"count"},{"level":3,"title":"min","slug":"min"},{"level":3,"title":"max","slug":"max"},{"level":3,"title":"sum","slug":"sum"},{"level":3,"title":"avg","slug":"avg"},{"level":3,"title":"increment","slug":"increment"},{"level":3,"title":"decrement","slug":"decrement"},{"level":3,"title":"truncate","slug":"truncate"},{"level":3,"title":"pluck","slug":"pluck"},{"level":3,"title":"first","slug":"first"},{"level":3,"title":"hintComment","slug":"hintcomment"},{"level":3,"title":"comment","slug":"comment"},{"level":3,"title":"clone","slug":"clone"},{"level":3,"title":"denseRank","slug":"denserank"},{"level":3,"title":"rank","slug":"rank"},{"level":3,"title":"rowNumber","slug":"rownumber"},{"level":3,"title":"partitionBy","slug":"partitionby"},{"level":3,"title":"modify","slug":"modify"},{"level":3,"title":"columnInfo","slug":"columninfo"},{"level":3,"title":"debug","slug":"debug"},{"level":3,"title":"connection","slug":"connection"},{"level":3,"title":"options","slug":"options"},{"level":3,"title":"queryContext","slug":"querycontext"},{"level":2,"title":"Where Clauses","slug":"where-clauses"},{"level":3,"title":"where","slug":"where"},{"level":3,"title":"whereNot","slug":"wherenot"},{"level":3,"title":"whereIn","slug":"wherein"},{"level":3,"title":"whereNotIn","slug":"wherenotin"},{"level":3,"title":"whereNull","slug":"wherenull"},{"level":3,"title":"whereNotNull","slug":"wherenotnull"},{"level":3,"title":"whereExists","slug":"whereexists"},{"level":3,"title":"whereNotExists","slug":"wherenotexists"},{"level":3,"title":"whereBetween","slug":"wherebetween"},{"level":3,"title":"whereNotBetween","slug":"wherenotbetween"},{"level":3,"title":"whereRaw","slug":"whereraw"},{"level":3,"title":"whereLike","slug":"wherelike"},{"level":3,"title":"whereILike","slug":"whereilike"},{"level":3,"title":"whereJsonObject","slug":"wherejsonobject"},{"level":3,"title":"whereJsonPath","slug":"wherejsonpath"},{"level":3,"title":"whereJsonSupersetOf","slug":"wherejsonsupersetof"},{"level":3,"title":"whereJsonSubsetOf","slug":"wherejsonsubsetof"},{"level":2,"title":"Join Methods","slug":"join-methods"},{"level":3,"title":"join","slug":"join"},{"level":3,"title":"innerJoin","slug":"innerjoin"},{"level":3,"title":"leftJoin","slug":"leftjoin"},{"level":3,"title":"leftOuterJoin","slug":"leftouterjoin"},{"level":3,"title":"rightJoin","slug":"rightjoin"},{"level":3,"title":"rightOuterJoin","slug":"rightouterjoin"},{"level":3,"title":"fullOuterJoin","slug":"fullouterjoin"},{"level":3,"title":"crossJoin","slug":"crossjoin"},{"level":3,"title":"joinRaw","slug":"joinraw"},{"level":2,"title":"OnClauses","slug":"onclauses"},{"level":3,"title":"onIn","slug":"onin"},{"level":3,"title":"onNotIn","slug":"onnotin"},{"level":3,"title":"onNull","slug":"onnull"},{"level":3,"title":"onNotNull","slug":"onnotnull"},{"level":3,"title":"onExists","slug":"onexists"},{"level":3,"title":"onNotExists","slug":"onnotexists"},{"level":3,"title":"onBetween","slug":"onbetween"},{"level":3,"title":"onNotBetween","slug":"onnotbetween"},{"level":3,"title":"onJsonPathEquals","slug":"onjsonpathequals"},{"level":2,"title":"ClearClauses","slug":"clearclauses"},{"level":3,"title":"clear","slug":"clear"},{"level":3,"title":"clearSelect","slug":"clearselect"},{"level":3,"title":"clearWhere","slug":"clearwhere"},{"level":3,"title":"clearGroup","slug":"cleargroup"},{"level":3,"title":"clearOrder","slug":"clearorder"},{"level":3,"title":"clearHaving","slug":"clearhaving"},{"level":3,"title":"clearCounters","slug":"clearcounters"},{"level":3,"title":"distinct","slug":"distinct"},{"level":3,"title":"distinctOn","slug":"distincton"},{"level":3,"title":"groupBy","slug":"groupby"},{"level":3,"title":"groupByRaw","slug":"groupbyraw"},{"level":3,"title":"orderBy","slug":"orderby"},{"level":3,"title":"orderByRaw","slug":"orderbyraw"},{"level":2,"title":"Having Clauses","slug":"having-clauses"},{"level":3,"title":"having","slug":"having"},{"level":3,"title":"havingIn","slug":"havingin"},{"level":3,"title":"havingNotIn","slug":"havingnotin"},{"level":3,"title":"havingNull","slug":"havingnull"},{"level":3,"title":"havingNotNull","slug":"havingnotnull"},{"level":3,"title":"havingExists","slug":"havingexists"},{"level":3,"title":"havingNotExists","slug":"havingnotexists"},{"level":3,"title":"havingBetween","slug":"havingbetween"},{"level":3,"title":"havingNotBetween","slug":"havingnotbetween"},{"level":3,"title":"havingRaw","slug":"havingraw"}],"relativePath":"guide/query-builder.md"}',p={},o=t(`

Knex Query Builder

The heart of the library, the knex query builder is the interface used for building and executing standard SQL queries, such as select, insert, update, delete.

Identifier Syntax

In many places in APIs identifiers like table name or column name can be passed to methods.

Most commonly one needs just plain tableName.columnName, tableName or columnName, but in many cases one also needs to pass an alias how that identifier is referred later on in the query.

There are two ways to declare an alias for identifier. One can directly give as aliasName suffix for the identifier (e.g. identifierName as aliasName) or one can pass an object { aliasName: 'identifierName' }.

If the object has multiple aliases { alias1: 'identifier1', alias2: 'identifier2' }, then all the aliased identifiers are expanded to comma separated list.

INFO

Identifier syntax has no place for selecting schema, so if you are doing schemaName.tableName, query might be rendered wrong. Use .withSchema('schemaName') instead.

knex({ a: 'table', b: 'table' })
+  .select({
+    aTitle: 'a.title',
+    bTitle: 'b.title'
+  })
+  .whereRaw('?? = ??', ['a.column_1', 'b.column_2'])
+

Common

knex

knex(tableName, options={only: boolean})
knex.[methodName]

The query builder starts off either by specifying a tableName you wish to query against, or by calling any method directly on the knex object. This kicks off a jQuery-like chain, with which you can call additional query builder methods as needed to construct the query, eventually calling any of the interface methods, to either convert toString, or execute the query with a promise, callback, or stream. Optional second argument for passing options:* only: if true, the ONLY keyword is used before the tableName to discard inheriting tables' data.

WARNING

Only supported in PostgreSQL for now.

Usage with TypeScript

If using TypeScript, you can pass the type of database row as a type parameter to get better autocompletion support down the chain.

interface User {
+  id: number;
+  name: string;
+  age: number;
+}
+
+knex('users')
+  .where('id')
+  .first(); // Resolves to any
+
+knex<User>('users') // User is the type of row in database
+  .where('id', 1) // Your IDE will be able to help with the completion of id
+  .first(); // Resolves to User | undefined
+

It is also possible to take advantage of auto-completion support (in TypeScript-aware IDEs) with generic type params when writing code in plain JavaScript through JSDoc comments.

/**
+ * @typedef {Object} User
+ * @property {number} id
+ * @property {number} age
+ * @property {string} name
+ *
+ * @returns {Knex.QueryBuilder<User, {}>}
+ */
+const Users = () => knex('Users')
+
+// 'id' property can be autocompleted by editor
+Users().where('id', 1) 
+
Caveat with type inference and mutable fluent APIs

Most of the knex APIs mutate current object and return it. This pattern does not work well with type-inference.

knex<User>('users')
+  .select('id')
+  .then((users) => { // Type of users is inferred as Pick<User, "id">[]
+    // Do something with users
+  });
+
+knex<User>('users')
+  .select('id')
+  .select('age')
+  .then((users) => { // Type of users is inferred as Pick<User, "id" | "age">[]
+    // Do something with users
+  });
+
+// The type of usersQueryBuilder is determined here
+const usersQueryBuilder = knex<User>('users').select('id');
+
+if (someCondition) {
+  // This select will not change the type of usersQueryBuilder
+  // We can not change the type of a pre-declared variable in TypeScript
+  usersQueryBuilder.select('age');
+}
+usersQueryBuilder.then((users) => {
+  // Type of users here will be Pick<User, "id">[]
+  // which may not be what you expect.
+});
+
+// You can specify the type of result explicitly through a second type parameter:
+const queryBuilder = knex<User, Pick<User, "id" | "age">>('users');
+
+// But there is no type constraint to ensure that these properties have actually been
+// selected.
+
+// So, this will compile:
+queryBuilder.select('name').then((users) => {
+  // Type of users is Pick<User, "id"> but it will only have name
+})
+

If you don't want to manually specify the result type, it is recommended to always use the type of last value of the chain and assign result of any future chain continuation to a separate variable (which will have a different type).

timeout

.timeout(ms, options={cancel: boolean})

Sets a timeout for the query and will throw a TimeoutError if the timeout is exceeded. The error contains information about the query, bindings, and the timeout that was set. Useful for complex queries that you want to make sure are not taking too long to execute. Optional second argument for passing options:* cancel: if true, cancel query if timeout is reached.

WARNING

Only supported in MySQL and PostgreSQL for now.

knex.select()
+  .from('books')
+  .timeout(1000)
+
+knex.select()
+  .from('books')
+  .timeout(1000, { 
+    cancel: true // MySQL and PostgreSQL only
+  }) 
+

select

.select([*columns])

Creates a select query, taking an optional array of columns for the query, eventually defaulting to * if none are specified when the query is built. The response of a select call will resolve with an array of objects selected from the database.

knex.select('title', 'author', 'year')
+  .from('books')
+
+knex.select()
+  .table('books')
+
Usage with TypeScript

We are generally able to infer the result type based on the columns being selected as long as the select arguments match exactly the key names in record type. However, aliasing and scoping can get in the way of inference.

knex.select('id')
+  .from<User>('users'); // Resolves to Pick<User, "id">[]
+
+knex.select('users.id')
+  .from<User>('users'); // Resolves to any[]
+// ^ TypeScript doesn't provide us a way to look into a string and infer the type
+//   from a substring, so we fall back to any
+
+// We can side-step this using knex.ref:
+knex.select(knex.ref('id').withSchema('users'))
+  .from<User>('users'); // Resolves to Pick<User, "id">[]
+
+knex.select('id as identifier')
+  .from<User>('users'); // Resolves to any[], for same reason as above
+
+// Refs are handy here too:
+knex.select(knex.ref('id').as('identifier'))
+  .from<User>('users'); // Resolves to { identifier: number; }[]
+

as

.as(name)

Allows for aliasing a subquery, taking the string you wish to name the current query. If the query is not a sub-query, it will be ignored.

knex.avg('sum_column1')
+  .from(function() {
+    this.sum('column1 as sum_column1')
+      .from('t1')
+      .groupBy('column1')
+      .as('t1')
+  })
+  .as('ignored_alias')
+

column

.column(columns)

Specifically set the columns to be selected on a select query, taking an array, an object or a list of column names. Passing an object will automatically alias the columns with the given keys.

knex.column('title', 'author', 'year')
+  .select()
+  .from('books')
+
+knex.column(['title', 'author', 'year'])
+  .select()
+  .from('books')
+
+knex.column('title', { by: 'author' }, 'year')
+  .select()
+  .from('books')
+

from

.from([tableName], options={only: boolean})

Specifies the table used in the current query, replacing the current table name if one has already been specified. This is typically used in the sub-queries performed in the advanced where or union methods. Optional second argument for passing options:* only: if true, the ONLY keyword is used before the tableName to discard inheriting tables' data.

WARNING

Only supported in PostgreSQL for now.

knex.select('*')
+  .from('users')
+

Usage with TypeScript

We can specify the type of database row through the TRecord type parameter

knex.select('id')
+  .from('users'); // Resolves to any[]
+
+knex.select('id')
+  .from<User>('users'); // Results to Pick<User, "id">[]
+

fromRaw

.fromRaw(sql, [bindings])

knex.select('*')
+  .fromRaw('(select * from "users" where "age" > ?)', '18')
+

with

.with(alias, [columns], callback|builder|raw)

Add a "with" clause to the query. "With" clauses are supported by PostgreSQL, Oracle, SQLite3 and MSSQL. An optional column list can be provided after the alias; if provided, it must include at least one column name.

knex
+  .with(
+    'with_alias', 
+    knex.raw(
+      'select * from "books" where "author" = ?', 
+      'Test'
+    )
+  )
+  .select('*')
+  .from('with_alias')
+
+knex
+  .with(
+    'with_alias', 
+    ["title"], 
+    knex.raw(
+      'select "title" from "books" where "author" = ?', 
+      'Test'
+    )
+  )
+  .select('*')
+  .from('with_alias')
+
+knex
+  .with('with_alias', (qb) => {
+    qb.select('*')
+      .from('books')
+      .where('author', 'Test')
+  })
+  .select('*')
+  .from('with_alias')
+

withRecursive

.withRecursive(alias, [columns], callback|builder|raw)

Identical to the with method except "recursive" is appended to "with" (or not, as required by the target database) to make self-referential CTEs possible. Note that some databases, such as Oracle, require a column list be provided when using an rCTE.

knex
+  .withRecursive('ancestors', (qb) => {
+    qb.select('*')
+      .from('people')
+      .where('people.id', 1)
+      .union((qb) => {
+        qb.select('*')
+          .from('people')
+          .join(
+            'ancestors', 
+            'ancestors.parentId', 
+            'people.id'
+          )
+      })
+  })
+  .select('*')
+  .from('ancestors')
+
+knex
+  .withRecursive('family', ['name', 'parentName'], (qb) => {
+    qb.select('name', 'parentName')
+      .from('folks')
+      .where({ name: 'grandchild' })
+      .unionAll((qb) =>
+        qb
+          .select('folks.name', 'folks.parentName')
+          .from('folks')
+          .join(
+            'family',
+            knex.ref('family.parentName'),
+            knex.ref('folks.name')
+          )
+      )
+  })
+  .select('name')
+  .from('family')
+

withMaterialized

.withMaterialized(alias, [columns], callback|builder|raw)

Add a "with" materialized clause to the query. "With" materialized clauses are supported by PostgreSQL and SQLite3. An optional column list can be provided after the alias; if provided, it must include at least one column name.

knex
+  .withMaterialized(
+    'with_alias', 
+    knex.raw(
+      'select * from "books" where "author" = ?', 
+      'Test'
+    )
+  )
+  .select('*')
+  .from('with_alias')
+
+knex
+  .withMaterialized(
+    'with_alias', 
+    ["title"], 
+    knex.raw(
+      'select "title" from "books" where "author" = ?', 
+      'Test'
+    )
+  )
+  .select('*')
+  .from('with_alias')
+
+knex
+  .withMaterialized('with_alias', (qb) => {
+    qb.select('*')
+      .from('books')
+      .where('author', 'Test')
+  })
+  .select('*')
+  .from('with_alias')
+

withNotMaterialized

.withNotMaterialized(alias, [columns], callback|builder|raw)

Add a "with" not materialized clause to the query. "With" not materialized clauses are supported by PostgreSQL and SQLite3. An optional column list can be provided after the alias; if provided, it must include at least one column name.

knex
+  .withNotMaterialized(
+    'with_alias', 
+    knex.raw(
+      'select * from "books" where "author" = ?', 
+      'Test'
+    )
+  )
+  .select('*')
+  .from('with_alias')
+
+knex
+  .withNotMaterialized(
+    'with_alias', 
+    ["title"], 
+    knex.raw(
+      'select "title" from "books" where "author" = ?', 
+      'Test'
+    )
+  )
+  .select('*')
+  .from('with_alias')
+
+knex
+  .withNotMaterialized('with_alias', (qb) => {
+    qb.select('*')
+      .from('books')
+      .where('author', 'Test')
+  })
+  .select('*')
+  .from('with_alias')
+

withSchema

.withSchema([schemaName])

Specifies the schema to be used as prefix of table name.

knex.withSchema('public')
+  .select('*')
+  .from('users')
+

jsonExtract

.jsonExtract(column|builder|raw|array[], path, [alias], [singleValue])

Extract a value from a json column given a JsonPath. An alias can be specified. The singleValue boolean can be used to specify, with Oracle or MSSQL, if the value returned by the function is a single value or an array/object value. An array of arrays can be used to specify multiple extractions with one call to this function.

knex('accounts')
+  .jsonExtract('json_col', '$.name')
+
+knex('accounts')
+  .jsonExtract('json_col', '$.name', 'accountName')
+
+knex('accounts')
+  .jsonExtract('json_col', '$.name', 'accountName', true)
+
+knex('accounts')
+  .jsonExtract([ 
+    ['json_col', '$.name', 'accountName'], 
+    ['json_col', '$.lastName', 'accountLastName'] 
+  ])
+

All json*() functions can be used directly from knex object and can be nested.

knex('cities')
+  .jsonExtract([
+    [
+      knex.jsonRemove('population', '$.min'), 
+      '$', 
+      'withoutMin'
+    ],
+    [
+      knex.jsonRemove('population', '$.max'), 
+      '$', 
+      'withoutMax'
+    ],
+    [
+      knex.jsonSet('population', '$.current', '1234'),
+      '$',
+      'currentModified',
+    ]
+  ])
+

jsonSet

.jsonSet(column|builder|raw, path, value, [alias])

Return a json value/object/array where a given value is set at the given JsonPath. Value can be single value or json object. If a value already exists at the given place, the value is replaced. Not supported by Redshift and versions before Oracle 21c.

knex('accounts')
+  .jsonSet('json_col', '$.name', 'newName', 'newNameCol')
+
+knex('accounts')
+  .jsonSet(
+    'json_col', 
+    '$.name', 
+    { "name": "newName" }, 
+    'newNameCol'
+  )
+

jsonInsert

.jsonInsert(column|builder|raw, path, value, [alias])

Return a json value/object/array where a given value is inserted at the given JsonPath. Value can be single value or json object. If a value exists at the given path, the value is not replaced. Not supported by Redshift and versions before Oracle 21c.

knex('accounts')
+  .jsonInsert('json_col', '$.name', 'newName', 'newNameCol')
+
+knex('accounts')
+  .jsonInsert(
+    'json_col', 
+    '$.name', 
+    { "name": "newName" }, 
+    'newNameCol'
+  )
+
+knex('accounts')
+  .jsonInsert(
+    knex.jsonExtract('json_col', '$.otherAccount'), 
+    '$.name', 
+    { "name": "newName" }, 
+    'newNameCol'
+  )
+

jsonRemove

.jsonRemove(column|builder|raw, path, [alias])

Return a json value/object/array where a given value is removed at the given JsonPath. Not supported by Redshift and versions before Oracle 21c.

knex('accounts')
+  .jsonRemove('json_col', '$.name', 'colWithRemove')
+
+knex('accounts')
+  .jsonInsert(
+    'json_col', 
+    '$.name', 
+    { "name": "newName" }, 
+    'newNameCol'
+  )
+

offset

.offset(value, options={skipBinding: boolean})

Adds an offset clause to the query. An optional skipBinding parameter may be specified which would avoid setting offset as a prepared value (some databases don't allow prepared values for offset).

knex.select('*')
+  .from('users')
+  .offset(10)
+
+knex.select('*')
+  .from('users')
+  .offset(10)
+  .toSQL()
+  .sql
+
+// Offset value isn't a prepared value.
+knex.select('*')
+  .from('users')
+  .offset(10, {skipBinding: true})
+  .toSQL()
+  .sql
+

limit

.limit(value, options={skipBinding: boolean})

Adds a limit clause to the query. An optional skipBinding parameter may be specified to avoid adding limit as a prepared value (some databases don't allow prepared values for limit).

knex.select('*')
+  .from('users')
+  .limit(10)
+  .offset(30)
+
+knex.select('*')
+  .from('users')
+  .limit(10)
+  .offset(30)
+  .toSQL()
+  .sql
+
+// Limit value isn't a prepared value.
+knex.select('*')
+  .from('users')
+  .limit(10, {skipBinding: true})
+  .offset(30)
+  .toSQL()
+  .sql
+

union

.union([*queries], [wrap])

Creates a union query, taking an array or a list of callbacks, builders, or raw statements to build the union statement, with optional boolean wrap. If the wrap parameter is true, the queries will be individually wrapped in parentheses.

knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .union(function() {
+    this.select('*')
+      .from('users')
+      .whereNull('first_name')
+  })
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .union([
+    knex.select('*')
+      .from('users')
+      .whereNull('first_name')
+  ])
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .union(
+    knex.raw(
+      'select * from users where first_name is null'
+    ),
+    knex.raw(
+      'select * from users where email is null'
+    )
+  )
+

If you want to apply orderBy, groupBy, limit, offset or having to inputs of the union you need to use knex.union as a base statement. If you don't do this, those clauses will get appended to the end of the union.

// example showing how clauses get appended to the end of the query
+knex('users')
+  .select('id', 'name')
+  .groupBy('id')
+  .union(
+    knex('invitations')
+      .select('id', 'name')
+      .orderBy('expires_at')
+  )
+
+knex.union([
+  knex('users').select('id', 'name').groupBy('id'),
+  knex('invitations').select('id', 'name').orderBy('expires_at')
+])
+

before and after

unionAll

.unionAll([*queries], [wrap])

Creates a union all query, with the same method signature as the union method. If the wrap parameter is true, the queries will be individually wrapped in parentheses.

knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .unionAll(function() {
+    this.select('*').from('users').whereNull('first_name');
+  })
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .unionAll([
+    knex.select('*').from('users').whereNull('first_name')
+  ])
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .unionAll(
+    knex.raw(
+      'select * from users where first_name is null'
+    ),
+    knex.raw(
+      'select * from users where email is null'
+    )
+  )
+

intersect

.intersect([*queries], [wrap])

Creates an intersect query, taking an array or a list of callbacks, builders, or raw statements to build the intersect statement, with optional boolean wrap. If the wrap parameter is true, the queries will be individually wrapped in parentheses. The intersect method is unsupported on MySQL.

knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .intersect(function() {
+    this.select('*').from('users').whereNull('first_name')
+  })
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .intersect([
+    knex.select('*').from('users').whereNull('first_name')
+  ])
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .intersect(
+    knex.raw(
+      'select * from users where first_name is null'
+    ),
+    knex.raw(
+      'select * from users where email is null'
+    )
+  )
+

except

.except([*queries], [wrap])

Creates an except query, taking an array or a list of callbacks, builders, or raw statements to build the except statement, with optional boolean wrap. If the wrap parameter is true, the queries will be individually wrapped in parentheses. The except method is unsupported on MySQL.

knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .except(function() {
+    this.select('*').from('users').whereNull('first_name')
+  })
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .except([
+    knex.select('*').from('users').whereNull('first_name')
+  ])
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .except(
+    knex.raw(
+      'select * from users where first_name is null'
+    ),
+    knex.raw(
+      'select * from users where email is null'
+    )
+  )
+

insert

.insert(data, [returning], [options])

Creates an insert query, taking either a hash of properties to be inserted into the row, or an array of inserts, to be executed as a single insert command. If returning array is passed e.g. ['id', 'title'], it resolves the promise / fulfills the callback with an array of all the added rows with specified columns. It's a shortcut for returning method

// Returns [1] in "mysql", "sqlite", "oracle"; 
+// [] in "postgresql" 
+// unless the 'returning' parameter is set.
+knex('books').insert({title: 'Slaughterhouse Five'})
+
+// Normalizes for empty keys on multi-row insert:
+knex('coords').insert([{x: 20}, {y: 30},  {x: 10, y: 20}])
+
+// Returns [2] in "mysql", "sqlite"; [2, 3] in "postgresql"
+knex
+  .insert(
+    [
+      { title: 'Great Gatsby' }, 
+      { title: 'Fahrenheit 451' }
+    ], 
+    ['id']
+  )
+  .into('books')
+

For MSSQL, triggers on tables can interrupt returning a valid value from the standard insert statements. You can add the includeTriggerModifications option to get around this issue. This modifies the SQL so the proper values can be returned. This only modifies the statement if you are using MSSQL, a returning value is specified, and the includeTriggerModifications option is set.

// Adding the option includeTriggerModifications 
+// allows you to run statements on tables 
+// that contain triggers. Only affects MSSQL.
+knex('books')
+  .insert(
+    {title: 'Alice in Wonderland'}, 
+    ['id'], 
+    { includeTriggerModifications: true }
+  )
+

If one prefers that undefined keys are replaced with NULL instead of DEFAULT one may give useNullAsDefault configuration parameter in knex config.

const knex = require('knex')({
+  client: 'mysql',
+  connection: {
+    host : '127.0.0.1',
+    port : 3306,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  },
+  useNullAsDefault: true
+});
+
+knex('coords').insert([{x: 20}, {y: 30}, {x: 10, y: 20}])
+
insert into \`coords\` (\`x\`, \`y\`) values (20, NULL), (NULL, 30), (10, 20)"
+

onConflict

insert(..).onConflict(column)
insert(..).onConflict([column1, column2, ...])
insert(..).onConflict(knex.raw(...))

Implemented for the PostgreSQL, MySQL, and SQLite databases. A modifier for insert queries that specifies alternative behaviour in the case of a conflict. A conflict occurs when a table has a PRIMARY KEY or a UNIQUE index on a column (or a composite index on a set of columns) and a row being inserted has the same value as a row which already exists in the table in those column(s). The default behaviour in case of conflict is to raise an error and abort the query. Using this method you can change this behaviour to either silently ignore the error by using .onConflict().ignore() or to update the existing row with new data (perform an "UPSERT") by using .onConflict().merge().

INFO

For PostgreSQL and SQLite, the column(s) specified by this method must either be the table's PRIMARY KEY or have a UNIQUE index on them, or the query will fail to execute. When specifying multiple columns, they must be a composite PRIMARY KEY or have composite UNIQUE index. MySQL will ignore the specified columns and always use the table's PRIMARY KEY. For cross-platform support across PostgreSQL, MySQL, and SQLite you must both explicitly specify the columns in .onConflict() and those column(s) must be the table's PRIMARY KEY.

For PostgreSQL and SQLite, you can use knex.raw(...) function in onConflict. It can be useful to specify condition when you have partial index :

knex('tableName')
+  .insert({
+    email: "ignore@example.com",
+    name: "John Doe",
+    active: true
+  })
+    // ignore only on email conflict and active is true.
+  .onConflict(knex.raw('(email) where active'))
+  .ignore()
+

See documentation on .ignore() and .merge() methods for more details.

ignore

insert(..).onConflict(..).ignore()

Implemented for the PostgreSQL, MySQL, and SQLite databases. Modifies an insert query, and causes it to be silently dropped without an error if a conflict occurs. Uses INSERT IGNORE in MySQL, and adds an ON CONFLICT (columns) DO NOTHING clause to the insert statement in PostgreSQL and SQLite.

knex('tableName')
+  .insert({
+    email: "ignore@example.com",
+    name: "John Doe"
+  })
+  .onConflict('email')
+  .ignore()
+

merge

insert(..).onConflict(..).merge()
insert(..).onConflict(..).merge(updates)

Implemented for the PostgreSQL, MySQL, and SQLite databases. Modifies an insert query, to turn it into an 'upsert' operation. Uses ON DUPLICATE KEY UPDATE in MySQL, and adds an ON CONFLICT (columns) DO UPDATE clause to the insert statement in PostgreSQL and SQLite. By default, it merges all columns.

knex('tableName')
+  .insert({
+    email: "ignore@example.com",
+    name: "John Doe"
+  })
+  .onConflict('email')
+  .merge()
+

This also works with batch inserts:

knex('tableName')
+  .insert([
+    { email: "john@example.com", name: "John Doe" },
+    { email: "jane@example.com", name: "Jane Doe" },
+    { email: "alex@example.com", name: "Alex Doe" },
+  ])
+  .onConflict('email')
+  .merge()
+

It is also possible to specify a subset of the columns to merge when a conflict occurs. For example, you may want to set a 'created_at' column when inserting but would prefer not to update it if the row already exists:

const timestamp = Date.now();
+knex('tableName')
+  .insert({
+    email: "ignore@example.com",
+    name: "John Doe",
+    created_at: timestamp,
+    updated_at: timestamp,
+  })
+  .onConflict('email')
+  .merge(['email', 'name', 'updated_at'])
+

It is also possible to specify data to update separately from the data to insert. This is useful if you want to update with different data to the insert. For example, you may want to change a value if the row already exists:

const timestamp = Date.now();
+knex('tableName')
+  .insert({
+    email: "ignore@example.com",
+    name: "John Doe",
+    created_at: timestamp,
+    updated_at: timestamp,
+  })
+  .onConflict('email')
+  .merge({
+    name: "John Doe The Second",
+  })
+

For PostgreSQL/SQLite databases only, it is also possible to add a WHERE clause to conditionally update only the matching rows:

const timestamp = Date.now();
+knex('tableName')
+  .insert({
+    email: "ignore@example.com",
+    name: "John Doe",
+    created_at: timestamp,
+    updated_at: timestamp,
+  })
+  .onConflict('email')
+  .merge({
+    name: "John Doe",
+    updated_at: timestamp,
+  })
+  .where('updated_at', '<', timestamp)
+

upsert

.upsert(data, [returning], [options])

Implemented for the CockroachDB. Creates an upsert query, taking either a hash of properties to be inserted into the row, or an array of upserts, to be executed as a single upsert command. If returning array is passed e.g. ['id', 'title'], it resolves the promise / fulfills the callback with an array of all the added rows with specified columns. It's a shortcut for returning method

// insert new row with unique index on title column
+knex('books').upsert({title: 'Great Gatsby'})
+
+// update row by unique title 'Great Gatsby' 
+// and insert row with title 'Fahrenheit 451'
+knex('books').upsert([
+  {title: 'Great Gatsby'}, 
+  {title: 'Fahrenheit 451'}
+], ['id'])
+
+// Normalizes for empty keys on multi-row upsert, 
+// result sql: 
+// ("x", "y") values (20, default), (default, 30), (10, 20):
+knex('coords').upsert([{x: 20}, {y: 30}, {x: 10, y: 20}])
+

update

.update(data, [returning], [options]).update(key, value, [returning], [options])

Creates an update query, taking a hash of properties or a key/value pair to be updated based on the other query constraints. If returning array is passed e.g. ['id', 'title'], it resolves the promise / fulfills the callback with an array of all the updated rows with specified columns. It's a shortcut for returning method

knex('books')
+  .where('published_date', '<', 2000)
+  .update({
+    status: 'archived',
+    thisKeyIsSkipped: undefined
+  })
+
+// Returns [1] in "mysql", "sqlite", "oracle"; 
+// [] in "postgresql" 
+// unless the 'returning' parameter is set.
+knex('books').update('title', 'Slaughterhouse Five')
+
+/** Returns  
+ * [{ 
+ *   id: 42, 
+ *   title: "The Hitchhiker's Guide to the Galaxy" 
+ * }] **/
+knex('books')
+  .where({ id: 42 })
+  .update({ 
+    title: "The Hitchhiker's Guide to the Galaxy" 
+  }, ['id', 'title'])
+

For MSSQL, triggers on tables can interrupt returning a valid value from the standard update statements. You can add the includeTriggerModifications option to get around this issue. This modifies the SQL so the proper values can be returned. This only modifies the statement if you are using MSSQL, a returning value is specified, and the includeTriggerModifications option is set.

// Adding the option includeTriggerModifications allows you
+// to run statements on tables that contain triggers.
+// Only affects MSSQL.
+knex('books')
+  .update(
+    {title: 'Alice in Wonderland'}, 
+    ['id', 'title'], 
+    { includeTriggerModifications: true }
+  )
+

updateFrom

.updateFrom(tableName)

Can be used to define in PostgreSQL an update statement with explicit 'from' syntax which can be referenced in 'where' conditions.

knex('accounts')
+  .update({ enabled: false })
+  .updateFrom('clients')
+  .where('accounts.id', '=', 'clients.id')
+  .where('clients.active', '=', false)
+

del / delete

.del([returning], [options])

Aliased to del as delete is a reserved word in JavaScript, this method deletes one or more rows, based on other conditions specified in the query. Resolves the promise / fulfills the callback with the number of affected rows for the query.

knex('accounts')
+  .where('activated', false)
+  .del()
+

For MSSQL, triggers on tables can interrupt returning a valid value from the standard delete statements. You can add the includeTriggerModifications option to get around this issue. This modifies the SQL so the proper values can be returned. This only modifies the statement if you are using MSSQL, a returning value is specified, and the includeTriggerModifications option is set.

// Adding the option includeTriggerModifications allows you
+// to run statements on tables that contain triggers. 
+// Only affects MSSQL.
+knex('books')
+  .where('title', 'Alice in Wonderland')
+  .del(
+    ['id', 'title'], 
+    { includeTriggerModifications: true }
+  )
+

For PostgreSQL, Delete statement with joins is both supported with classic 'join' syntax and 'using' syntax.

knex('accounts')
+  .where('activated', false)
+  .join('accounts', 'accounts.id', 'users.account_id')
+  .del()
+

using

.using(tableName|tableNames)

Can be used to define in PostgreSQL a delete statement with joins with explicit 'using' syntax. Classic join syntax can be used too.

knex('accounts')
+  .where('activated', false)
+  .using('accounts')
+  .whereRaw('accounts.id = users.account_id')
+  .del()
+

returning

.returning(column, [options]).returning([column1, column2, ...], [options])

Utilized by PostgreSQL, MSSQL, SQLite, and Oracle databases, the returning method specifies which column should be returned by the insert, update and delete methods. Passed column parameter may be a string or an array of strings. The SQL result be reported as an array of objects, each containing a single property for each of the specified columns. The returning method is not supported on Amazon Redshift.

// Returns [ { id: 1 } ]
+knex('books')
+  .returning('id')
+  .insert({title: 'Slaughterhouse Five'})
+
+// Returns [{ id: 2 } ] in "mysql", "sqlite"; 
+// [ { id: 2 }, { id: 3 } ] in "postgresql"
+knex('books')
+  .returning('id')
+  .insert([
+    {title: 'Great Gatsby'}, 
+    {title: 'Fahrenheit 451'}
+  ])
+
+// Returns [ { id: 1, title: 'Slaughterhouse Five' } ]
+knex('books')
+  .returning(['id','title'])
+  .insert({title: 'Slaughterhouse Five'})
+

For MSSQL, triggers on tables can interrupt returning a valid value from the standard DML statements. You can add the includeTriggerModifications option to get around this issue. This modifies the SQL so the proper values can be returned. This only modifies the statement if you are using MSSQL, a returning value is specified, and the includeTriggerModifications option is set.

// Adding the option includeTriggerModifications allows you
+// to run statements on tables that contain triggers. 
+// Only affects MSSQL.
+knex('books')
+  .returning(
+    ['id','title'], 
+    { includeTriggerModifications: true }
+  )
+  .insert({title: 'Slaughterhouse Five'})
+

transacting

.transacting(transactionObj)

Used by knex.transaction, the transacting method may be chained to any query and passed the object you wish to join the query as part of the transaction for.

const Promise = require('bluebird');
+knex.transaction(function(trx) {
+  knex('books').transacting(trx).insert({name: 'Old Books'})
+    .then(function(resp) {
+      const id = resp[0];
+      return someExternalMethod(id, trx);
+    })
+    .then(trx.commit)
+    .catch(trx.rollback);
+})
+.then(function(resp) {
+  console.log('Transaction complete.');
+})
+.catch(function(err) {
+  console.error(err);
+});
+

forUpdate

.transacting(t).forUpdate()

Dynamically added after a transaction is specified, the forUpdate adds a FOR UPDATE in PostgreSQL and MySQL during a select statement. Not supported on Amazon Redshift due to lack of table locks.

knex('tableName')
+  .transacting(trx)
+  .forUpdate()
+  .select('*')
+

forShare

.transacting(t).forShare()

Dynamically added after a transaction is specified, the forShare adds a FOR SHARE in PostgreSQL and a LOCK IN SHARE MODE for MySQL during a select statement. Not supported on Amazon Redshift due to lack of table locks.

knex('tableName')
+  .transacting(trx)
+  .forShare()
+  .select('*')
+

forNoKeyUpdate

.transacting(t).forNoKeyUpdate()

Dynamically added after a transaction is specified, the forNoKeyUpdate adds a FOR NO KEY UPDATE in PostgreSQL.

knex('tableName')
+  .transacting(trx)
+  .forNoKeyUpdate()
+  .select('*')
+

forKeyShare

.transacting(t).forKeyShare()

Dynamically added after a transaction is specified, the forKeyShare adds a FOR KEY SHARE in PostgreSQL.

knex('tableName')
+  .transacting(trx)
+  .forKeyShare()
+  .select('*')
+

skipLocked

.skipLocked()

MySQL 8.0+, MariaDB-10.6+ and PostgreSQL 9.5+ only. This method can be used after a lock mode has been specified with either forUpdate or forShare, and will cause the query to skip any locked rows, returning an empty set if none are available.

knex('tableName')
+  .select('*')
+  .forUpdate()
+  .skipLocked()
+

noWait

.noWait()

MySQL 8.0+, MariaDB-10.3+ and PostgreSQL 9.5+ only. This method can be used after a lock mode has been specified with either forUpdate or forShare, and will cause the query to fail immediately if any selected rows are currently locked.

knex('tableName')
+  .select('*')
+  .forUpdate()
+  .noWait()
+

count

.count(column|columns|raw, [options])

Performs a count on the specified column or array of columns (note that some drivers do not support multiple columns). Also accepts raw expressions. The value returned from count (and other aggregation queries) is an array of objects like: [{'COUNT(*)': 1}]. The actual keys are dialect specific, so usually we would want to specify an alias (Refer examples below). Note that in Postgres, count returns a bigint type which will be a String and not a Number (more info).

knex('users').count('active')
+
+knex('users').count('active', {as: 'a'})
+
+knex('users').count('active as a')
+
+knex('users').count({ a: 'active' })
+
+knex('users').count({ a: 'active', v: 'valid' })
+
+knex('users').count('id', 'active')
+
+knex('users').count({ count: ['id', 'active'] })
+
+knex('users').count(knex.raw('??', ['active']))
+
Usage with TypeScript

The value of count will, by default, have type of string | number. This may be counter-intuitive but some connectors (eg. postgres) will automatically cast BigInt result to string when javascript's Number type is not large enough for the value.

    knex('users').count('age') // Resolves to: Record<string, number | string>
+    
+    knex('users').count({count: '*'}) // Resolves to { count?: string | number | undefined; }
+

Working with string | number can be inconvenient if you are not working with large tables. Two alternatives are available:

    // Be explicit about what you want as a result:
+    knex('users').count<Record<string, number>>('age');
+    
+    // Setup a one time declaration to make knex use number as result type for all
+    // count and countDistinct invocations (for any table)
+    declare module "knex/types/result" {
+        interface Registry {
+            Count: number;
+        }
+    }
+

Use countDistinct to add a distinct expression inside the aggregate function.

    knex('users').countDistinct('active')
+

min

.min(column|columns|raw, [options])

Gets the minimum value for the specified column or array of columns (note that some drivers do not support multiple columns). Also accepts raw expressions.

knex('users').min('age')
+
+knex('users').min('age', {as: 'a'})
+
+knex('users').min('age as a')
+
+knex('users').min({ a: 'age' })
+
+knex('users').min({ a: 'age', b: 'experience' })
+
+knex('users').min('age', 'logins')
+
+knex('users').min({ min: ['age', 'logins'] })
+
+knex('users').min(knex.raw('??', ['age']))
+

max

.max(column|columns|raw, [options])

Gets the maximum value for the specified column or array of columns (note that some drivers do not support multiple columns). Also accepts raw expressions.

knex('users').max('age')
+
+knex('users').max('age', {as: 'a'})
+
+knex('users').max('age as a')
+
+knex('users').max({ a: 'age' })
+
+knex('users').max('age', 'logins')
+
+knex('users').max({ max: ['age', 'logins'] })
+
+knex('users').max({ max: 'age', exp: 'experience' })
+
+knex('users').max(knex.raw('??', ['age']))
+

sum

.sum(column|columns|raw)

Retrieve the sum of the values of a given column or array of columns (note that some drivers do not support multiple columns). Also accepts raw expressions.

knex('users').sum('products')
+
+knex('users').sum('products as p')
+
+knex('users').sum({ p: 'products' })
+
+knex('users').sum('products', 'orders')
+
+knex('users').sum({ sum: ['products', 'orders'] })
+
+knex('users').sum(knex.raw('??', ['products']))
+

Use sumDistinct to add a distinct expression inside the aggregate function.

    knex('users').sumDistinct('products')
+

avg

.avg(column|columns|raw)

Retrieve the average of the values of a given column or array of columns (note that some drivers do not support multiple columns). Also accepts raw expressions.

knex('users').avg('age')
+
+knex('users').avg('age as a')
+
+knex('users').avg({ a: 'age' })
+
+knex('users').avg('age', 'logins')
+
+knex('users').avg({ avg: ['age', 'logins'] })
+
+knex('users').avg(knex.raw('??', ['age']))
+

Use avgDistinct to add a distinct expression inside the aggregate function.

    knex('users').avgDistinct('age')
+

increment

.increment(column, amount)

Increments a column value by the specified amount. Object syntax is supported for column.

knex('accounts')
+  .where('userid', '=', 1)
+  .increment('balance', 10)
+
+knex('accounts')
+  .where('id', '=', 1)
+  .increment({
+    balance: 10,
+    times: 1,
+  })
+

decrement

.decrement(column, amount)

Decrements a column value by the specified amount. Object syntax is supported for column.

knex('accounts')
+  .where('userid', '=', 1)
+  .decrement('balance', 5)
+
+knex('accounts')
+  .where('id', '=', 1)
+  .decrement({
+    balance: 50,
+  })
+

truncate

.truncate()

Truncates the current table.

knex('accounts').truncate()
+

pluck

.pluck(id)

This will pluck the specified column from each row in your results, yielding a promise which resolves to the array of values selected.

knex.table('users')
+  .pluck('id')
+  .then(function(ids) { console.log(ids); });
+

first

.first([columns])

Similar to select, but only retrieves & resolves with the first record from the query.

knex.table('users')
+  .first('id', 'name')
+  .then(function(row) { console.log(row); });
+

hintComment

.hintComment(hint|hints)

Add hints to the query using comment-like syntax /*+ ... */. MySQL and Oracle use this syntax for optimizer hints. Also various DB proxies and routers use this syntax to pass hints to alter their behavior. In other dialects the hints are ignored as simple comments.

knex('accounts')
+  .where('userid', '=', 1)
+  .hintComment('NO_ICP(accounts)')
+

comment

.comment(comment)

Prepend comment to the sql query using the syntax /* ... */. Some characters are forbidden such as /*, */ and ?.

knex('users')
+  .where('id', '=', 1)
+  .comment('Get user by id')
+

clone

.clone()

Clones the current query chain, useful for re-using partial query snippets in other queries without mutating the original.

denseRank

.denseRank(alias, ~mixed~)

Add a dense_rank() call to your query. For all the following queries, alias can be set to a falsy value if not needed.

String Syntax \u2014 .denseRank(alias, orderByClause, [partitionByClause]) :

knex('users')
+  .select('*')
+  .denseRank('alias_name', 'email', 'firstName')
+

It also accepts arrays of strings as argument :

knex('users')
+  .select('*')
+  .denseRank(
+    'alias_name', 
+    ['email', 'address'], 
+    ['firstName', 'lastName']
+  )
+

Raw Syntax \u2014 .denseRank(alias, rawQuery) :

knex('users').select('*')
+  .denseRank(
+    'alias_name', 
+    knex.raw('order by ??', ['email']
+  ))
+

Function Syntax \u2014 .denseRank(alias, function) :

Use orderBy() and partitionBy() (both chainable) to build your query :

knex('users')
+  .select('*')
+  .denseRank('alias_name', function() {
+    this.orderBy('email').partitionBy('firstName')
+  })
+

rank

.rank(alias, ~mixed~)

Add a rank() call to your query. For all the following queries, alias can be set to a falsy value if not needed.

String Syntax \u2014 .rank(alias, orderByClause, [partitionByClause]) :

knex('users')
+  .select('*')
+  .rank('alias_name', 'email', 'firstName')
+

It also accepts arrays of strings as argument :

knex('users')
+  .select('*')
+  .rank(
+    'alias_name', 
+    ['email', 'address'], 
+    ['firstName', 'lastName']
+  )
+

Raw Syntax \u2014 .rank(alias, rawQuery) :

knex('users')
+  .select('*')
+  .rank('alias_name', knex.raw('order by ??', ['email']))
+

Function Syntax \u2014 .rank(alias, function) :

Use orderBy() and partitionBy() (both chainable) to build your query :

knex('users').select('*').rank('alias_name', function() {
+  this.orderBy('email').partitionBy('firstName')
+})
+

rowNumber

.rowNumber(alias, ~mixed~)

Add a row_number() call to your query. For all the following queries, alias can be set to a falsy value if not needed.

String Syntax \u2014 .rowNumber(alias, orderByClause, [partitionByClause]) :

knex('users')
+  .select('*')
+  .rowNumber('alias_name', 'email', 'firstName')
+

It also accepts arrays of strings as argument :

knex('users')
+  .select('*')
+  .rowNumber(
+    'alias_name', 
+    ['email', 'address'], 
+    ['firstName', 'lastName']
+  )
+

Raw Syntax \u2014 .rowNumber(alias, rawQuery) :

knex('users')
+  .select('*')
+  .rowNumber(
+    'alias_name', 
+    knex.raw('order by ??', ['email'])
+  )
+

Function Syntax \u2014 .rowNumber(alias, function) :

Use orderBy() and partitionBy() (both chainable) to build your query :

knex('users')
+  .select('*')
+  .rowNumber('alias_name', function() {
+    this.orderBy('email').partitionBy('firstName')
+  })
+

partitionBy

.partitionBy(column, direction)

Partitions rowNumber, denseRank, rank after a specific column or columns. If direction is not supplied it will default to ascending order.

No direction sort :

knex('users')
+  .select('*')
+  .rowNumber('alias_name', function() {
+    this.partitionBy('firstName');
+  });
+

With direction sort :

knex('users')
+  .select('*')
+  .rowNumber('alias_name', function() {
+    this.partitionBy('firstName', 'desc');
+  });
+

With multiobject :

knex('users')
+  .select('*')
+  .rowNumber('alias_name', function() {
+    this.partitionBy([
+      { column: 'firstName', order: 'asc' }, 
+      { column: 'lastName', order: 'desc' }
+    ]);
+  });
+

modify

*.modify(fn, arguments)

Allows encapsulating and re-using query snippets and common behaviors as functions. The callback function should receive the query builder as its first argument, followed by the rest of the (optional) parameters passed to modify.

const withUserName = function(queryBuilder, foreignKey) {
+  queryBuilder.leftJoin(
+    'users', 
+    foreignKey, 
+    'users.id'
+  ).select('users.user_name');
+};
+knex.table('articles')
+  .select('title', 'body')
+  .modify(withUserName, 'articles_user.id')
+  .then(function(article) {
+    console.log(article.user_name);
+  });
+

columnInfo

.columnInfo([columnName])

Returns an object with the column info about the current table, or an individual column if one is passed, returning an object with the following keys:

  • defaultValue: the default value for the column
  • type: the column type
  • maxLength: the max length set for the column
  • nullable: whether the column may be null
knex('users').columnInfo().then(function(info) { /*...*/ });
+

debug

.debug([enabled])

Overrides the global debug setting for the current query chain. If enabled is omitted, query debugging will be turned on.

connection

.connection(dbConnection)

The method sets the db connection to use for the query without using the connection pool. You should pass to it the same object that acquireConnection() for the corresponding driver returns

const Pool = require('pg-pool');
+const pool = new Pool({ /* ... */ });
+const connection = await pool.connect();
+
+try {
+  return await knex.connection(connection); // knex here is a query builder with query already built
+} catch (error) {
+  // Process error
+} finally {
+  connection.release();
+}
+

options

.options()

Allows for mixing in additional options as defined by database client specific libraries:

    knex('accounts as a1')
+      .leftJoin('accounts as a2', function() {
+        this.on('a1.email', '<>', 'a2.email');
+      })
+      .select(['a1.email', 'a2.email'])
+      .where(knex.raw('a1.id = 1'))
+      .options({ nestTables: true, rowMode: 'array' })
+      .limit(2)
+      .then({ /*...*/ })
+

queryContext

.queryContext(context)

Allows for configuring a context to be passed to the wrapIdentifier and postProcessResponse hooks:

    knex('accounts as a1')
+      .queryContext({ foo: 'bar' })
+      .select(['a1.email', 'a2.email'])
+

The context can be any kind of value and will be passed to the hooks without modification. However, note that objects will be shallow-cloned when a query builder instance is cloned, which means that they will contain all the properties of the original object but will not be the same object reference. This allows modifying the context for the cloned query builder instance.

Calling queryContext with no arguments will return any context configured for the query builder instance.

Extending Query Builder

Important: this feature is experimental and its API may change in the future.

It allows to add custom function to the Query Builder.

Example:

const { knex } = require('knex');
+knex.QueryBuilder.extend('customSelect', function(value) {
+  return this.select(this.client.raw(\`\${value} as value\`));
+});
+
+const meaningOfLife = await knex('accounts')
+  .customSelect(42);
+

If using TypeScript, you can extend the QueryBuilder interface with your custom method.

  1. Create a knex.d.ts file inside a @types folder (or any other folder).
// knex.d.ts
+
+import { Knex as KnexOriginal } from 'knex';
+
+declare module 'knex' {
+  namespace Knex {
+    interface QueryInterface {
+      customSelect<TRecord, TResult>(value: number): KnexOriginal.QueryBuilder<TRecord, TResult>;
+    }
+  }
+}
+
  1. Add the new @types folder to typeRoots in your tsconfig.json.
// tsconfig.json
+{
+  "compilerOptions": {
+    "typeRoots": [
+      "node_modules/@types",
+      "@types"
+    ],
+  }
+}
+

Where Clauses

Several methods exist to assist in dynamic where clauses. In many places functions may be used in place of values, constructing subqueries. In most places existing knex queries may be used to compose sub-queries, etc. Take a look at a few of the examples for each method for instruction on use:

Important: Supplying knex with an undefined value to any of the where functions will cause knex to throw an error during sql compilation. This is both for yours and our sake. Knex cannot know what to do with undefined values in a where clause, and generally it would be a programmatic error to supply one to begin with. The error will throw a message containing the type of query and the compiled query-string. Example:

knex('accounts')
+  .where('login', undefined)
+  .select()
+  .toSQL()
+

where

.where(~mixed~)
.orWhere

Object Syntax:

knex('users').where({
+  first_name: 'Test',
+  last_name:  'User'
+}).select('id')
+

Key, Value:

knex('users').where('id', 1)
+

Functions:

knex('users')
+  .where((builder) =>
+    builder
+      .whereIn('id', [1, 11, 15])
+      .whereNotIn('id', [17, 19])
+  )
+  .andWhere(function() {
+    this.where('id', '>', 10)
+  })
+

Grouped Chain:

knex('users').where(function() {
+  this.where('id', 1).orWhere('id', '>', 10)
+}).orWhere({name: 'Tester'})
+

Operator:

knex('users').where('columnName', 'like', '%rowlikeme%')
+

The above query demonstrates the common use case of returning all users for which a specific pattern appears within a designated column.

knex('users').where('votes', '>', 100)
+
+const subquery = knex('users')
+  .where('votes', '>', 100)
+  .andWhere('status', 'active')
+  .orWhere('name', 'John')
+  .select('id');
+
+knex('accounts').where('id', 'in', subquery)
+

.orWhere with an object automatically wraps the statement and creates an or (and - and - and) clause

knex('users')
+  .where('id', 1)
+  .orWhere({votes: 100, user: 'knex'})
+

whereNot

.whereNot(~mixed~).orWhereNot

Object Syntax:

knex('users').whereNot({
+  first_name: 'Test',
+  last_name:  'User'
+}).select('id')
+

Key, Value:

knex('users').whereNot('id', 1)
+

Grouped Chain:

knex('users').whereNot(function() {
+  this.where('id', 1).orWhereNot('id', '>', 10)
+}).orWhereNot({name: 'Tester'})
+

Operator:

knex('users').whereNot('votes', '>', 100)
+

WARNING

WhereNot is not suitable for "in" and "between" type subqueries. You should use "not in" and "not between" instead.

const subquery = knex('users')
+  .whereNot('votes', '>', 100)
+  .andWhere('status', 'active')
+  .orWhere('name', 'John')
+  .select('id');
+
+knex('accounts').where('id', 'not in', subquery)
+

whereIn

.whereIn(column|columns, array|callback|builder)
.orWhereIn

Shorthand for .where('id', 'in', obj), the .whereIn and .orWhereIn methods add a "where in" clause to the query. Note that passing empty array as the value results in a query that never returns any rows (WHERE 1 = 0)

knex.select('name').from('users')
+  .whereIn('id', [1, 2, 3])
+  .orWhereIn('id', [4, 5, 6])
+
+knex.select('name').from('users')
+  .whereIn('account_id', function() {
+    this.select('id').from('accounts');
+  })
+
+const subquery = knex.select('id').from('accounts');
+
+knex.select('name').from('users')
+  .whereIn('account_id', subquery)
+
+knex.select('name').from('users')
+  .whereIn(
+    ['account_id', 'email'], 
+    [
+      [3, 'test3@example.com'], 
+      [4, 'test4@example.com']
+    ]
+  )
+
+knex.select('name').from('users')
+  .whereIn(
+    ['account_id', 'email'], 
+    knex.select('id', 'email')
+      .from('accounts')
+  )
+

whereNotIn

.whereNotIn(column, array|callback|builder).orWhereNotIn

knex('users').whereNotIn('id', [1, 2, 3])
+
+knex('users')
+  .where('name', 'like', '%Test%')
+  .orWhereNotIn('id', [1, 2, 3])
+

whereNull

.whereNull(column)
.orWhereNull

knex('users').whereNull('updated_at')
+

whereNotNull

.whereNotNull(column)
.orWhereNotNull

knex('users').whereNotNull('created_at')
+

whereExists

.whereExists(builder | callback)
.orWhereExists

knex('users').whereExists(function() {
+  this.select('*')
+    .from('accounts')
+    .whereRaw('users.account_id = accounts.id');
+})
+
+knex('users')
+  .whereExists(
+    knex.select('*')
+      .from('accounts')
+      .whereRaw('users.account_id = accounts.id')
+  )
+

whereNotExists

.whereNotExists(builder | callback)
.orWhereNotExists

knex('users').whereNotExists(function() {
+  this.select('*')
+    .from('accounts')
+    .whereRaw('users.account_id = accounts.id');
+})
+
+knex('users')
+  .whereNotExists(
+    knex.select('*')
+      .from('accounts')
+      .whereRaw('users.account_id = accounts.id')
+  )
+

whereBetween

.whereBetween(column, range)
.orWhereBetween

knex('users').whereBetween('votes', [1, 100])
+

whereNotBetween

.whereNotBetween(column, range)
.orWhereNotBetween

knex('users').whereNotBetween('votes', [1, 100])
+

whereRaw

.whereRaw(query, [bindings])

Convenience helper for .where(knex.raw(query)).

knex('users').whereRaw('id = ?', [1])
+

whereLike

.whereLike(column, string|builder|raw)
.orWhereLike

Adds a where clause with case-sensitive substring comparison on a given column with a given value.

knex('users').whereLike('email', '%mail%')
+
+knex('users')
+  .whereLike('email', '%mail%')
+  .andWhereLike('email', '%.com')
+  .orWhereLike('email', '%name%')
+

whereILike

.whereILike(column, string|builder|raw)
.orWhereILike

Adds a where clause with case-insensitive substring comparison on a given column with a given value.

knex('users').whereILike('email', '%mail%')
+
+knex('users')
+  .whereILike('email', '%MAIL%')
+  .andWhereILike('email', '%.COM')
+  .orWhereILike('email', '%NAME%')
+

whereJsonObject

.whereJsonObject(column, string|json|builder|raw)

Adds a where clause with json object comparison on given json column.

knex('users')
+  .whereJsonObject('json_col', { "name" : "user_name"})
+

whereJsonPath

.whereJsonPath(column, jsonPath, operator, value)

Adds a where clause with comparison of a value returned by a JsonPath given an operator and a value.

knex('users')
+  .whereJsonPath('json_col', '$.age', '>', 18)
+
+knex('users')
+  .whereJsonPath('json_col', '$.name', '=', 'username')
+

whereJsonSupersetOf

.whereJsonSupersetOf(column, string|json|builder|raw)

Adds a where clause where the comparison is true if a json given by the column include a given value. Only on MySQL, PostgreSQL and CockroachDB.

knex('users')
+  .whereJsonSupersetOf('hobbies', { "sport" : "foot" })
+

whereJsonSubsetOf

.whereJsonSubsetOf(column, string|json|builder|raw)

Adds a where clause where the comparison is true if a json given by the column is included in a given value. Only on MySQL, PostgreSQL and CockroachDB.

// given a hobby column with { "sport" : "tennis" }, 
+// the where clause is true
+knex('users')
+  .whereJsonSubsetOf(
+    'hobby', 
+    { "sport" : "tennis", "book" : "fantasy" }
+  )
+

Join Methods

Several methods are provided which assist in building joins.

join

.join(table, first, [operator], second)

The join builder can be used to specify joins between tables, with the first argument being the joining table, the next three arguments being the first join column, the join operator and the second join column, respectively.

knex('users')
+  .join('contacts', 'users.id', '=', 'contacts.user_id')
+  .select('users.id', 'contacts.phone')
+
+knex('users')
+  .join('contacts', 'users.id', 'contacts.user_id')
+  .select('users.id', 'contacts.phone')
+

For grouped joins, specify a function as the second argument for the join query, and use on with orOn or andOn to create joins that are grouped with parentheses.

knex.select('*').from('users').join('accounts', function() {
+  this
+    .on('accounts.id', '=', 'users.account_id')
+    .orOn('accounts.owner_id', '=', 'users.id')
+})
+

For nested join statements, specify a function as first argument of on, orOn or andOn

knex.select('*').from('users').join('accounts', function() {
+  this.on(function() {
+    this.on('accounts.id', '=', 'users.account_id')
+    this.orOn('accounts.owner_id', '=', 'users.id')
+  })
+})
+

It is also possible to use an object to represent the join syntax.

knex.select('*')
+  .from('users')
+  .join('accounts', {'accounts.id': 'users.account_id'})
+

If you need to use a literal value (string, number, or boolean) in a join instead of a column, use knex.raw.

knex.select('*')
+  .from('users')
+  .join(
+    'accounts', 
+    'accounts.type',
+    knex.raw('?', ['admin'])
+  )
+

innerJoin

.innerJoin(table, ~mixed~)

knex
+  .from('users')
+  .innerJoin('accounts', 'users.id', 'accounts.user_id')
+
+knex
+  .table('users')
+  .innerJoin(
+    'accounts', 
+    'users.id', 
+    '=', 
+    'accounts.user_id'
+  )
+
+knex('users')
+  .innerJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

leftJoin

.leftJoin(table, ~mixed~)

knex.select('*')
+  .from('users')
+  .leftJoin('accounts', 'users.id', 'accounts.user_id')
+
+knex.select('*')
+  .from('users')
+  .leftJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

leftOuterJoin

.leftOuterJoin(table, ~mixed~)

knex.select('*')
+  .from('users')
+  .leftOuterJoin('accounts', 'users.id', 'accounts.user_id')
+
+knex.select('*')
+  .from('users')
+  .leftOuterJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

rightJoin

.rightJoin(table, ~mixed~)

knex.select('*')
+  .from('users')
+  .rightJoin('accounts', 'users.id', 'accounts.user_id')
+
+knex.select('*')
+  .from('users')
+  .rightJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

rightOuterJoin

.rightOuterJoin(table, ~mixed~)

knex.select('*')
+  .from('users')
+  .rightOuterJoin(
+    'accounts', 
+    'users.id', 
+    'accounts.user_id'
+  )
+
+knex.select('*')
+  .from('users')
+  .rightOuterJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

fullOuterJoin

.fullOuterJoin(table, ~mixed~)

knex.select('*')
+  .from('users')
+  .fullOuterJoin('accounts', 'users.id', 'accounts.user_id')
+
+knex.select('*')
+  .from('users')
+  .fullOuterJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

crossJoin

.crossJoin(table, ~mixed~)

Cross join conditions are only supported in MySQL and SQLite3. For join conditions rather use innerJoin.

knex.select('*')
+  .from('users')
+  .crossJoin('accounts')
+
+knex.select('*')
+  .from('users')
+  .crossJoin('accounts', 'users.id', 'accounts.user_id')
+
+knex.select('*')
+  .from('users')
+  .crossJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

joinRaw

.joinRaw(sql, [bindings])

knex.select('*')
+  .from('accounts')
+  .joinRaw('natural full join table1').where('id', 1)
+
+knex.select('*')
+  .from('accounts')
+  .join(knex.raw('natural full join table1')).where('id', 1)
+

OnClauses

onIn

.onIn(column, values)

Adds a onIn clause to the query.

knex.select('*')
+  .from('users')
+  .join('contacts', function() {
+    this
+      .on('users.id', '=', 'contacts.id')
+      .onIn('contacts.id', [7, 15, 23, 41])
+  })
+

onNotIn

.onNotIn(column, values)

Adds a onNotIn clause to the query.

knex.select('*')
+  .from('users')
+  .join('contacts', function() {
+    this
+      .on('users.id', '=', 'contacts.id')
+      .onNotIn('contacts.id', [7, 15, 23, 41])
+  })
+

onNull

.onNull(column)

Adds a onNull clause to the query.

knex.select('*').from('users').join('contacts', function() {
+  this
+    .on('users.id', '=', 'contacts.id')
+    .onNull('contacts.email')
+})
+

onNotNull

.onNotNull(column)

Adds a onNotNull clause to the query.

knex.select('*').from('users').join('contacts', function() {
+  this
+    .on('users.id', '=', 'contacts.id')
+    .onNotNull('contacts.email')
+})
+

onExists

.onExists(builder | callback)

Adds a onExists clause to the query.

knex.select('*').from('users').join('contacts', function() {
+  this
+    .on('users.id', '=', 'contacts.id')
+    .onExists(function() {
+      this.select('*')
+        .from('accounts')
+        .whereRaw('users.account_id = accounts.id');
+    })
+})
+

onNotExists

.onNotExists(builder | callback)

Adds a onNotExists clause to the query.

knex.select('*').from('users').join('contacts', function() {
+  this
+    .on('users.id', '=', 'contacts.id')
+    .onNotExists(function() {
+      this.select('*')
+        .from('accounts')
+        .whereRaw('users.account_id = accounts.id');
+    })
+})
+

onBetween

.onBetween(column, range)

Adds a onBetween clause to the query.

knex.select('*').from('users').join('contacts', function() {
+  this
+    .on('users.id', '=', 'contacts.id')
+    .onBetween('contacts.id', [5, 30])
+})
+

onNotBetween

.onNotBetween(column, range)

Adds a onNotBetween clause to the query.

knex.select('*').from('users').join('contacts', function() {
+  this
+    .on('users.id', '=', 'contacts.id')
+    .onNotBetween('contacts.id', [5, 30])
+})
+

onJsonPathEquals

.onJsonPathEquals(column, range)

Adds a onJsonPathEquals clause to the query. The clause performs a join on value returned by two json paths on two json columns.

knex('cities')
+  .select(
+    'cities.name as cityName', 
+    'country.name as countryName'
+  )
+  .join('country', function () {
+    this.onJsonPathEquals(
+      // json column in cities
+      'country_name', 
+      // json path to country name in 'country_name' column
+      '$.country.name', 
+      // json column in country
+      'description', 
+       // json field in 'description' column
+      '$.name'
+    );
+  })
+

ClearClauses

clear

.clear(statement)

Clears the specified operator from the query. Available operators: 'select' alias 'columns', 'with', 'select', 'columns', 'where', 'union', 'join', 'group', 'order', 'having', 'limit', 'offset', 'counter', 'counters'. Counter(s) alias for method .clearCounter()

knex.select('email', 'name')
+  .from('users')
+  .where('id', '<', 10)
+  .clear('select')
+  .clear('where')
+

clearSelect

.clearSelect()

Deprecated, use clear('select'). Clears all select clauses from the query, excluding subqueries.

    knex.select('email', 'name').from('users').clearSelect()
+

clearWhere

.clearWhere()

Deprecated, use clear('where'). Clears all where clauses from the query, excluding subqueries.

knex.select('email', 'name')
+  .from('users')
+  .where('id', 1)
+  .clearWhere()
+

clearGroup

.clearGroup()

Deprecated, use clear('group'). Clears all group clauses from the query, excluding subqueries.

knex.select().from('users').groupBy('id').clearGroup()
+

clearOrder

.clearOrder()

Deprecated, use clear('order'). Clears all order clauses from the query, excluding subqueries.

knex.select()
+  .from('users')
+  .orderBy('name', 'desc')
+  .clearOrder()
+

clearHaving

.clearHaving()

Deprecated, use clear('having'). Clears all having clauses from the query, excluding subqueries.

knex.select()
+  .from('users')
+  .having('id', '>', 5)
+  .clearHaving()
+

clearCounters

.clearCounters()

Clears all increments/decrements clauses from the query.

  knex('accounts')
+    .where('id', '=', 1)
+    .update({ email: 'foo@bar.com' })
+    .decrement({
+      balance: 50,
+    })
+    .clearCounters()
+

distinct

.distinct([*columns])

Sets a distinct clause on the query. If the parameter is falsy or empty array, method falls back to '*'.

// select distinct 'first_name' from customers
+knex('customers')
+  .distinct('first_name', 'last_name')
+
+// select which eliminates duplicate rows
+knex('customers')
+  .distinct()
+

distinctOn

.distinctOn([*columns])

PostgreSQL only. Adds a distinctOn clause to the query.

knex('users').distinctOn('age')
+

groupBy

*.groupBy(names)

Adds a group by clause to the query.

knex('users').groupBy('count')
+

groupByRaw

.groupByRaw(sql)

Adds a raw group by clause to the query.

knex.select('year', knex.raw('SUM(profit)'))
+  .from('sales')
+  .groupByRaw('year WITH ROLLUP')
+

orderBy

.orderBy(column|columns, [direction], [nulls])

Adds an order by clause to the query. column can be string, or list mixed with string and object. nulls specify where the nulls values are put (can be 'first' or 'last').

Single Column:

knex('users').orderBy('email')
+
+knex('users').orderBy('name', 'desc')
+
+knex('users').orderBy('name', 'desc', 'first')
+

Multiple Columns:

knex('users').orderBy([
+  'email', { column: 'age', order: 'desc' }
+])
+
+knex('users').orderBy([
+  { column: 'email' }, 
+  { column: 'age', order: 'desc' }
+])
+
+knex('users').orderBy([
+  { column: 'email' }, 
+  { column: 'age', order: 'desc', nulls: 'last' }
+])
+

orderByRaw

.orderByRaw(sql)

Adds an order by raw clause to the query.

knex.select('*')
+  .from('table')
+  .orderByRaw('col DESC NULLS LAST')
+

Having Clauses

having

.having(column, operator, value)

Adds a having clause to the query.

knex('users')
+  .groupBy('count')
+  .orderBy('name', 'desc')
+  .having('count', '>', 100)
+

havingIn

.havingIn(column, values)

Adds a havingIn clause to the query.

knex.select('*')
+  .from('users')
+  .havingIn('id', [5, 3, 10, 17])
+

havingNotIn

.havingNotIn(column, values)

Adds a havingNotIn clause to the query.

knex.select('*')
+  .from('users')
+  .havingNotIn('id', [5, 3, 10, 17])
+

havingNull

.havingNull(column)

Adds a havingNull clause to the query.

knex.select('*').from('users').havingNull('email')
+

havingNotNull

.havingNotNull(column)

Adds a havingNotNull clause to the query.

knex.select('*').from('users').havingNotNull('email')
+

havingExists

.havingExists(builder | callback)

Adds a havingExists clause to the query.

knex.select('*').from('users').havingExists(function() {
+  this.select('*')
+    .from('accounts')
+    .whereRaw('users.account_id = accounts.id');
+})
+

havingNotExists

.havingNotExists(builder | callback)

Adds a havingNotExists clause to the query.

knex.select('*').from('users').havingNotExists(function() {
+  this.select('*')
+    .from('accounts')
+    .whereRaw('users.account_id = accounts.id');
+})
+

havingBetween

.havingBetween(column, range)

Adds a havingBetween clause to the query.

knex.select('*')
+  .from('users')
+  .havingBetween('id', [5, 10])
+

havingNotBetween

.havingNotBetween(column, range)

Adds a havingNotBetween clause to the query.

knex.select('*')
+  .from('users')
+  .havingNotBetween('id', [5, 10])
+

havingRaw

.havingRaw(sql, [bindings])

Adds a havingRaw clause to the query.

knex('users')
+  .groupBy('count')
+  .orderBy('name', 'desc')
+  .havingRaw('count > ?', [100])
+
`,610),e=[o];function c(u,i,l,k,r,d){return a(),s("div",null,e)}var f=n(p,[["render",c]]);export{h as __pageData,f as default}; diff --git a/assets/guide_query-builder.md.c2c99abe.lean.js b/assets/guide_query-builder.md.c2c99abe.lean.js new file mode 100644 index 00000000..11447024 --- /dev/null +++ b/assets/guide_query-builder.md.c2c99abe.lean.js @@ -0,0 +1 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const h='{"title":"Knex Query Builder","description":"","frontmatter":{},"headers":[{"level":2,"title":"Identifier Syntax","slug":"identifier-syntax"},{"level":2,"title":"Common","slug":"common"},{"level":3,"title":"knex","slug":"knex"},{"level":3,"title":"select","slug":"select"},{"level":3,"title":"as","slug":"as"},{"level":3,"title":"column","slug":"column"},{"level":3,"title":"from","slug":"from"},{"level":3,"title":"fromRaw","slug":"fromraw"},{"level":3,"title":"with","slug":"with"},{"level":3,"title":"withRecursive","slug":"withrecursive"},{"level":3,"title":"withMaterialized","slug":"withmaterialized"},{"level":3,"title":"withNotMaterialized","slug":"withnotmaterialized"},{"level":3,"title":"withSchema","slug":"withschema"},{"level":3,"title":"jsonExtract","slug":"jsonextract"},{"level":3,"title":"jsonSet","slug":"jsonset"},{"level":3,"title":"jsonInsert","slug":"jsoninsert"},{"level":3,"title":"jsonRemove","slug":"jsonremove"},{"level":3,"title":"offset","slug":"offset"},{"level":3,"title":"limit","slug":"limit"},{"level":3,"title":"union","slug":"union"},{"level":3,"title":"unionAll","slug":"unionall"},{"level":3,"title":"intersect","slug":"intersect"},{"level":3,"title":"except","slug":"except"},{"level":3,"title":"insert","slug":"insert"},{"level":3,"title":"onConflict","slug":"onconflict"},{"level":3,"title":"upsert","slug":"upsert"},{"level":3,"title":"update","slug":"update"},{"level":3,"title":"updateFrom","slug":"updatefrom"},{"level":3,"title":"del / delete","slug":"del-delete"},{"level":3,"title":"using","slug":"using"},{"level":3,"title":"returning","slug":"returning"},{"level":3,"title":"transacting","slug":"transacting"},{"level":3,"title":"skipLocked","slug":"skiplocked"},{"level":3,"title":"noWait","slug":"nowait"},{"level":3,"title":"count","slug":"count"},{"level":3,"title":"min","slug":"min"},{"level":3,"title":"max","slug":"max"},{"level":3,"title":"sum","slug":"sum"},{"level":3,"title":"avg","slug":"avg"},{"level":3,"title":"increment","slug":"increment"},{"level":3,"title":"decrement","slug":"decrement"},{"level":3,"title":"truncate","slug":"truncate"},{"level":3,"title":"pluck","slug":"pluck"},{"level":3,"title":"first","slug":"first"},{"level":3,"title":"hintComment","slug":"hintcomment"},{"level":3,"title":"comment","slug":"comment"},{"level":3,"title":"clone","slug":"clone"},{"level":3,"title":"denseRank","slug":"denserank"},{"level":3,"title":"rank","slug":"rank"},{"level":3,"title":"rowNumber","slug":"rownumber"},{"level":3,"title":"partitionBy","slug":"partitionby"},{"level":3,"title":"modify","slug":"modify"},{"level":3,"title":"columnInfo","slug":"columninfo"},{"level":3,"title":"debug","slug":"debug"},{"level":3,"title":"connection","slug":"connection"},{"level":3,"title":"options","slug":"options"},{"level":3,"title":"queryContext","slug":"querycontext"},{"level":2,"title":"Where Clauses","slug":"where-clauses"},{"level":3,"title":"where","slug":"where"},{"level":3,"title":"whereNot","slug":"wherenot"},{"level":3,"title":"whereIn","slug":"wherein"},{"level":3,"title":"whereNotIn","slug":"wherenotin"},{"level":3,"title":"whereNull","slug":"wherenull"},{"level":3,"title":"whereNotNull","slug":"wherenotnull"},{"level":3,"title":"whereExists","slug":"whereexists"},{"level":3,"title":"whereNotExists","slug":"wherenotexists"},{"level":3,"title":"whereBetween","slug":"wherebetween"},{"level":3,"title":"whereNotBetween","slug":"wherenotbetween"},{"level":3,"title":"whereRaw","slug":"whereraw"},{"level":3,"title":"whereLike","slug":"wherelike"},{"level":3,"title":"whereILike","slug":"whereilike"},{"level":3,"title":"whereJsonObject","slug":"wherejsonobject"},{"level":3,"title":"whereJsonPath","slug":"wherejsonpath"},{"level":3,"title":"whereJsonSupersetOf","slug":"wherejsonsupersetof"},{"level":3,"title":"whereJsonSubsetOf","slug":"wherejsonsubsetof"},{"level":2,"title":"Join Methods","slug":"join-methods"},{"level":3,"title":"join","slug":"join"},{"level":3,"title":"innerJoin","slug":"innerjoin"},{"level":3,"title":"leftJoin","slug":"leftjoin"},{"level":3,"title":"leftOuterJoin","slug":"leftouterjoin"},{"level":3,"title":"rightJoin","slug":"rightjoin"},{"level":3,"title":"rightOuterJoin","slug":"rightouterjoin"},{"level":3,"title":"fullOuterJoin","slug":"fullouterjoin"},{"level":3,"title":"crossJoin","slug":"crossjoin"},{"level":3,"title":"joinRaw","slug":"joinraw"},{"level":2,"title":"OnClauses","slug":"onclauses"},{"level":3,"title":"onIn","slug":"onin"},{"level":3,"title":"onNotIn","slug":"onnotin"},{"level":3,"title":"onNull","slug":"onnull"},{"level":3,"title":"onNotNull","slug":"onnotnull"},{"level":3,"title":"onExists","slug":"onexists"},{"level":3,"title":"onNotExists","slug":"onnotexists"},{"level":3,"title":"onBetween","slug":"onbetween"},{"level":3,"title":"onNotBetween","slug":"onnotbetween"},{"level":3,"title":"onJsonPathEquals","slug":"onjsonpathequals"},{"level":2,"title":"ClearClauses","slug":"clearclauses"},{"level":3,"title":"clear","slug":"clear"},{"level":3,"title":"clearSelect","slug":"clearselect"},{"level":3,"title":"clearWhere","slug":"clearwhere"},{"level":3,"title":"clearGroup","slug":"cleargroup"},{"level":3,"title":"clearOrder","slug":"clearorder"},{"level":3,"title":"clearHaving","slug":"clearhaving"},{"level":3,"title":"clearCounters","slug":"clearcounters"},{"level":3,"title":"distinct","slug":"distinct"},{"level":3,"title":"distinctOn","slug":"distincton"},{"level":3,"title":"groupBy","slug":"groupby"},{"level":3,"title":"groupByRaw","slug":"groupbyraw"},{"level":3,"title":"orderBy","slug":"orderby"},{"level":3,"title":"orderByRaw","slug":"orderbyraw"},{"level":2,"title":"Having Clauses","slug":"having-clauses"},{"level":3,"title":"having","slug":"having"},{"level":3,"title":"havingIn","slug":"havingin"},{"level":3,"title":"havingNotIn","slug":"havingnotin"},{"level":3,"title":"havingNull","slug":"havingnull"},{"level":3,"title":"havingNotNull","slug":"havingnotnull"},{"level":3,"title":"havingExists","slug":"havingexists"},{"level":3,"title":"havingNotExists","slug":"havingnotexists"},{"level":3,"title":"havingBetween","slug":"havingbetween"},{"level":3,"title":"havingNotBetween","slug":"havingnotbetween"},{"level":3,"title":"havingRaw","slug":"havingraw"}],"relativePath":"guide/query-builder.md"}',p={},o=t("",610),e=[o];function c(u,i,l,k,r,d){return a(),s("div",null,e)}var f=n(p,[["render",c]]);export{h as __pageData,f as default}; diff --git a/assets/guide_raw.md.bcfb9bab.js b/assets/guide_raw.md.bcfb9bab.js new file mode 100644 index 00000000..5d64597d --- /dev/null +++ b/assets/guide_raw.md.bcfb9bab.js @@ -0,0 +1,63 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const h='{"title":"Raw","description":"","frontmatter":{},"headers":[{"level":2,"title":"Raw Parameter Binding","slug":"raw-parameter-binding"},{"level":2,"title":"Raw Expressions","slug":"raw-expressions"},{"level":2,"title":"Raw Queries","slug":"raw-queries"},{"level":2,"title":"Wrapped Queries","slug":"wrapped-queries"}],"relativePath":"guide/raw.md"}',p={},e=t(`

Raw

Sometimes you may need to use a raw expression in a query. Raw query object may be injected pretty much anywhere you want, and using proper bindings can ensure your values are escaped properly, preventing SQL-injection attacks.

Raw Parameter Binding

One can parameterize sql given to knex.raw(sql, bindings). Parameters can be positional named. One can also choose if parameter should be treated as value or as sql identifier e.g. in case of 'TableName.ColumnName' reference.

knex('users')
+  .select(knex.raw('count(*) as user_count, status'))
+  .where(knex.raw(1))
+  .orWhere(knex.raw('status <> ?', [1]))
+  .groupBy('status')
+

Positional bindings ? are interpreted as values and ?? are interpreted as identifiers.

knex('users').where(knex.raw('?? = ?', ['user.name', 1]))
+

Named bindings such as :name are interpreted as values and :name: interpreted as identifiers. Named bindings are processed so long as the value is anything other than undefined.

const raw = ':name: = :thisGuy or :name: = :otherGuy or :name: = :undefinedBinding'
+
+knex('users')
+  .where(
+    knex.raw(raw, {
+      name: 'users.name',
+      thisGuy: 'Bob',
+      otherGuy: 'Jay',
+      undefinedBinding: undefined
+  }))
+

For simpler queries where one only has a single binding, .raw can accept said binding as its second parameter.

knex('users')
+  .where(
+    knex.raw('LOWER("login") = ?', 'knex')
+  )
+  .orWhere(
+    knex.raw('accesslevel = ?', 1)
+  )
+  .orWhere(
+    knex.raw('updtime = ?', '01-01-2016')
+  )
+

Since there is no unified syntax for array bindings, instead you need to treat them as multiple values by adding ? directly in your query.

const myArray = [1,2,3]
+knex.raw('select * from users where id in (' + myArray.map(_ => '?').join(',') + ')', [...myArray]);
+
+

query will become:

select * from users where id in (?, ?, ?) /* with bindings [1,2,3] */
+

To prevent replacement of ? one can use the escape sequence \\\\?.

knex.select('*')
+  .from('users')
+  .where('id', '=', 1)
+  .whereRaw('?? \\\\? ?', ['jsonColumn', 'jsonKey'])
+

To prevent replacement of named bindings one can use the escape sequence \\\\:.

knex.select('*')
+  .from('users')
+  .whereRaw(":property: = '\\\\:value' OR \\\\:property: = :value", {
+    property: 'name',
+    value: 'Bob'
+  })
+

Raw Expressions

Raw expressions are created by using knex.raw(sql, [bindings]) and passing this as a value for any value in the query chain.

knex('users').select(knex.raw('count(*) as user_count, status'))
+  .where(knex.raw(1))
+  .orWhere(knex.raw('status <> ?', [1]))
+  .groupBy('status')
+

Raw Queries

The knex.raw may also be used to build a full query and execute it, as a standard query builder query would be executed. The benefit of this is that it uses the connection pool and provides a standard interface for the different client libraries.

knex.raw('select * from users where id = ?', [1])
+  .then(function(resp) { /*...*/ });
+

Note that the response will be whatever the underlying sql library would typically return on a normal query, so you may need to look at the documentation for the base library the queries are executing against to determine how to handle the response.

Wrapped Queries

The raw query builder also comes with a wrap method, which allows wrapping the query in a value:

const subcolumn = knex.raw(
+    'select avg(salary) from employee where dept_no = e.dept_no'
+  )
+  .wrap('(', ') avg_sal_dept');
+
+knex.select('e.lastname', 'e.salary', subcolumn)
+  .from('employee as e')
+  .whereRaw('dept_no = e.dept_no')
+

Note that the example above be achieved more easily using the as method.

const subcolumn = knex.avg('salary')
+  .from('employee')
+  .whereRaw('dept_no = e.dept_no')
+  .as('avg_sal_dept');
+
+knex.select('e.lastname', 'e.salary', subcolumn)
+  .from('employee as e')
+  .whereRaw('dept_no = e.dept_no')
+
`,31),o=[e];function c(u,i,l,r,k,d){return a(),s("div",null,o)}var f=n(p,[["render",c]]);export{h as __pageData,f as default}; diff --git a/assets/guide_raw.md.bcfb9bab.lean.js b/assets/guide_raw.md.bcfb9bab.lean.js new file mode 100644 index 00000000..3c28087b --- /dev/null +++ b/assets/guide_raw.md.bcfb9bab.lean.js @@ -0,0 +1 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const h='{"title":"Raw","description":"","frontmatter":{},"headers":[{"level":2,"title":"Raw Parameter Binding","slug":"raw-parameter-binding"},{"level":2,"title":"Raw Expressions","slug":"raw-expressions"},{"level":2,"title":"Raw Queries","slug":"raw-queries"},{"level":2,"title":"Wrapped Queries","slug":"wrapped-queries"}],"relativePath":"guide/raw.md"}',p={},e=t("",31),o=[e];function c(u,i,l,r,k,d){return a(),s("div",null,o)}var f=n(p,[["render",c]]);export{h as __pageData,f as default}; diff --git a/assets/guide_ref.md.0b35ab7a.js b/assets/guide_ref.md.0b35ab7a.js new file mode 100644 index 00000000..6da9aa54 --- /dev/null +++ b/assets/guide_ref.md.0b35ab7a.js @@ -0,0 +1 @@ +import{_ as s,c as n,o as a,a as t}from"./app.ef097145.js";const q='{"title":"Ref","description":"","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage"},{"level":3,"title":"withSchema","slug":"withschema"},{"level":3,"title":"alias","slug":"alias"}],"relativePath":"guide/ref.md"}',p={},e=t('

Ref

Can be used to create references in a query, such as column- or tablenames. This is a good and shorter alternative to using knex.raw('??', 'tableName.columName') which essentially does the same thing.

Usage

knex.ref can be used essentially anywhere in a build-chain. Here is an example:

knex(knex.ref('Users').withSchema('TenantId'))\n  .where(knex.ref('Id'), 1)\n  .orWhere(knex.ref('Name'), 'Admin')\n  .select(['Id', knex.ref('Name').as('Username')])\n
select `Id`, `Name` as `Username` from `TenantId`.`Users` where `Id` = 1 or `Name` = 'Admin'\n
select "Id", "Name" as "Username" from "TenantId"."Users" where "Id" = 1 or "Name" = 'Admin'\n
select [Id], [Name] as [Username] from [TenantId].[Users] where [Id] = 1 or [Name] = 'Admin'\n
select `Id`, `Name` as `Username` from `TenantId`.`Users` where `Id` = 1 or `Name` = 'Admin'\n
select `Id`, `Name` as `Username` from `TenantId`.`Users` where `Id` = 1 or `Name` = 'Admin'\n
select "Id", "Name" as "Username" from "TenantId"."Users" where "Id" = 1 or "Name" = 'Admin'\n
select "Id", "Name" as "Username" from "TenantId"."Users" where "Id" = 1 or "Name" = 'Admin'\n
select "Id", "Name" as "Username" from "TenantId"."Users" where "Id" = 1 or "Name" = 'Admin'\n
select "Id", "Name" as "Username" from "TenantId"."Users" where "Id" = 1 or "Name" = 'Admin'\n
select `Id`, `Name` as `Username` from `TenantId`.`Users` where `Id` = 1 or `Name` = 'Admin'\n

withSchema

The Ref function supports schema using .withSchema(string):

knex(knex.ref('users').withSchema('TenantId')).select()\n

alias

Alias is supported using .alias(string)

knex('users')\n  .select(knex.ref('Id').as('UserId'))\n
select `Id` as `UserId` from `users`\n
select "Id" as "UserId" from "users"\n
select [Id] as [UserId] from [users]\n
select `Id` as `UserId` from `users`\n
select `Id` as `UserId` from `users`\n
select "Id" as "UserId" from "users"\n
select "Id" as "UserId" from "users"\n
select "Id" as "UserId" from "users"\n
select "Id" as "UserId" from "users"\n
select `Id` as `UserId` from `users`\n
',31),o=[e];function c(l,i,u,k,r,d){return a(),n("div",null,o)}var f=s(p,[["render",c]]);export{q as __pageData,f as default}; diff --git a/assets/guide_ref.md.0b35ab7a.lean.js b/assets/guide_ref.md.0b35ab7a.lean.js new file mode 100644 index 00000000..e0c0dd98 --- /dev/null +++ b/assets/guide_ref.md.0b35ab7a.lean.js @@ -0,0 +1 @@ +import{_ as s,c as n,o as a,a as t}from"./app.ef097145.js";const q='{"title":"Ref","description":"","frontmatter":{},"headers":[{"level":2,"title":"Usage","slug":"usage"},{"level":3,"title":"withSchema","slug":"withschema"},{"level":3,"title":"alias","slug":"alias"}],"relativePath":"guide/ref.md"}',p={},e=t("",31),o=[e];function c(l,i,u,k,r,d){return a(),n("div",null,o)}var f=s(p,[["render",c]]);export{q as __pageData,f as default}; diff --git a/assets/guide_schema-builder.md.1a118d1d.js b/assets/guide_schema-builder.md.1a118d1d.js new file mode 100644 index 00000000..43118d00 --- /dev/null +++ b/assets/guide_schema-builder.md.1a118d1d.js @@ -0,0 +1,263 @@ +import{_ as n,c as a,o as s,a as t}from"./app.ef097145.js";const m='{"title":"Schema Builder","description":"","frontmatter":{},"headers":[{"level":2,"title":"Essentials","slug":"essentials"},{"level":3,"title":"withSchema","slug":"withschema"},{"level":3,"title":"createTable","slug":"createtable"},{"level":3,"title":"createTableLike","slug":"createtablelike"},{"level":3,"title":"dropTable","slug":"droptable"},{"level":3,"title":"dropTableIfExists","slug":"droptableifexists"},{"level":3,"title":"renameTable","slug":"renametable"},{"level":3,"title":"hasTable","slug":"hastable"},{"level":3,"title":"hasColumn","slug":"hascolumn"},{"level":3,"title":"table","slug":"table"},{"level":3,"title":"alterTable","slug":"altertable"},{"level":3,"title":"createView","slug":"createview"},{"level":3,"title":"createViewOrReplace","slug":"createvieworreplace"},{"level":3,"title":"createMaterializedView","slug":"creatematerializedview"},{"level":3,"title":"refreshMaterializedView","slug":"refreshmaterializedview"},{"level":3,"title":"dropView","slug":"dropview"},{"level":3,"title":"dropViewIfExists","slug":"dropviewifexists"},{"level":3,"title":"dropMaterializedView","slug":"dropmaterializedview"},{"level":3,"title":"dropMaterializedViewIfExists","slug":"dropmaterializedviewifexists"},{"level":3,"title":"renameView","slug":"renameview"},{"level":3,"title":"alterView","slug":"alterview"},{"level":3,"title":"generateDdlCommands","slug":"generateddlcommands"},{"level":3,"title":"raw","slug":"raw"},{"level":3,"title":"queryContext","slug":"querycontext"},{"level":3,"title":"dropSchema","slug":"dropschema"},{"level":3,"title":"dropSchemaIfExists","slug":"dropschemaifexists"},{"level":2,"title":"Schema Building","slug":"schema-building"},{"level":3,"title":"dropColumn","slug":"dropcolumn"},{"level":3,"title":"dropColumns","slug":"dropcolumns"},{"level":3,"title":"renameColumn","slug":"renamecolumn"},{"level":3,"title":"increments","slug":"increments"},{"level":3,"title":"integer","slug":"integer"},{"level":3,"title":"bigInteger","slug":"biginteger"},{"level":3,"title":"tinyint","slug":"tinyint"},{"level":3,"title":"smallint","slug":"smallint"},{"level":3,"title":"mediumint","slug":"mediumint"},{"level":3,"title":"bigint","slug":"bigint"},{"level":3,"title":"text","slug":"text"},{"level":3,"title":"string","slug":"string"},{"level":3,"title":"float","slug":"float"},{"level":3,"title":"double","slug":"double"},{"level":3,"title":"decimal","slug":"decimal"},{"level":3,"title":"boolean","slug":"boolean"},{"level":3,"title":"date","slug":"date"},{"level":3,"title":"datetime","slug":"datetime"},{"level":3,"title":"time","slug":"time"},{"level":3,"title":"timestamp","slug":"timestamp"},{"level":3,"title":"timestamps","slug":"timestamps"},{"level":3,"title":"dropTimestamps","slug":"droptimestamps"},{"level":3,"title":"binary","slug":"binary"},{"level":3,"title":"enum / enu","slug":"enum-enu"},{"level":3,"title":"json","slug":"json"},{"level":3,"title":"jsonb","slug":"jsonb"},{"level":3,"title":"uuid","slug":"uuid"},{"level":3,"title":"geometry","slug":"geometry"},{"level":3,"title":"geography","slug":"geography"},{"level":3,"title":"point","slug":"point"},{"level":3,"title":"comment","slug":"comment"},{"level":3,"title":"engine","slug":"engine"},{"level":3,"title":"charset","slug":"charset"},{"level":3,"title":"collate","slug":"collate"},{"level":3,"title":"inherits","slug":"inherits"},{"level":3,"title":"specificType","slug":"specifictype"},{"level":3,"title":"index","slug":"index"},{"level":3,"title":"dropIndex","slug":"dropindex"},{"level":3,"title":"setNullable","slug":"setnullable"},{"level":3,"title":"dropNullable","slug":"dropnullable"},{"level":3,"title":"primary","slug":"primary"},{"level":3,"title":"unique","slug":"unique"},{"level":3,"title":"foreign","slug":"foreign"},{"level":3,"title":"dropForeign","slug":"dropforeign"},{"level":3,"title":"dropUnique","slug":"dropunique"},{"level":3,"title":"dropPrimary","slug":"dropprimary"},{"level":3,"title":"queryContext","slug":"querycontext-1"},{"level":2,"title":"Chainable Methods","slug":"chainable-methods"},{"level":3,"title":"alter","slug":"alter"},{"level":3,"title":"index","slug":"index-1"},{"level":3,"title":"primary","slug":"primary-1"},{"level":3,"title":"unique","slug":"unique-1"},{"level":3,"title":"references","slug":"references"},{"level":3,"title":"inTable","slug":"intable"},{"level":3,"title":"onDelete","slug":"ondelete"},{"level":3,"title":"onUpdate","slug":"onupdate"},{"level":3,"title":"defaultTo","slug":"defaultto"},{"level":3,"title":"unsigned","slug":"unsigned"},{"level":3,"title":"notNullable","slug":"notnullable"},{"level":3,"title":"nullable","slug":"nullable"},{"level":3,"title":"first","slug":"first"},{"level":3,"title":"after","slug":"after"},{"level":3,"title":"comment","slug":"comment-1"},{"level":3,"title":"collate","slug":"collate-1"},{"level":2,"title":"View","slug":"view"},{"level":3,"title":"columns","slug":"columns"},{"level":3,"title":"as","slug":"as"},{"level":3,"title":"checkOption","slug":"checkoption"},{"level":3,"title":"localCheckOption","slug":"localcheckoption"},{"level":3,"title":"cascadedCheckOption","slug":"cascadedcheckoption"},{"level":2,"title":"Checks","slug":"checks"},{"level":3,"title":"check","slug":"check"},{"level":3,"title":"checkPositive","slug":"checkpositive"},{"level":3,"title":"checkNegative","slug":"checknegative"},{"level":3,"title":"checkIn","slug":"checkin"},{"level":3,"title":"checkNotIn","slug":"checknotin"},{"level":3,"title":"checkBetween","slug":"checkbetween"},{"level":3,"title":"checkLength","slug":"checklength"},{"level":3,"title":"checkRegex","slug":"checkregex"},{"level":3,"title":"dropChecks","slug":"dropchecks"}],"relativePath":"guide/schema-builder.md"}',e={},p=t(`

Schema Builder

The knex.schema is a getter function, which returns a stateful object containing the query. Therefore be sure to obtain a new instance of the knex.schema for every query. These methods return promises.

Essentials

withSchema

knex.schema.withSchema([schemaName])

Specifies the schema to be used when using the schema-building commands.

knex.schema.withSchema('public').createTable('users', function (table) {
+  table.increments();
+})
+

createTable

knex.schema.createTable(tableName, callback)

Creates a new table on the database, with a callback function to modify the table's structure, using the schema-building commands.

knex.schema.createTable('users', function (table) {
+  table.increments();
+  table.string('name');
+  table.timestamps();
+})
+

createTableLike

knex.schema.createTableLike(tableName, tableNameToCopy, [callback])

Creates a new table on the database based on another table. Copy only the structure : columns, keys and indexes (expected on SQL Server which only copy columns) and not the data. Callback function can be specified to add columns in the duplicated table.

knex.schema.createTableLike('new_users', 'users')
+
+// "new_users" table contains columns 
+// of users and two new columns 'age' and 'last_name'.
+knex.schema.createTableLike('new_users', 'users', (table) => {
+  table.integer('age');
+  table.string('last_name');
+})
+

dropTable

knex.schema.dropTable(tableName)

Drops a table, specified by tableName.

knex.schema.dropTable('users')
+

dropTableIfExists

knex.schema.dropTableIfExists(tableName)

Drops a table conditionally if the table exists, specified by tableName.

knex.schema.dropTableIfExists('users')
+

renameTable

knex.schema.renameTable(from, to)

Renames a table from a current tableName to another.

knex.schema.renameTable('old_users', 'users')
+

hasTable

knex.schema.hasTable(tableName)

Checks for a table's existence by tableName, resolving with a boolean to signal if the table exists.

knex.schema.hasTable('users').then(function(exists) {
+  if (!exists) {
+    return knex.schema.createTable('users', function(t) {
+      t.increments('id').primary();
+      t.string('first_name', 100);
+      t.string('last_name', 100);
+      t.text('bio');
+    });
+  }
+});
+

hasColumn

knex.schema.hasColumn(tableName, columnName)

Checks if a column exists in the current table, resolves the promise with a boolean, true if the column exists, false otherwise.

table

knex.schema.table(tableName, callback)

Chooses a database table, and then modifies the table, using the Schema Building functions inside of the callback.

knex.schema.table('users', function (table) {
+  table.dropColumn('name');
+  table.string('first_name');
+  table.string('last_name');
+})
+

alterTable

knex.schema.alterTable(tableName, callback)

Chooses a database table, and then modifies the table, using the Schema Building functions inside of the callback.

knex.schema.alterTable('users', function (table) {
+  table.dropColumn('name');
+  table.string('first_name');
+  table.string('last_name');
+})
+

createView

knex.schema.createView(tableName, callback)

Creates a new view on the database, with a callback function to modify the view's structure, using the schema-building commands.

knex.schema.createView('users_view', function (view) {
+  view.columns(['first_name']);
+  view.as(knex('users').select('first_name').where('age','>', '18'));
+})
+

createViewOrReplace

knex.schema.createViewOrReplace(tableName, callback)

Creates a new view or replace it on the database, with a callback function to modify the view's structure, using the schema-building commands. You need to specify at least the same columns in same order (you can add extra columns). In SQLite, this function generate drop/create view queries (view columns can be different).

knex.schema.createViewOrReplace('users_view', function (view) {
+  view.columns(['first_name']);
+  view.as(knex('users').select('first_name').where('age','>', '18'));
+})
+

createMaterializedView

knex.schema.createMaterializedView(viewName, callback)

Creates a new materialized view on the database, with a callback function to modify the view's structure, using the schema-building commands. Only on PostgreSQL, CockroachDb, Redshift and Oracle.

knex.schema.createMaterializedView('users_view', function (view) {
+  view.columns(['first_name']);
+  view.as(knex('users').select('first_name').where('age','>', '18'));
+})
+

refreshMaterializedView

knex.schema.refreshMaterializedView(viewName)

Refresh materialized view on the database. Only on PostgreSQL, CockroachDb, Redshift and Oracle.

knex.schema.refreshMaterializedView('users_view')
+

dropView

knex.schema.dropView(viewName)

Drop view on the database.

knex.schema.dropView('users_view')
+

dropViewIfExists

knex.schema.dropViewIfExists(viewName)

Drop view on the database if exists.

knex.schema.dropViewIfExists('users_view')
+

dropMaterializedView

knex.schema.dropMaterializedView(viewName)

Drop materialized view on the database. Only on PostgreSQL, CockroachDb, Redshift and Oracle.

knex.schema.dropMaterializedView('users_view')
+

dropMaterializedViewIfExists

knex.schema.dropMaterializedViewIfExists(viewName)

Drop materialized view on the database if exists. Only on PostgreSQL, CockroachDb, Redshift and Oracle.

knex.schema.dropMaterializedViewIfExists('users_view')
+

renameView

knex.schema.renameView(viewName)

Rename a existing view in the database. Not supported by Oracle and SQLite.

knex.schema.renameView('users_view')
+

alterView

knex.schema.alterView(viewName)

Alter view to rename columns or change default values. Only available on PostgreSQL, MSSQL and Redshift.

knex.schema.alterView('view_test', function (view) {
+  view.column('first_name').rename('name_user');
+  view.column('bio').defaultTo('empty');
+})
+

generateDdlCommands

knex.schema.generateDdlCommands()

Generates complete SQL commands for applying described schema changes, without executing anything. Useful when knex is being used purely as a query builder. Generally produces same result as .toSQL(), with a notable exception with SQLite, which relies on asynchronous calls to the database for building part of its schema modification statements

const ddlCommands = knex.schema.alterTable(
+  'users',
+  (table) => {
+    table
+      .foreign('companyId')
+      .references('company.companyId')
+      .withKeyName('fk_fkey_company');
+  }
+).generateDdlCommands();
+

raw

knex.schema.raw(statement)

Run an arbitrary sql query in the schema builder chain.

knex.schema.raw("SET sql_mode='TRADITIONAL'")
+  .table('users', function (table) {
+    table.dropColumn('name');
+    table.string('first_name');
+    table.string('last_name');
+  })
+

queryContext

knex.schema.queryContext(context)

Allows configuring a context to be passed to the wrapIdentifier hook. The context can be any kind of value and will be passed to wrapIdentifier without modification.

knex.schema.queryContext({ foo: 'bar' })
+  .table('users', function (table) {
+    table.string('first_name');
+    table.string('last_name');
+  })
+

The context configured will be passed to wrapIdentifier for each identifier that needs to be formatted, including the table and column names. However, a different context can be set for the column names via table.queryContext.

Calling queryContext with no arguments will return any context configured for the schema builder instance.

dropSchema

knex.schema.dropSchema(schemaName, [cascade])

Drop a schema, specified by the schema's name, with optional cascade option (default to false). Only supported by PostgreSQL.

//drop schema 'public'
+knex.schema.dropSchema('public')
+//drop schema 'public' cascade
+knex.schema.dropSchema('public', true)
+

dropSchemaIfExists

knex.schema.dropSchemaIfExists(schemaName, [cascade])

Drop a schema conditionally if the schema exists, specified by the schema's name, with optional cascade option (default to false). Only supported by PostgreSQL.

//drop schema if exists 'public'
+knex.schema.dropSchemaIfExists('public')
+//drop schema if exists 'public' cascade
+knex.schema.dropSchemaIfExists('public', true)
+

Schema Building

dropColumn

table.dropColumn(name)

Drops a column, specified by the column's name

dropColumns

table.dropColumns(columns)

Drops multiple columns, taking a variable number of column names.

renameColumn

table.renameColumn(from, to)

Renames a column from one name to another.

increments

table.increments(name, options={[primaryKey: boolean = true])

Adds an auto incrementing column. In PostgreSQL this is a serial; in Amazon Redshift an integer identity(1,1). This will be used as the primary key for the table if the column isn't in another primary key. Also available is a bigIncrements if you wish to add a bigint incrementing number (in PostgreSQL bigserial). Note that a primary key is created by default if the column isn't in primary key (with primary function), but you can override this behaviour by passing the primaryKey option. If you use this function with primary function, the column is added to the composite primary key. With SQLite, autoincrement column need to be a primary key, so if primary function is used, primary keys are transformed in unique index. MySQL don't support autoincrement column without primary key, so multiple queries are generated to create int column, add increments column to composite primary key then modify the column to autoincrement column.

// create table 'users' 
+// with a primary key using 'increments()'
+knex.schema.createTable('users', function (table) {
+  table.increments('userId');
+  table.string('name');
+});
+
+// create table 'users' 
+// with a composite primary key ('userId', 'name'). 
+// increments doesn't generate primary key.
+knex.schema.createTable('users', function (table) {
+  table.primary(['userId', 'name']);
+  table.increments('userId');
+  table.string('name');
+});
+
+// reference the 'users' primary key in new table 'posts'
+knex.schema.createTable('posts', function (table) {
+  table.integer('author').unsigned().notNullable();
+  table.string('title', 30);
+  table.string('content');
+
+  table.foreign('author').references('userId').inTable('users');
+});
+

A primaryKey option may be passed, to disable to automatic primary key creation:

// create table 'users' 
+// with a primary key using 'increments()'
+// but also increments field 'other_id' 
+// that does not need primary key
+knex.schema.createTable('users', function (table) {
+  table.increments('id');
+  table.increments('other_id', { primaryKey: false });
+});
+

integer

table.integer(name, length)

Adds an integer column. On PostgreSQL you cannot adjust the length, you need to use other option such as bigInteger, etc

bigInteger

table.bigInteger(name)

In MySQL or PostgreSQL, adds a bigint column, otherwise adds a normal integer. Note that bigint data is returned as a string in queries because JavaScript may be unable to parse them without loss of precision.

tinyint

table.tinyint(name, length)

Adds a tinyint column

smallint

table.smallint(name)

Adds a smallint column

mediumint

table.mediumint(name)

Adds a mediumint column

bigint

table.bigint(name)

Adds a bigint column

text

table.text(name, [textType])

Adds a text column, with optional textType for MySql text datatype preference. textType may be mediumtext or longtext, otherwise defaults to text.

string

table.string(name, [length])

Adds a string column, with optional length defaulting to 255.

float

table.float(column, [precision], [scale])

Adds a float column, with optional precision (defaults to 8) and scale (defaults to 2).

double

table.double(column, [precision], [scale])

Adds a double column, with optional precision (defaults to 8) and scale (defaults to 2). In SQLite/MSSQL this is a float with no precision/scale; In PostgreSQL this is a double precision; In Oracle this is a number with matching precision/scale.

decimal

table.decimal(column, [precision], [scale])

Adds a decimal column, with optional precision (defaults to 8) and scale (defaults to 2). Specifying NULL as precision creates a decimal column that can store numbers of any precision and scale. (Only supported for Oracle, SQLite, Postgres)

boolean

table.boolean(name)

Adds a boolean column.

date

table.date(name)

Adds a date column.

datetime

table.datetime(name, options={[useTz: boolean], [precision: number]})

Adds a datetime column. By default PostgreSQL creates column with timezone (timestamptz type). This behaviour can be overriden by passing the useTz option (which is by default true for PostgreSQL). MySQL and MSSQL do not have useTz option.

A precision option may be passed:

table.datetime('some_time', { precision: 6 }).defaultTo(knex.fn.now(6))
+

time

table.time(name, [precision])

Adds a time column, with optional precision for MySQL. Not supported on Amazon Redshift.

In MySQL a precision option may be passed:

table.time('some_time', { precision: 6 })
+

timestamp

table.timestamp(name, options={[useTz: boolean], [precision: number]})

Adds a timestamp column. By default PostgreSQL creates column with timezone (timestamptz type) and MSSQL does not (datetime2). This behaviour can be overriden by passing the useTz option (which is by default false for MSSQL and true for PostgreSQL). MySQL does not have useTz option.

table.timestamp('created_at').defaultTo(knex.fn.now());
+

In PostgreSQL and MySQL a precision option may be passed:

table.timestamp('created_at', { precision: 6 }).defaultTo(knex.fn.now(6));
+

In PostgreSQL and MSSQL a timezone option may be passed:

table.timestamp('created_at', { useTz: true });
+

timestamps

table.timestamps([useTimestamps], [defaultToNow], [useCamelCase])

Adds created_at and updated_at columns on the database, setting each to datetime types. When true is passed as the first argument a timestamp type is used instead. Both columns default to being not null and using the current timestamp when true is passed as the second argument. Note that on MySQL the .timestamps() only have seconds precision, to get better precision use the .datetime or .timestamp methods directly with precision. If useCamelCase is true, the name of columns are createdAt and updatedAt.

INFO

PostgreSQL updated_at field will not automatically be updated. Please see this issue for details

dropTimestamps

table.dropTimestamps([useCamelCase])

Drops the columns created_at and updated_at from the table, which can be created via timestamps. If useCamelCase is true, the name of columns are createdAt and updatedAt.

binary

table.binary(name, [length])

Adds a binary column, with optional length argument for MySQL.

enum / enu

table.enu(col, values, [options])

Adds a enum column, (aliased to enu, as enum is a reserved word in JavaScript). Implemented as unchecked varchar(255) on Amazon Redshift. Note that the second argument is an array of values. Example:

table.enu('column', ['value1', 'value2'])
+

For Postgres, an additional options argument can be provided to specify whether or not to use Postgres's native TYPE:

table.enu('column', ['value1', 'value2'], { useNative: true, enumName: 'foo_type' })
+

It will use the values provided to generate the appropriate TYPE. Example:

CREATE TYPE "foo_type" AS ENUM ('value1', 'value2');
+

To use an existing native type across columns, specify 'existingType' in the options (this assumes the type has already been created):

INFO

Since the enum values aren't utilized for a native && existing type, the type being passed in for values is immaterial.

table.enu('column', null, { useNative: true, existingType: true, enumName: 'foo_type' })
+

If you want to use existing enums from a schema, different from the schema of your current table, specify 'schemaName' in the options:

table.enu('column', null, { useNative: true, existingType: true, enumName: 'foo_type', schemaName: 'public' })
+

Knex does not provide any way to alter enumerations after creation. To change an enumeration later on you must use Knex.raw, and the appropriate command for your database.

json

table.json(name)

Adds a json column, using the built-in json type in PostgreSQL, MySQL and SQLite, defaulting to a text column in older versions or in unsupported databases.

For PostgreSQL, due to incompatibility between native array and json types, when setting an array (or a value that could be an array) as the value of a json or jsonb column, you should use JSON.stringify() to convert your value to a string prior to passing it to the query builder, e.g.

knex.table('users')
+  .where({id: 1})
+  .update({json_data: JSON.stringify(mightBeAnArray)});
+

jsonb

table.jsonb(name)

Adds a jsonb column. Works similar to table.json(), but uses native jsonb type if possible.

uuid

table.uuid(name, options=({[useBinaryUuid:boolean],[primaryKey:boolean]})

Adds a uuid column - this uses the built-in uuid type in PostgreSQL, and falling back to a char(36) in other databases by default. If useBinaryUuid is true, binary(16) is used. See uuidToBin function to convert uuid in binary before inserting and binToUuid to convert binary uuid to uuid. If primaryKey is true, then for PostgreSQL the field will be configured as uuid primary key, for CockroackDB an additional default gen_random_uuid() is set on the type.

You may set the default value to the uuid helper function. Not supported by Redshift.

knex.schema.createTable(tblName, (table) => {
+  table.uuid('uuidColumn').defaultTo(knex.fn.uuid());
+});
+

geometry

table.geometry(name)

Adds a geometry column. Supported by SQLite, MSSQL and PostgreSQL.

knex.schema.createTable(tblName, (table) => {
+  table.geometry('geometryColumn');
+});
+

geography

table.geography(name)

Adds a geography column. Supported by SQLite, MSSQL and PostgreSQL (in PostGIS extension).

knex.schema.createTable(tblName, (table) => {
+  table.geography('geographyColumn');
+});
+

point

table.point(name)

Add a point column. Not supported by CockroachDB and MSSQL.

knex.schema.createTable(tblName, (table) => {
+  table.point('pointColumn');
+});
+

comment

table.comment(value)

Sets the comment for a table.

engine

table.engine(val)

Sets the engine for the database table, only available within a createTable call, and only applicable to MySQL.

charset

table.charset(val)

Sets the charset for the database table, only available within a createTable call, and only applicable to MySQL.

collate

table.collate(val)

Sets the collation for the database table, only available within a createTable call, and only applicable to MySQL.

inherits

table.inherits(val)

Sets the tables that this table inherits, only available within a createTable call, and only applicable to PostgreSQL.

specificType

table.specificType(name, type)

Sets a specific type for the column creation, if you'd like to add a column type that isn't supported here.

index

table.index(columns, [indexName], options=({[indexType: string], [storageEngineIndexType: 'btree'|'hash'], [predicate: QueryBuilder]}))

Adds an index to a table over the given columns. A default index name using the columns is used unless indexName is specified. In MySQL, the storage engine index type may be 'btree' or 'hash' index types, more info in Index Options section : https://dev.mysql.com/doc/refman/8.0/en/create-index.html. The indexType can be optionally specified for PostgreSQL and MySQL. Amazon Redshift does not allow creating an index. In PostgreSQL, SQLite and MSSQL a partial index can be specified by setting a 'where' predicate.

knex.table('users', function (table) {
+  table.index(['name', 'last_name'], 'idx_name_last_name', {
+    indexType: 'FULLTEXT',
+    storageEngineIndexType: 'hash',
+    predicate: knex.whereNotNull('email'),
+  });
+});
+

dropIndex

table.dropIndex(columns, [indexName])

Drops an index from a table. A default index name using the columns is used unless indexName is specified (in which case columns is ignored). Amazon Redshift does not allow creating an index.

setNullable

table.setNullable(column)

Makes table column nullable.

dropNullable

table.dropNullable(column)

Makes table column not nullable. Note that this operation will fail if there are already null values in this column.

primary

table.primary(columns, options=({[constraintName:string],[deferrable:'not deferrable'|'deferred'|'immediate']})

Create a primary key constraint on table using input columns. If you need to create a composite primary key, pass an array of columns to columns. Constraint name defaults to tablename_pkey unless constraintName is specified. On Amazon Redshift, all columns included in a primary key must be not nullable. Deferrable primary constraint are supported on Postgres and Oracle and can be set by passing deferrable option to options object.

knex.schema.alterTable('users', function(t) {
+  t.unique('email')
+})
+knex.schema.alterTable('job', function(t) {
+  t.primary('email',{constraintName:'users_primary_key',deferrable:'deferred'})
+})
+

INFO

If you want to chain primary() while creating new column you can use primary

unique

table.unique(columns, options={[indexName: string], [deferrable:'not deferrable'|'immediate'|'deferred'], [storageEngineIndexType:'btree'|'hash'], [useConstraint:true|false], [predicate: QueryBuilder]})

Adds an unique index to a table over the given columns. In MySQL, the storage engine index type may be 'btree' or 'hash' index types, more info in Index Options section : https://dev.mysql.com/doc/refman/8.0/en/create-index.html. A default index name using the columns is used unless indexName is specified. If you need to create a composite index, pass an array of column to columns. Deferrable unique constraint are supported on Postgres and Oracle and can be set by passing deferrable option to options object. In MSSQL and Postgres, you can set the useConstraint option to true to create a unique constraint instead of a unique index (defaults to false for MSSQL, true for Postgres without predicate, false for Postgres with predicate). In PostgreSQL, SQLite and MSSQL a partial unique index can be specified by setting a 'where' predicate.

knex.schema.alterTable('users', function(t) {
+  t.unique('email')
+})
+knex.schema.alterTable('job', function(t) {
+  t.unique(['account_id', 'program_id'], {indexName: 'job_composite_index', deferrable: 'deferred', storageEngineIndexType: 'hash'})
+})
+knex.schema.alterTable('job', function(t) {
+  t.unique(['account_id', 'program_id'], {indexName: 'job_composite_index', useConstraint: true})
+})
+knex.schema.alterTable('job', function(t) {
+  t.unique(['account_id', 'program_id'], {indexName: 'job_composite_index', predicate: knex.whereNotNull('account_id')})
+})
+

INFO

If you want to chain unique() while creating new column you can use unique

foreign

table.foreign(columns, [foreignKeyName])[.onDelete(statement).onUpdate(statement).withKeyName(foreignKeyName).deferrable(type)]

Adds a foreign key constraint to a table for an existing column using table.foreign(column).references(column) or multiple columns using table.foreign(columns).references(columns).inTable(table).

A default key name using the columns is used unless foreignKeyName is specified.

You can also chain onDelete() and/or onUpdate() to set the reference option (RESTRICT, CASCADE, SET NULL, NO ACTION) for the operation. You can also chain withKeyName() to override default key name that is generated from table and column names (result is identical to specifying second parameter to function foreign()).

Deferrable foreign constraint is supported on Postgres and Oracle and can be set by chaining .deferrable(type)

Note that using foreign() is the same as column.references(column) but it works for existing columns.

knex.schema.table('users', function (table) {
+  table.integer('user_id').unsigned()
+  table.foreign('user_id').references('Items.user_id_in_items').deferrable('deferred')
+})
+

dropForeign

table.dropForeign(columns, [foreignKeyName])

Drops a foreign key constraint from a table. A default foreign key name using the columns is used unless foreignKeyName is specified (in which case columns is ignored).

dropUnique

table.dropUnique(columns, [indexName])

Drops a unique key constraint from a table. A default unique key name using the columns is used unless indexName is specified (in which case columns is ignored).

dropPrimary

table.dropPrimary([constraintName])

Drops the primary key constraint on a table. Defaults to tablename_pkey unless constraintName is specified.

queryContext

table.queryContext(context)

Allows configuring a context to be passed to the wrapIdentifier hook for formatting table builder identifiers. The context can be any kind of value and will be passed to wrapIdentifier without modification.

knex.schema.table('users', function (table) {
+  table.queryContext({ foo: 'bar' });
+  table.string('first_name');
+  table.string('last_name');
+})
+

This method also enables overwriting the context configured for a schema builder instance via schema.queryContext:

knex.schema.queryContext('schema context')
+  .table('users', function (table) {
+    table.queryContext('table context');
+    table.string('first_name');
+    table.string('last_name');
+})
+

Note that it's also possible to overwrite the table builder context for any column in the table definition:

knex.schema.queryContext('schema context')
+  .table('users', function (table) {
+    table.queryContext('table context');
+    table.string('first_name').queryContext('first_name context');
+    table.string('last_name').queryContext('last_name context');
+})
+

Calling queryContext with no arguments will return any context configured for the table builder instance.

Chainable Methods

The following three methods may be chained on the schema building methods, as modifiers to the column.

alter

column.alter(options={[alterNullable: boolean = true, alterType: boolean = true])

Marks the column as an alter / modify, instead of the default add.

WARNING

This only works in .alterTable() and is not supported by SQlite or Amazon Redshift. Alter is not done incrementally over older column type so if you like to add notNullable and keep the old default value, the alter statement must contain both .notNullable().defaultTo(1).alter(). If one just tries to add .notNullable().alter() the old default value will be dropped. Nullable alterations are done only if alterNullable is true. Type alterations are done only if alterType is true.

knex.schema.alterTable('user', function(t) {
+  t.increments().primary(); // add
+  // drops previous default value from column,
+  // change type to string and add not nullable constraint
+  t.string('username', 35).notNullable().alter();
+  // drops both not null constraint and the default value
+  t.integer('age').alter();
+  // if alterNullable is false, drops only the default value
+  t.integer('age').alter({alterNullable : false});
+  // if alterType is false, type of column is not altered.
+  t.integer('age').alter({alterType : false});
+});
+

index

column.index([indexName], options=({[indexType: string], [storageEngineIndexType: 'btree'|'hash'], [predicate: QueryBuilder]}))

Specifies a field as an index. If an indexName is specified, it is used in place of the standard index naming convention of tableName_columnName. In MySQL, the storage engine index type may be 'btree' or 'hash' index types, more info in Index Options section : https://dev.mysql.com/doc/refman/8.0/en/create-index.html. The indexType can be optionally specified for PostgreSQL and MySQL. No-op if this is chained off of a field that cannot be indexed. In PostgreSQL, SQLite and MSSQL a partial index can be specified by setting a 'where' predicate.

primary

column.primary(options=({[constraintName:string],[deferrable:'not deferrable'|'deferred'|'immediate']}));

Sets a primary key constraint on column. Constraint name defaults to tablename_pkey unless constraintName is specified. On Amazon Redshift, all columns included in a primary key must be not nullable. Deferrable primary constraint are supported on Postgres and Oracle and can be set by passing deferrable option to options object.

knex.schema.table('users', function (table) {
+  table.integer('user_id').primary('email',{constraintName:'users_primary_key',deferrable:'deferred'})
+})
+

INFO

If you want to create primary constraint on existing column use primary

unique

column.unique(options={[indexName:string],[deferrable:'not deferrable'|'immediate'|'deferred']})

Sets the column as unique. On Amazon Redshift, this constraint is not enforced, but it is used by the query planner. Deferrable unique constraint are supported on Postgres and Oracle and can be set by passing deferrable option to options object.

knex.schema.table('users', function (table) {
+  table.integer('user_id').unique({indexName:'user_unique_id', deferrable:'immediate'})
+})
+

INFO

If you want to create unique constraint on existing column use unique

references

column.references(column)

Sets the "column" that the current column references as a foreign key. "column" can either be "." syntax, or just the column name followed up with a call to inTable to specify the table.

inTable

column.inTable(table)

Sets the "table" where the foreign key column is located after calling column.references.

onDelete

column.onDelete(command)

Sets the SQL command to be run "onDelete".

onUpdate

column.onUpdate(command)

Sets the SQL command to be run "onUpdate".

defaultTo

column.defaultTo(value, options={[constraintName: string = undefined]))

Sets the default value for the column on an insert.

In MSSQL a constraintName option may be passed to ensure a specific constraint name:

column.defaultTo('value', { constraintName: 'df_table_value' });
+

unsigned

column.unsigned()

Specifies a number as unsigned. Only for numeric values.

notNullable

column.notNullable()

Adds a not null on the current column being created.

nullable

column.nullable()

Default on column creation, this explicitly sets a field to be nullable.

first

column.first()

Sets the column to be inserted on the first position, only used in MySQL alter tables.

after

column.after(field)

Sets the column to be inserted after another, only used in MySQL alter tables.

comment

column.comment(value)

Sets the comment for a column.

knex.schema.createTable('accounts', function(t) {
+  t.increments().primary();
+  t.string('email').unique().comment('This is the email field');
+});
+

collate

column.collate(collation)

Sets the collation for a column (only works in MySQL). Here is a list of all available collations: https://dev.mysql.com/doc/refman/5.5/en/charset-charsets.html

knex.schema.createTable('users', function(t) {
+  t.increments();
+  t.string('email').unique().collate('utf8_unicode_ci');
+});
+

View

columns

view.columns([columnNames])

Specify the columns of the view.

knex.schema.createView('users_view', function (view) {
+  view.columns(['first_name', 'last_name']);
+  view.as(knex('users').select('first_name').where('age','>', '18'));
+});
+

as

view.as(selectQuery)

Specify the select query of the view.

checkOption

view.checkOption()

Add check option on the view definition. On OracleDb, MySQL, PostgreSQL and Redshift.

localCheckOption

view.localCheckOption()

Add local check option on the view definition. On MySQL, PostgreSQL and Redshift.

cascadedCheckOption

view.cascadedCheckOption()

Add cascaded check option on the view definition. On MySQL, PostgreSQL and Redshift.

Checks

check

table.check(checkPredicate, [bindings], [constraintName]))

Specify a check on table or column with raw predicate.

knex.schema.createTable('product', function (table) {
+  table.integer('price_min');
+  table.integer('price');
+  table.check('?? >= ??', ['price', 'price_min']);
+})
+

checkPositive

column.checkPositive([constraintName])

Specify a check on column that test if the value of column is positive.

knex.schema.createTable('product', function (table) {
+  table.integer('price').checkPositive();
+})
+

checkNegative

column.checkNegative([constraintName])

Specify a check on column that test if the value of column is negative.

knex.schema.createTable('product', function (table) {
+  table.integer('price_decrease').checkNegative();
+})
+

checkIn

column.checkIn(values, [constraintName])

Specify a check on column that test if the value of column is contained in a set of specified values.

knex.schema.createTable('product', function (table) {
+  table.string('type').checkIn(['table', 'chair', 'sofa']);
+})
+

checkNotIn

column.checkNotIn(values, [constraintName])

Specify a check on column that test if the value of column is not contains in a set of specified values.

knex.schema.createTable('product', function (table) {
+  table.string('type').checkNotIn(['boot', 'shoe']);
+})
+

checkBetween

column.checkBetween(values, [constraintName])

Specify a check on column that test if the value of column is within a range of values.

knex.schema.createTable('product', function (table) {
+  table.integer('price').checkBetween([0, 100]);
+})
+// You can add checks on multiple intervals
+knex.schema.createTable('product', function (table) {
+  table.integer('price').checkBetween([ [0, 20], [30,40] ]);
+})
+

checkLength

column.checkLength(operator, length, [constraintName])

Specify a check on column that test if the length of a string match the predicate.

knex.schema.createTable('product', function (table) {
+  // operator can be =, !=, <=, >=, <, >
+  t.varchar('phone').checkLength('=', 8);
+})
+

checkRegex

column.checkRegex(regex, [constraintName])

Specify a check on column that test if the value match the specified regular expression. In MSSQL only simple pattern matching in supported but not regex syntax.

knex.schema.createTable('product', function (table) {
+  table.string('phone').checkRegex('[0-9]{8}');
+  // In MSSQL, {8} syntax don't work,
+  // you need to duplicate [0-9].
+  table.string('phone').checkRegex('[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]');
+})
+

dropChecks

table.dropChecks([checkConstraintNames])

Drop checks constraint given an array of constraint names.

knex.schema.createTable('product', function (table) {
+  table.integer('price').checkPositive('price_check')
+  table.integer('price_proportion').checkBetween([0, 100],'price_proportion_check')
+  table.dropChecks(['price_check', 'price_proportion_check']);
+})
+
`,407),o=[p];function c(i,l,u,r,k,d){return s(),a("div",null,o)}var g=n(e,[["render",c]]);export{m as __pageData,g as default}; diff --git a/assets/guide_schema-builder.md.1a118d1d.lean.js b/assets/guide_schema-builder.md.1a118d1d.lean.js new file mode 100644 index 00000000..98f12021 --- /dev/null +++ b/assets/guide_schema-builder.md.1a118d1d.lean.js @@ -0,0 +1 @@ +import{_ as n,c as a,o as s,a as t}from"./app.ef097145.js";const m='{"title":"Schema Builder","description":"","frontmatter":{},"headers":[{"level":2,"title":"Essentials","slug":"essentials"},{"level":3,"title":"withSchema","slug":"withschema"},{"level":3,"title":"createTable","slug":"createtable"},{"level":3,"title":"createTableLike","slug":"createtablelike"},{"level":3,"title":"dropTable","slug":"droptable"},{"level":3,"title":"dropTableIfExists","slug":"droptableifexists"},{"level":3,"title":"renameTable","slug":"renametable"},{"level":3,"title":"hasTable","slug":"hastable"},{"level":3,"title":"hasColumn","slug":"hascolumn"},{"level":3,"title":"table","slug":"table"},{"level":3,"title":"alterTable","slug":"altertable"},{"level":3,"title":"createView","slug":"createview"},{"level":3,"title":"createViewOrReplace","slug":"createvieworreplace"},{"level":3,"title":"createMaterializedView","slug":"creatematerializedview"},{"level":3,"title":"refreshMaterializedView","slug":"refreshmaterializedview"},{"level":3,"title":"dropView","slug":"dropview"},{"level":3,"title":"dropViewIfExists","slug":"dropviewifexists"},{"level":3,"title":"dropMaterializedView","slug":"dropmaterializedview"},{"level":3,"title":"dropMaterializedViewIfExists","slug":"dropmaterializedviewifexists"},{"level":3,"title":"renameView","slug":"renameview"},{"level":3,"title":"alterView","slug":"alterview"},{"level":3,"title":"generateDdlCommands","slug":"generateddlcommands"},{"level":3,"title":"raw","slug":"raw"},{"level":3,"title":"queryContext","slug":"querycontext"},{"level":3,"title":"dropSchema","slug":"dropschema"},{"level":3,"title":"dropSchemaIfExists","slug":"dropschemaifexists"},{"level":2,"title":"Schema Building","slug":"schema-building"},{"level":3,"title":"dropColumn","slug":"dropcolumn"},{"level":3,"title":"dropColumns","slug":"dropcolumns"},{"level":3,"title":"renameColumn","slug":"renamecolumn"},{"level":3,"title":"increments","slug":"increments"},{"level":3,"title":"integer","slug":"integer"},{"level":3,"title":"bigInteger","slug":"biginteger"},{"level":3,"title":"tinyint","slug":"tinyint"},{"level":3,"title":"smallint","slug":"smallint"},{"level":3,"title":"mediumint","slug":"mediumint"},{"level":3,"title":"bigint","slug":"bigint"},{"level":3,"title":"text","slug":"text"},{"level":3,"title":"string","slug":"string"},{"level":3,"title":"float","slug":"float"},{"level":3,"title":"double","slug":"double"},{"level":3,"title":"decimal","slug":"decimal"},{"level":3,"title":"boolean","slug":"boolean"},{"level":3,"title":"date","slug":"date"},{"level":3,"title":"datetime","slug":"datetime"},{"level":3,"title":"time","slug":"time"},{"level":3,"title":"timestamp","slug":"timestamp"},{"level":3,"title":"timestamps","slug":"timestamps"},{"level":3,"title":"dropTimestamps","slug":"droptimestamps"},{"level":3,"title":"binary","slug":"binary"},{"level":3,"title":"enum / enu","slug":"enum-enu"},{"level":3,"title":"json","slug":"json"},{"level":3,"title":"jsonb","slug":"jsonb"},{"level":3,"title":"uuid","slug":"uuid"},{"level":3,"title":"geometry","slug":"geometry"},{"level":3,"title":"geography","slug":"geography"},{"level":3,"title":"point","slug":"point"},{"level":3,"title":"comment","slug":"comment"},{"level":3,"title":"engine","slug":"engine"},{"level":3,"title":"charset","slug":"charset"},{"level":3,"title":"collate","slug":"collate"},{"level":3,"title":"inherits","slug":"inherits"},{"level":3,"title":"specificType","slug":"specifictype"},{"level":3,"title":"index","slug":"index"},{"level":3,"title":"dropIndex","slug":"dropindex"},{"level":3,"title":"setNullable","slug":"setnullable"},{"level":3,"title":"dropNullable","slug":"dropnullable"},{"level":3,"title":"primary","slug":"primary"},{"level":3,"title":"unique","slug":"unique"},{"level":3,"title":"foreign","slug":"foreign"},{"level":3,"title":"dropForeign","slug":"dropforeign"},{"level":3,"title":"dropUnique","slug":"dropunique"},{"level":3,"title":"dropPrimary","slug":"dropprimary"},{"level":3,"title":"queryContext","slug":"querycontext-1"},{"level":2,"title":"Chainable Methods","slug":"chainable-methods"},{"level":3,"title":"alter","slug":"alter"},{"level":3,"title":"index","slug":"index-1"},{"level":3,"title":"primary","slug":"primary-1"},{"level":3,"title":"unique","slug":"unique-1"},{"level":3,"title":"references","slug":"references"},{"level":3,"title":"inTable","slug":"intable"},{"level":3,"title":"onDelete","slug":"ondelete"},{"level":3,"title":"onUpdate","slug":"onupdate"},{"level":3,"title":"defaultTo","slug":"defaultto"},{"level":3,"title":"unsigned","slug":"unsigned"},{"level":3,"title":"notNullable","slug":"notnullable"},{"level":3,"title":"nullable","slug":"nullable"},{"level":3,"title":"first","slug":"first"},{"level":3,"title":"after","slug":"after"},{"level":3,"title":"comment","slug":"comment-1"},{"level":3,"title":"collate","slug":"collate-1"},{"level":2,"title":"View","slug":"view"},{"level":3,"title":"columns","slug":"columns"},{"level":3,"title":"as","slug":"as"},{"level":3,"title":"checkOption","slug":"checkoption"},{"level":3,"title":"localCheckOption","slug":"localcheckoption"},{"level":3,"title":"cascadedCheckOption","slug":"cascadedcheckoption"},{"level":2,"title":"Checks","slug":"checks"},{"level":3,"title":"check","slug":"check"},{"level":3,"title":"checkPositive","slug":"checkpositive"},{"level":3,"title":"checkNegative","slug":"checknegative"},{"level":3,"title":"checkIn","slug":"checkin"},{"level":3,"title":"checkNotIn","slug":"checknotin"},{"level":3,"title":"checkBetween","slug":"checkbetween"},{"level":3,"title":"checkLength","slug":"checklength"},{"level":3,"title":"checkRegex","slug":"checkregex"},{"level":3,"title":"dropChecks","slug":"dropchecks"}],"relativePath":"guide/schema-builder.md"}',e={},p=t("",407),o=[p];function c(i,l,u,r,k,d){return s(),a("div",null,o)}var g=n(e,[["render",c]]);export{m as __pageData,g as default}; diff --git a/assets/guide_transactions.md.1e8e7cd5.js b/assets/guide_transactions.md.1e8e7cd5.js new file mode 100644 index 00000000..8b64f079 --- /dev/null +++ b/assets/guide_transactions.md.1e8e7cd5.js @@ -0,0 +1,176 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const y='{"title":"Transactions","description":"","frontmatter":{},"headers":[{"level":2,"title":"Transaction Modes","slug":"transaction-modes"}],"relativePath":"guide/transactions.md"}',p={},o=t(`

Transactions

Transactions are an important feature of relational databases, as they allow correct recovery from failures and keep a database consistent even in cases of system failure. All queries within a transaction are executed on the same database connection, and run the entire set of queries as a single unit of work. Any failure will mean the database will rollback any queries executed on that connection to the pre-transaction state.

Transactions are handled by passing a handler function into knex.transaction. The handler function accepts a single argument, an object which may be used in two ways:

  1. As the "promise aware" knex connection
  2. As an object passed into a query with transacting and eventually call commit or rollback.

Consider these two examples:

// Using trx as a query builder:
+knex.transaction(function(trx) {
+
+  const books = [
+    {title: 'Canterbury Tales'},
+    {title: 'Moby Dick'},
+    {title: 'Hamlet'}
+  ];
+
+  return trx
+    .insert({name: 'Old Books'}, 'id')
+    .into('catalogues')
+    .then(function(ids) {
+      books.forEach((book) => book.catalogue_id = ids[0]);
+      return trx('books').insert(books);
+    });
+})
+.then(function(inserts) {
+  console.log(inserts.length + ' new books saved.');
+})
+.catch(function(error) {
+  // If we get here, that means that 
+  // neither the 'Old Books' catalogues insert,
+  // nor any of the books inserts will have taken place.
+  console.error(error);
+});
+

And then this example:

// Using trx as a transaction object:
+knex.transaction(function(trx) {
+
+  const books = [
+    {title: 'Canterbury Tales'},
+    {title: 'Moby Dick'},
+    {title: 'Hamlet'}
+  ];
+
+  knex.insert({name: 'Old Books'}, 'id')
+    .into('catalogues')
+    .transacting(trx)
+    .then(function(ids) {
+      books.forEach((book) => book.catalogue_id = ids[0]);
+      return knex('books').insert(books).transacting(trx);
+    })
+    .then(trx.commit)
+    .catch(trx.rollback);
+})
+.then(function(inserts) {
+  console.log(inserts.length + ' new books saved.');
+})
+.catch(function(error) {
+  // If we get here, that means that 
+  // neither the 'Old Books' catalogues insert,
+  // nor any of the books inserts will have taken place.
+  console.error(error);
+});
+

Same example as above using await/async:

try {
+  await knex.transaction(async trx => {
+
+    const books = [
+      {title: 'Canterbury Tales'},
+      {title: 'Moby Dick'},
+      {title: 'Hamlet'}
+    ];
+    
+    const ids = await trx('catalogues')
+      .insert({
+        name: 'Old Books'
+      }, 'id')
+
+    books.forEach((book) => book.catalogue_id = ids[0])
+    const inserts = await trx('books').insert(books)
+    
+    console.log(inserts.length + ' new books saved.')
+  })
+} catch (error) {
+  // If we get here, that means that neither the 'Old Books' catalogues insert,
+  // nor any of the books inserts will have taken place.
+  console.error(error);
+}
+

Same example as above using another await/async approach:

try {
+  await knex.transaction(async trx => {
+
+    const books = [
+      {title: 'Canterbury Tales'},
+      {title: 'Moby Dick'},
+      {title: 'Hamlet'}
+    ];
+
+    const ids = await knex('catalogues')
+      .insert({
+        name: 'Old Books'
+      }, 'id')
+      .transacting(trx)
+
+    books.forEach(book => book.catalogue_id = ids[0])
+    await knex('books')
+      .insert(books)
+      .transacting(trx)
+
+    console.log(inserts.length + ' new books saved.')
+  })
+} catch (error) {
+  console.error(error);
+}
+

Throwing an error directly from the transaction handler function automatically rolls back the transaction, same as returning a rejected promise.

Notice that if a promise is not returned within the handler, it is up to you to ensure trx.commit, or trx.rollback are called, otherwise the transaction connection will hang.

Calling trx.rollback will return a rejected Promise. If you don't pass any argument to trx.rollback, a generic Error object will be created and passed in to ensure the Promise always rejects with something.

Note that Amazon Redshift does not support savepoints in transactions.

In some cases you may prefer to create transaction but only execute statements in it later. In such case call method transaction without a handler function:

// Using trx as a transaction object:
+const trx = await knex.transaction();
+
+const books = [
+  {title: 'Canterbury Tales'},
+  {title: 'Moby Dick'},
+  {title: 'Hamlet'}
+];
+
+trx('catalogues')
+  .insert({name: 'Old Books'}, 'id')
+  .then(function(ids) {
+    books.forEach((book) => book.catalogue_id = ids[0]);
+    return trx('books').insert(books);
+  })
+  .then(trx.commit)
+  .catch(trx.rollback);
+

If you want to create a reusable transaction instance, but do not want to actually start it until it is used, you can create a transaction provider instance. It will start transaction after being called for the first time, and return same transaction on subsequent calls:

// Does not start a transaction yet
+const trxProvider = knex.transactionProvider();
+
+const books = [
+  {title: 'Canterbury Tales'},
+  {title: 'Moby Dick'},
+  {title: 'Hamlet'}
+];
+
+// Starts a transaction
+const trx = await trxProvider();
+const ids = await trx('catalogues')
+  .insert({name: 'Old Books'}, 'id')
+books.forEach((book) => book.catalogue_id = ids[0]);
+await trx('books').insert(books);
+
+// Reuses same transaction
+const sameTrx = await trxProvider();
+const ids2 = await sameTrx('catalogues')
+  .insert({name: 'New Books'}, 'id')
+books.forEach((book) => book.catalogue_id = ids2[0]);
+await sameTrx('books').insert(books);
+

You can access the promise that gets resolved after transaction is rolled back explicitly by user or committed, or rejected if it gets rolled back by DB itself, when using either way of creating transaction, from field executionPromise:

const trxProvider = knex.transactionProvider();
+const trx = await trxProvider();
+const trxPromise = trx.executionPromise;
+
+const trx2 = await knex.transaction();
+const trx2Promise = trx2.executionPromise;
+
+const trxInitPromise = new Promise(async (resolve, reject) => {
+  knex.transaction((transaction) => {
+    resolve(transaction);
+  });
+});
+const trx3 = await trxInitPromise;
+const trx3Promise = trx3.executionPromise;
+

You can check if a transaction has been committed or rolled back with the method isCompleted:

const trx = await knex.transaction();
+trx.isCompleted(); // false
+await trx.commit();
+trx.isCompleted(); // true
+
+const trx2 = knex.transactionProvider();
+await trx2.rollback();
+trx2.isCompleted(); // true
+

You can check the property knex.isTransaction to see if the current knex instance you are working with is a transaction.

Transaction Modes

In case you need to specify an isolation level for your transaction, you can use a config parameter isolationLevel. Not supported by oracle and sqlite, options are read uncommitted, read committed, repeatable read, snapshot (mssql only), serializable.

// Simple read skew example
+const isolationLevel = 'read committed';
+const trx = await knex.transaction({isolationLevel});
+const result1 = await trx(tableName).select();
+await knex(tableName).insert({ id: 1, value: 1 });
+const result2 = await trx(tableName).select();
+await trx.commit();
+// result1 may or may not deep equal result2 depending on isolation level
+

You may also set the transaction mode as read only using the readOnly config parameter. It is currently only supported on mysql, postgres, and redshift.

const trx = await knex.transaction({ readOnly: true });
+// \u{1F4A5} Cannot \`INSERT\` while inside a \`READ ONLY\` transaction
+const result = await trx(tableName).insert({ id: 1, foo: 'bar' });
+
`,30),c=[o];function e(u,l,i,k,r,d){return a(),s("div",null,c)}var f=n(p,[["render",e]]);export{y as __pageData,f as default}; diff --git a/assets/guide_transactions.md.1e8e7cd5.lean.js b/assets/guide_transactions.md.1e8e7cd5.lean.js new file mode 100644 index 00000000..80f41cd2 --- /dev/null +++ b/assets/guide_transactions.md.1e8e7cd5.lean.js @@ -0,0 +1 @@ +import{_ as n,c as s,o as a,a as t}from"./app.ef097145.js";const y='{"title":"Transactions","description":"","frontmatter":{},"headers":[{"level":2,"title":"Transaction Modes","slug":"transaction-modes"}],"relativePath":"guide/transactions.md"}',p={},o=t("",30),c=[o];function e(u,l,i,k,r,d){return a(),s("div",null,c)}var f=n(p,[["render",e]]);export{y as __pageData,f as default}; diff --git a/assets/guide_utility.md.bc42b63a.js b/assets/guide_utility.md.bc42b63a.js new file mode 100644 index 00000000..a1766ef8 --- /dev/null +++ b/assets/guide_utility.md.bc42b63a.js @@ -0,0 +1,24 @@ +import{_ as n,c as a,o as s,a as t}from"./app.ef097145.js";const f='{"title":"Utility","description":"","frontmatter":{},"headers":[{"level":2,"title":"batchInsert","slug":"batchinsert"},{"level":2,"title":"now","slug":"now"},{"level":2,"title":"uuid","slug":"uuid"},{"level":2,"title":"uuidToBin","slug":"uuidtobin"},{"level":2,"title":"binToUuid","slug":"bintouuid"}],"relativePath":"guide/utility.md"}',p={},o=t(`

Utility

A collection of utilities that the knex library provides for convenience.

batchInsert

knex.batchInsert(tableName)

The batchInsert utility will insert a batch of rows wrapped inside a transaction (which is automatically created unless explicitly given a transaction using transacting), at a given chunkSize.

It's primarily designed to be used when you have thousands of rows to insert into a table.

By default, the chunkSize is set to 1000.

BatchInsert also allows for returning values and supplying transactions using transacting.

const rows = [{/*...*/}, {/*...*/}];
+const chunkSize = 30;
+knex.batchInsert('TableName', rows, chunkSize)
+  .returning('id')
+  .then(function(ids) { /*...*/ })
+  .catch(function(error) { /*...*/ });
+
+knex.transaction(function(tr) {
+  return knex.batchInsert('TableName', rows, chunkSize)
+    .transacting(tr)
+  })
+  .then(function() { /*...*/ })
+  .catch(function(error) { /*...*/ });
+

now

knex.fn.now(precision)

Return the current timestamp with a precision (optional)

table.datetime('some_time', { precision: 6 }).defaultTo(knex.fn.now(6))
+

uuid

knex.fn.uuid()

Return a uuid generation function. Not supported by Redshift

table.uuid('uuid').defaultTo(knex.fn.uuid())
+

uuidToBin

knex.fn.uuidToBin(uuid)

Convert a string uuid (char(36)) to a binary uuid (binary(16))

knex.schema.createTable('uuid_table', (t) => {
+  t.uuid('uuid_col_binary', { useBinaryUuid: true });
+});
+knex('uuid_table').insert({
+  uuid_col_binary:  knex.fn.uuidToBin('3f06af63-a93c-11e4-9797-00505690773f'),
+});
+

binToUuid

knex.fn.binToUuid(binaryUuid)

Convert a binary uuid (binary(16)) to a string uuid (char(36))

const res = await knex('uuid_table').select('uuid_col_binary');
+knex.fn.binToUuid(res[0].uuid_col_binary)
+
`,25),e=[o];function c(u,i,l,k,r,d){return s(),a("div",null,e)}var g=n(p,[["render",c]]);export{f as __pageData,g as default}; diff --git a/assets/guide_utility.md.bc42b63a.lean.js b/assets/guide_utility.md.bc42b63a.lean.js new file mode 100644 index 00000000..815bffe6 --- /dev/null +++ b/assets/guide_utility.md.bc42b63a.lean.js @@ -0,0 +1 @@ +import{_ as n,c as a,o as s,a as t}from"./app.ef097145.js";const f='{"title":"Utility","description":"","frontmatter":{},"headers":[{"level":2,"title":"batchInsert","slug":"batchinsert"},{"level":2,"title":"now","slug":"now"},{"level":2,"title":"uuid","slug":"uuid"},{"level":2,"title":"uuidToBin","slug":"uuidtobin"},{"level":2,"title":"binToUuid","slug":"bintouuid"}],"relativePath":"guide/utility.md"}',p={},o=t("",25),e=[o];function c(u,i,l,k,r,d){return s(),a("div",null,e)}var g=n(p,[["render",c]]);export{f as __pageData,g as default}; diff --git a/assets/index.md.0854ee54.js b/assets/index.md.0854ee54.js new file mode 100644 index 00000000..9cdbee49 --- /dev/null +++ b/assets/index.md.0854ee54.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as r,a}from"./app.ef097145.js";const p='{"title":"SQL Query Builder for Javascript","description":"","frontmatter":{"home":true,"heroImage":"/knex-logo.png","heroAlt":"Logo knex","heroText":"Knex.js","tagline":"SQL query builder","actionText":"View guide","actionLink":"/guide/","altActionText":"Star on GitHub","altActionLink":"https://github.com/knex/knex","title":"SQL Query Builder for Javascript"},"headers":[],"relativePath":"index.md"}',n={},o=a('

Knex.js (pronounced /k\u0259\u02C8n\u025Bks/) is a "batteries included" SQL query builder for PostgreSQL, CockroachDB, MSSQL, MySQL, MariaDB, SQLite3, Better-SQLite3, Oracle, and Amazon Redshift designed to be flexible, portable, and fun to use.

It features both traditional node style callbacks as well as a promise interface for cleaner async flow control, a stream interface, full-featured query and schema builders, transaction support (with savepoints), connection pooling and standardized responses between different query clients and dialects.

',1),s=[o];function i(l,c,d,u,g,f){return r(),t("div",null,s)}var _=e(n,[["render",i]]);export{p as __pageData,_ as default}; diff --git a/assets/index.md.0854ee54.lean.js b/assets/index.md.0854ee54.lean.js new file mode 100644 index 00000000..83f176ed --- /dev/null +++ b/assets/index.md.0854ee54.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as r,a}from"./app.ef097145.js";const p='{"title":"SQL Query Builder for Javascript","description":"","frontmatter":{"home":true,"heroImage":"/knex-logo.png","heroAlt":"Logo knex","heroText":"Knex.js","tagline":"SQL query builder","actionText":"View guide","actionLink":"/guide/","altActionText":"Star on GitHub","altActionLink":"https://github.com/knex/knex","title":"SQL Query Builder for Javascript"},"headers":[],"relativePath":"index.md"}',n={},o=a("",1),s=[o];function i(l,c,d,u,g,f){return r(),t("div",null,s)}var _=e(n,[["render",i]]);export{p as __pageData,_ as default}; diff --git a/assets/style.fcb294ee.css b/assets/style.fcb294ee.css new file mode 100644 index 00000000..4d7c0467 --- /dev/null +++ b/assets/style.fcb294ee.css @@ -0,0 +1 @@ +body .DocSearch{--docsearch-primary-color: var(--c-brand);--docsearch-modal-background: var(--c-white);--docsearch-hit-background: var(--c-white-dark);--docsearch-searchbox-focus-background: var(--c-white-dark);--docsearch-highlight-color: var(--docsearch-primary-color);--docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-text-lighter);--docsearch-hit-shadow: 0 1px 3px 0 var(--c-white);--docsearch-text-color: var(--c-text);--docsearch-muted-color: var(--c-text-dark-1);--docsearch-searchbox-background: var(--c-white);--docsearch-footer-background: var(--c-white);--docsearch-modal-shadow: 0 3px 8px 0 var(--c-white-dark);--docsearch-hit-color: var(--c-text);--docsearch-footer-shadow: 0 -1px 0 0 var(--c-white-dark);--docsearch-key-gradient: linear-gradient(-225deg,var(--c-white-dark),var(--c-white));--docsearch-key-shadow: inset 0 -2px 0 0 var(--c-white-dark),inset 0 0 1px 1px var(--c-white),0 1px 2px 1px var(--c-white-darker)}/*! @docsearch/css 3.0.0 | MIT License | © Algolia, Inc. and contributors | https://docsearch.algolia.com */:root{--docsearch-primary-color:#5468ff;--docsearch-text-color:#1c1e21;--docsearch-spacing:12px;--docsearch-icon-stroke-width:1.4;--docsearch-highlight-color:var(--docsearch-primary-color);--docsearch-muted-color:#969faf;--docsearch-container-background:rgba(101,108,133,.8);--docsearch-logo-color:#5468ff;--docsearch-modal-width:560px;--docsearch-modal-height:600px;--docsearch-modal-background:#f5f6f7;--docsearch-modal-shadow:inset 1px 1px 0 0 hsla(0,0%,100%,.5),0 3px 8px 0 #555a64;--docsearch-searchbox-height:56px;--docsearch-searchbox-background:#ebedf0;--docsearch-searchbox-focus-background:#fff;--docsearch-searchbox-shadow:inset 0 0 0 2px var(--docsearch-primary-color);--docsearch-hit-height:56px;--docsearch-hit-color:#444950;--docsearch-hit-active-color:#fff;--docsearch-hit-background:#fff;--docsearch-hit-shadow:0 1px 3px 0 #d4d9e1;--docsearch-key-gradient:linear-gradient(-225deg,#d5dbe4,#f8f8f8);--docsearch-key-shadow:inset 0 -2px 0 0 #cdcde6,inset 0 0 1px 1px #fff,0 1px 2px 1px rgba(30,35,90,.4);--docsearch-footer-height:44px;--docsearch-footer-background:#fff;--docsearch-footer-shadow:0 -1px 0 0 #e0e3e8,0 -3px 6px 0 rgba(69,98,155,.12)}html[data-theme=dark]{--docsearch-text-color:#f5f6f7;--docsearch-container-background:rgba(9,10,17,.8);--docsearch-modal-background:#15172a;--docsearch-modal-shadow:inset 1px 1px 0 0 #2c2e40,0 3px 8px 0 #000309;--docsearch-searchbox-background:#090a11;--docsearch-searchbox-focus-background:#000;--docsearch-hit-color:#bec3c9;--docsearch-hit-shadow:none;--docsearch-hit-background:#090a11;--docsearch-key-gradient:linear-gradient(-26.5deg,#565872,#31355b);--docsearch-key-shadow:inset 0 -2px 0 0 #282d55,inset 0 0 1px 1px #51577d,0 2px 2px 0 rgba(3,4,9,.3);--docsearch-footer-background:#1e2136;--docsearch-footer-shadow:inset 0 1px 0 0 rgba(73,76,106,.5),0 -4px 8px 0 rgba(0,0,0,.2);--docsearch-logo-color:#fff;--docsearch-muted-color:#7f8497}.DocSearch-Button{align-items:center;background:var(--docsearch-searchbox-background);border:0;border-radius:40px;color:var(--docsearch-muted-color);cursor:pointer;display:flex;font-weight:500;height:36px;justify-content:space-between;margin:0 0 0 16px;padding:0 8px;user-select:none}.DocSearch-Button:active,.DocSearch-Button:focus,.DocSearch-Button:hover{background:var(--docsearch-searchbox-focus-background);box-shadow:var(--docsearch-searchbox-shadow);color:var(--docsearch-text-color);outline:none}.DocSearch-Button-Container{align-items:center;display:flex}.DocSearch-Search-Icon{stroke-width:1.6}.DocSearch-Button .DocSearch-Search-Icon{color:var(--docsearch-text-color)}.DocSearch-Button-Placeholder{font-size:1rem;padding:0 12px 0 6px}.DocSearch-Button-Keys{display:flex;min-width:calc(40px + .8em)}.DocSearch-Button-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:3px;box-shadow:var(--docsearch-key-shadow);color:var(--docsearch-muted-color);display:flex;height:18px;justify-content:center;margin-right:.4em;padding-bottom:2px;position:relative;top:-1px;width:20px}@media (max-width:750px){.DocSearch-Button-Keys,.DocSearch-Button-Placeholder{display:none}}.DocSearch--active{overflow:hidden!important}.DocSearch-Container,.DocSearch-Container *{box-sizing:border-box}.DocSearch-Container{background-color:var(--docsearch-container-background);height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:200}.DocSearch-Container a{text-decoration:none}.DocSearch-Link{appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;font:inherit;margin:0;padding:0}.DocSearch-Modal{background:var(--docsearch-modal-background);border-radius:6px;box-shadow:var(--docsearch-modal-shadow);flex-direction:column;margin:60px auto auto;max-width:var(--docsearch-modal-width);position:relative}.DocSearch-SearchBar{display:flex;padding:var(--docsearch-spacing) var(--docsearch-spacing) 0}.DocSearch-Form{align-items:center;background:var(--docsearch-searchbox-focus-background);border-radius:4px;box-shadow:var(--docsearch-searchbox-shadow);display:flex;height:var(--docsearch-searchbox-height);margin:0;padding:0 var(--docsearch-spacing);position:relative;width:100%}.DocSearch-Input{appearance:none;background:transparent;border:0;color:var(--docsearch-text-color);flex:1;font:inherit;font-size:1.2em;height:100%;outline:none;padding:0 0 0 8px;width:80%}.DocSearch-Input::placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::-webkit-search-cancel-button,.DocSearch-Input::-webkit-search-decoration,.DocSearch-Input::-webkit-search-results-button,.DocSearch-Input::-webkit-search-results-decoration{display:none}.DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{margin:0;padding:0}.DocSearch-MagnifierLabel,.DocSearch-Reset{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,.DocSearch-LoadingIndicator{display:none}.DocSearch-Container--Stalled .DocSearch-LoadingIndicator{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Reset{animation:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;right:0;stroke-width:var(--docsearch-icon-stroke-width)}}.DocSearch-Reset{animation:fade-in .1s ease-in forwards;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;padding:2px;right:0;stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Reset[hidden]{display:none}.DocSearch-Reset:focus{outline:none}.DocSearch-Reset:hover{color:var(--docsearch-highlight-color)}.DocSearch-LoadingIndicator svg,.DocSearch-MagnifierLabel svg{height:24px;width:24px}.DocSearch-Cancel{display:none}.DocSearch-Dropdown{max-height:calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height));min-height:var(--docsearch-spacing);overflow-y:auto;overflow-y:overlay;padding:0 var(--docsearch-spacing);scrollbar-color:var(--docsearch-muted-color) var(--docsearch-modal-background);scrollbar-width:thin}.DocSearch-Dropdown::-webkit-scrollbar{width:12px}.DocSearch-Dropdown::-webkit-scrollbar-track{background:transparent}.DocSearch-Dropdown::-webkit-scrollbar-thumb{background-color:var(--docsearch-muted-color);border:3px solid var(--docsearch-modal-background);border-radius:20px}.DocSearch-Dropdown ul{list-style:none;margin:0;padding:0}.DocSearch-Label{font-size:.75em;line-height:1.6em}.DocSearch-Help,.DocSearch-Label{color:var(--docsearch-muted-color)}.DocSearch-Help{font-size:.9em;margin:0;user-select:none}.DocSearch-Title{font-size:1.2em}.DocSearch-Logo a{display:flex}.DocSearch-Logo svg{color:var(--docsearch-logo-color);margin-left:8px}.DocSearch-Hits:last-of-type{margin-bottom:24px}.DocSearch-Hits mark{background:none;color:var(--docsearch-highlight-color)}.DocSearch-HitsFooter{color:var(--docsearch-muted-color);display:flex;font-size:.85em;justify-content:center;margin-bottom:var(--docsearch-spacing);padding:var(--docsearch-spacing)}.DocSearch-HitsFooter a{border-bottom:1px solid;color:inherit}.DocSearch-Hit{border-radius:4px;display:flex;padding-bottom:4px;position:relative}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--deleting{transition:none}}.DocSearch-Hit--deleting{opacity:0;transition:all .25s linear}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit--favoriting{transition:none}}.DocSearch-Hit--favoriting{transform:scale(0);transform-origin:top center;transition:all .25s linear;transition-delay:.25s}.DocSearch-Hit a{background:var(--docsearch-hit-background);border-radius:4px;box-shadow:var(--docsearch-hit-shadow);display:block;padding-left:var(--docsearch-spacing);width:100%}.DocSearch-Hit-source{background:var(--docsearch-modal-background);color:var(--docsearch-highlight-color);font-size:.85em;font-weight:600;line-height:32px;margin:0 -4px;padding:8px 4px 0;position:sticky;top:0;z-index:10}.DocSearch-Hit-Tree{color:var(--docsearch-muted-color);height:var(--docsearch-hit-height);opacity:.5;stroke-width:var(--docsearch-icon-stroke-width);width:24px}.DocSearch-Hit[aria-selected=true] a{background-color:var(--docsearch-highlight-color)}.DocSearch-Hit[aria-selected=true] mark{text-decoration:underline}.DocSearch-Hit-Container{align-items:center;color:var(--docsearch-hit-color);display:flex;flex-direction:row;height:var(--docsearch-hit-height);padding:0 var(--docsearch-spacing) 0 0}.DocSearch-Hit-icon{height:20px;width:20px}.DocSearch-Hit-action,.DocSearch-Hit-icon{color:var(--docsearch-muted-color);stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Hit-action{align-items:center;display:flex;height:22px;width:22px}.DocSearch-Hit-action svg{display:block;height:18px;width:18px}.DocSearch-Hit-action+.DocSearch-Hit-action{margin-left:6px}.DocSearch-Hit-action-button{appearance:none;background:none;border:0;border-radius:50%;color:inherit;cursor:pointer;padding:2px}svg.DocSearch-Hit-Select-Icon{display:none}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Select-Icon{display:block}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:background-color .1s ease-in}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{transition:none}}.DocSearch-Hit-action-button:focus path,.DocSearch-Hit-action-button:hover path{fill:#fff}.DocSearch-Hit-content-wrapper{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;line-height:1.2em;margin:0 8px;overflow-x:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:80%}.DocSearch-Hit-title{font-size:.9em}.DocSearch-Hit-path{color:var(--docsearch-muted-color);font-size:.75em}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-action,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-icon,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-path,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-text,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-title,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Tree,.DocSearch-Hit[aria-selected=true] mark{color:var(--docsearch-hit-active-color)!important}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:none}}.DocSearch-ErrorScreen,.DocSearch-NoResults,.DocSearch-StartScreen{font-size:.9em;margin:0 auto;padding:36px 0;text-align:center;width:80%}.DocSearch-Screen-Icon{color:var(--docsearch-muted-color);padding-bottom:12px}.DocSearch-NoResults-Prefill-List{display:inline-block;padding-bottom:24px;text-align:left}.DocSearch-NoResults-Prefill-List ul{display:inline-block;padding:8px 0 0}.DocSearch-NoResults-Prefill-List li{list-style-position:inside;list-style-type:"\bb "}.DocSearch-Prefill{appearance:none;background:none;border:0;border-radius:1em;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;font-size:1em;font-weight:700;padding:0}.DocSearch-Prefill:focus,.DocSearch-Prefill:hover{outline:none;text-decoration:underline}.DocSearch-Footer{align-items:center;background:var(--docsearch-footer-background);border-radius:0 0 8px 8px;box-shadow:var(--docsearch-footer-shadow);display:flex;flex-direction:row-reverse;flex-shrink:0;height:var(--docsearch-footer-height);justify-content:space-between;padding:0 var(--docsearch-spacing);position:relative;user-select:none;width:100%;z-index:300}.DocSearch-Commands{color:var(--docsearch-muted-color);display:flex;list-style:none;margin:0;padding:0}.DocSearch-Commands li{align-items:center;display:flex}.DocSearch-Commands li:not(:last-of-type){margin-right:.8em}.DocSearch-Commands-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:2px;box-shadow:var(--docsearch-key-shadow);display:flex;height:18px;justify-content:center;margin-right:.4em;padding-bottom:1px;width:20px}@media (max-width:750px){:root{--docsearch-spacing:10px;--docsearch-footer-height:40px}.DocSearch-Dropdown{height:100%}.DocSearch-Container{height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);position:absolute}.DocSearch-Footer{border-radius:0;bottom:0;position:absolute}.DocSearch-Hit-content-wrapper{display:flex;position:relative;width:80%}.DocSearch-Modal{border-radius:0;box-shadow:none;height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh, 1vh)*100);margin:0;max-width:100%;width:100%}.DocSearch-Dropdown{max-height:calc(var(--docsearch-vh, 1vh)*100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))}.DocSearch-Cancel{appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;flex:none;font:inherit;font-size:1em;font-weight:500;margin-left:var(--docsearch-spacing);outline:none;overflow:hidden;padding:0;user-select:none;white-space:nowrap}.DocSearch-Commands,.DocSearch-Hit-Tree{display:none}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.algolia-search-box{padding-top:1px}@media (min-width: 720px){.algolia-search-box{padding-left:8px}}@media (min-width: 751px){.algolia-search-box{min-width:176.3px}.algolia-search-box .DocSearch-Button-Placeholder{padding-left:8px;font-size:.9rem;font-weight:500}}.DocSearch{--docsearch-primary-color: var(--c-brand);--docsearch-highlight-color: var(--docsearch-primary-color);--docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color);--docsearch-text-color: var(--c-text-light);--docsearch-muted-color: var(--c-text-lighter);--docsearch-searchbox-background: #f2f2f2}@import url(https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&family=Source+Code+Pro&display=swap);:root{--c-white: #ffffff;--c-white-dark: #f8f8f8;--c-black: #000000;--c-divider-light: rgba(60, 60, 67, .12);--c-divider-dark: rgba(84, 84, 88, .48);--c-text-light-1: #2c3e50;--c-text-light-2: #476582;--c-text-light-3: #90a4b7;--c-brand: #3eaf7c;--c-brand-light: #4abf8a;--font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;--font-family-mono: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;--z-index-navbar: 10;--z-index-sidebar: 6;--shadow-1: 0 1px 2px rgba(0, 0, 0, .04), 0 1px 2px rgba(0, 0, 0, .06);--shadow-2: 0 3px 12px rgba(0, 0, 0, .07), 0 1px 4px rgba(0, 0, 0, .07);--shadow-3: 0 12px 32px rgba(0, 0, 0, .1), 0 2px 6px rgba(0, 0, 0, .08);--shadow-4: 0 14px 44px rgba(0, 0, 0, .12), 0 3px 9px rgba(0, 0, 0, .12);--shadow-5: 0 18px 56px rgba(0, 0, 0, .16), 0 4px 12px rgba(0, 0, 0, .16);--header-height: 3.6rem}:root{--c-divider: var(--c-divider-light);--c-text: var(--c-text-light-1);--c-text-light: var(--c-text-light-2);--c-text-lighter: var(--c-text-light-3);--c-bg: var(--c-white);--c-bg-accent: var(--c-white-dark);--code-line-height: 24px;--code-font-family: var(--font-family-mono);--code-font-size: 14px;--code-inline-bg-color: rgba(27, 31, 35, .05);--code-bg-color: #282c34}*,:before,:after{box-sizing:border-box}html{line-height:1.4;font-size:16px;-webkit-text-size-adjust:100%}body{margin:0;width:100%;min-width:320px;min-height:100vh;line-height:1.4;font-family:var(--font-family-base);font-size:16px;font-weight:400;color:var(--c-text);background-color:var(--c-bg);direction:ltr;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}main{display:block}h1,h2,h3,h4,h5,h6{margin:0;line-height:1.25}h1,h2,h3,h4,h5,h6,strong,b{font-weight:600}h1:hover .header-anchor,h1:focus .header-anchor,h2:hover .header-anchor,h2:focus .header-anchor,h3:hover .header-anchor,h3:focus .header-anchor,h4:hover .header-anchor,h4:focus .header-anchor,h5:hover .header-anchor,h5:focus .header-anchor,h6:hover .header-anchor,h6:focus .header-anchor{opacity:1}h1{margin-top:1.5rem;font-size:1.9rem}@media screen and (min-width: 420px){h1{font-size:2.2rem}}h2{margin-top:2.25rem;margin-bottom:1.25rem;border-bottom:1px solid var(--c-divider);padding-bottom:.3rem;line-height:1.25;font-size:1.65rem}h2+h3{margin-top:1.5rem}h3{margin-top:2rem;font-size:1.35rem}h4{font-size:1.15rem}p,ol,ul{margin:1rem 0;line-height:1.7}a,area,button,[role=button],input,label,select,summary,textarea{touch-action:manipulation}a{text-decoration:none;color:var(--c-brand)}a:hover{text-decoration:underline}a.header-anchor{float:left;margin-top:.125em;margin-left:-.87em;padding-right:.23em;font-size:.85em;opacity:0}a.header-anchor:hover,a.header-anchor:focus{text-decoration:none}figure{margin:0}img{max-width:100%}ul,ol{padding-left:1.25em}li>ul,li>ol{margin:0}table{display:block;border-collapse:collapse;margin:1rem 0;overflow-x:auto}tr{border-top:1px solid #dfe2e5}tr:nth-child(2n){background-color:#f6f8fa}th,td{border:1px solid #dfe2e5;padding:.6em 1em}blockquote{margin:1rem 0;border-left:.2rem solid #dfe2e5;padding:.25rem 0 .25rem 1rem;font-size:1rem;color:#999}blockquote>p{margin:0}form{margin:0}.theme.sidebar-open .sidebar-mask{display:block}.theme.no-navbar>h1,.theme.no-navbar>h2,.theme.no-navbar>h3,.theme.no-navbar>h4,.theme.no-navbar>h5,.theme.no-navbar>h6{margin-top:1.5rem;padding-top:0}.theme.no-navbar aside{top:0}@media screen and (min-width: 720px){.theme.no-sidebar aside{display:none}.theme.no-sidebar main{margin-left:0}}.sidebar-mask{position:fixed;z-index:2;display:none;width:100vw;height:100vh}code{margin:0;border-radius:3px;padding:.25rem .5rem;font-family:var(--code-font-family);font-size:.85em;color:var(--c-text-light);background-color:var(--code-inline-bg-color)}code .token.deleted{color:#ec5975}code .token.inserted{color:var(--c-brand)}div[class*=language-]{position:relative;margin:1rem -1.5rem;background-color:var(--code-bg-color);overflow-x:auto}li>div[class*=language-]{border-radius:6px 0 0 6px;margin:1rem -1.5rem 1rem -1.25rem;line-height:initial}@media (min-width: 420px){div[class*=language-]{margin:1rem 0;border-radius:6px}li>div[class*=language-]{margin:1rem 0 1rem 0rem;border-radius:6px}}[class*=language-] pre,[class*=language-] code{text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none;background:transparent}[class*=language-] pre{position:relative;z-index:1;margin:0;padding:1.25rem 1.5rem;overflow-x:auto}[class*=language-] code{padding:0;line-height:var(--code-line-height);font-size:var(--code-font-size);color:#eee}.highlight-lines{position:absolute;top:0;bottom:0;left:0;padding:1.25rem 0;width:100%;line-height:var(--code-line-height);font-family:var(--code-font-family);font-size:var(--code-font-size);user-select:none;overflow:hidden}.highlight-lines .highlighted{background-color:#000000a8}div[class*=language-].line-numbers-mode{padding-left:3.5rem}.line-numbers-wrapper{position:absolute;top:0;bottom:0;left:0;z-index:3;border-right:1px solid rgba(0,0,0,.5);padding:1.25rem 0;width:3.5rem;text-align:center;line-height:var(--code-line-height);font-family:var(--code-font-family);font-size:var(--code-font-size);color:#888}div[class*=language-]:before{position:absolute;top:.6em;right:1em;z-index:2;font-size:.8rem;color:#888}div[class~=language-html]:before,div[class~=language-markup]:before{content:"html"}div[class~=language-md]:before,div[class~=language-markdown]:before{content:"md"}div[class~=language-css]:before{content:"css"}div[class~=language-sass]:before{content:"sass"}div[class~=language-scss]:before{content:"scss"}div[class~=language-less]:before{content:"less"}div[class~=language-stylus]:before{content:"styl"}div[class~=language-js]:before,div[class~=language-javascript]:before{content:"js"}div[class~=language-ts]:before,div[class~=language-typescript]:before{content:"ts"}div[class~=language-json]:before{content:"json"}div[class~=language-rb]:before,div[class~=language-ruby]:before{content:"rb"}div[class~=language-py]:before,div[class~=language-python]:before{content:"py"}div[class~=language-sh]:before,div[class~=language-bash]:before{content:"sh"}div[class~=language-php]:before{content:"php"}div[class~=language-go]:before{content:"go"}div[class~=language-rust]:before{content:"rust"}div[class~=language-java]:before{content:"java"}div[class~=language-c]:before{content:"c"}div[class~=language-yaml]:before{content:"yaml"}div[class~=language-dockerfile]:before{content:"dockerfile"}div[class~=language-vue]:before{content:"vue"}.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata{color:#999}.token.punctuation{color:#ccc}.token.tag,.token.attr-name,.token.namespace,.token.deleted{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}.custom-block.tip,.custom-block.info,.custom-block.warning,.custom-block.danger{margin:1rem 0;border-left:.5rem solid;padding:.1rem 1.5rem;overflow-x:auto}.custom-block.tip{background-color:#f3f5f7;border-color:var(--c-brand)}.custom-block.info{background-color:#f3f5f7;border-color:var(--c-text-light-2)}.custom-block.warning{border-color:#e7c000;color:#6b5900;background-color:#ffe5644d}.custom-block.warning .custom-block-title{color:#b29400}.custom-block.warning a{color:var(--c-text)}.custom-block.danger{border-color:#c00;color:#4d0000;background-color:#ffe6e6}.custom-block.danger .custom-block-title{color:#900}.custom-block.danger a{color:var(--c-text)}.custom-block.details{position:relative;display:block;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:#eee}.custom-block.details h4{margin-top:0}.custom-block.details figure:last-child,.custom-block.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-block.details summary{outline:none;cursor:pointer}.custom-block-title{margin-bottom:-.4rem;font-weight:600}.sidebar-links{margin:0;padding:0;list-style:none}.sidebar-link-item{display:block;margin:0;border-left:.25rem solid transparent;color:var(--c-text)}a.sidebar-link-item:hover{text-decoration:none;color:var(--c-brand)}a.sidebar-link-item.active{color:var(--c-brand)}.sidebar>.sidebar-links{padding:.75rem 0 5rem}@media (min-width: 720px){.sidebar>.sidebar-links{padding:1.5rem 0}}.sidebar>.sidebar-links>.sidebar-link+.sidebar-link{padding-top:.5rem}@media (min-width: 720px){.sidebar>.sidebar-links>.sidebar-link+.sidebar-link{padding-top:1.25rem}}.sidebar>.sidebar-links>.sidebar-link>.sidebar-link-item{padding:.35rem 1.5rem .35rem 1.25rem;font-size:1.1rem;font-weight:700}.sidebar>.sidebar-links>.sidebar-link>a.sidebar-link-item.active{border-left-color:var(--c-brand);font-weight:600}.sidebar>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>.sidebar-link-item{display:block;padding:.35rem 1.5rem .35rem 2rem;line-height:1.4;font-size:1rem;font-weight:400}.sidebar>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>a.sidebar-link-item.active{border-left-color:var(--c-brand);font-weight:600}.sidebar>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>.sidebar-link-item{display:block;padding:.3rem 1.5rem .3rem 3rem;line-height:1.4;font-size:.9rem;font-weight:400}.sidebar>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>.sidebar-link-item{display:block;padding:.3rem 1.5rem .3rem 4rem;line-height:1.4;font-size:.9rem;font-weight:400}.debug[data-v-bf835584]{box-sizing:border-box;position:fixed;right:8px;bottom:8px;z-index:9999;border-radius:4px;width:74px;height:32px;color:#eee;overflow:hidden;cursor:pointer;background-color:#000000d9;transition:all .15s ease}.debug[data-v-bf835584]:hover{background-color:#000000bf}.debug.open[data-v-bf835584]{right:0;bottom:0;width:100%;height:100%;margin-top:0;border-radius:0;padding:0;overflow:scroll}@media (min-width: 512px){.debug.open[data-v-bf835584]{width:512px}}.debug.open[data-v-bf835584]:hover{background-color:#000000d9}.title[data-v-bf835584]{margin:0;padding:6px 16px;line-height:20px;font-size:13px}.block[data-v-bf835584]{margin:2px 0 0;border-top:1px solid rgba(255,255,255,.16);padding:8px 16px;font-family:Hack,monospace;font-size:13px}.block+.block[data-v-bf835584]{margin-top:8px}.icon.outbound{position:relative;top:-1px;display:inline-block;vertical-align:middle;color:var(--c-text-lighter)}.item[data-v-b8818f8c]{display:block;padding:0 1.5rem;line-height:36px;font-size:1rem;font-weight:600;color:var(--c-text);white-space:nowrap}.item[data-v-b8818f8c]:hover,.item.active[data-v-b8818f8c]{text-decoration:none;color:var(--c-brand)}.item.external[data-v-b8818f8c]:hover{border-bottom-color:transparent;color:var(--c-text)}@media (min-width: 720px){.item[data-v-b8818f8c]{border-bottom:2px solid transparent;padding:0;line-height:24px;font-size:.9rem;font-weight:500}.item[data-v-b8818f8c]:hover,.item.active[data-v-b8818f8c]{border-bottom-color:var(--c-brand);color:var(--c-text)}}.home-hero[data-v-370f18c0]{margin:2.5rem 0 2.75rem;padding:0 1.5rem;text-align:center}@media (min-width: 420px){.home-hero[data-v-370f18c0]{margin:3.5rem 0}}@media (min-width: 720px){.home-hero[data-v-370f18c0]{margin:4rem 0 4.25rem}}.figure[data-v-370f18c0]{padding:0 1.5rem}.image[data-v-370f18c0]{display:block;margin:0 auto;width:auto;max-width:100%;max-height:280px}.title[data-v-370f18c0]{margin-top:1.5rem;font-size:2rem}@media (min-width: 420px){.title[data-v-370f18c0]{font-size:3rem}}@media (min-width: 720px){.title[data-v-370f18c0]{margin-top:2rem}}.tagline[data-v-370f18c0]{margin:0;margin-top:.25rem;line-height:1.3;font-size:1.2rem;color:var(--c-text-light)}@media (min-width: 420px){.tagline[data-v-370f18c0]{line-height:1.2;font-size:1.6rem}}.action[data-v-370f18c0]{margin-top:1.5rem;display:inline-block}.action.alt[data-v-370f18c0]{margin-left:1.5rem}@media (min-width: 420px){.action[data-v-370f18c0]{margin-top:2rem;display:inline-block}}.action[data-v-370f18c0] .item{display:inline-block;border-radius:6px;padding:0 20px;line-height:44px;font-size:1rem;font-weight:500;color:var(--c-bg);background-color:var(--c-brand);border:2px solid var(--c-brand);transition:background-color .1s ease}.action.alt[data-v-370f18c0] .item{background-color:var(--c-bg);color:var(--c-brand)}.action[data-v-370f18c0] .item:hover{text-decoration:none;color:var(--c-bg);background-color:var(--c-brand-light)}@media (min-width: 420px){.action[data-v-370f18c0] .item{padding:0 24px;line-height:52px;font-size:1.2rem;font-weight:500}}.home-features[data-v-e39c13e0]{margin:0 auto;padding:2.5rem 0 2.75rem;max-width:960px}.home-hero+.home-features[data-v-e39c13e0]{padding-top:0}@media (min-width: 420px){.home-features[data-v-e39c13e0]{padding:3.25rem 0 3.5rem}.home-hero+.home-features[data-v-e39c13e0]{padding-top:0}}@media (min-width: 720px){.home-features[data-v-e39c13e0]{padding-right:1.5rem;padding-left:1.5rem}}.wrapper[data-v-e39c13e0]{padding:0 1.5rem}.home-hero+.home-features .wrapper[data-v-e39c13e0]{border-top:1px solid var(--c-divider);padding-top:2.5rem}@media (min-width: 420px){.home-hero+.home-features .wrapper[data-v-e39c13e0]{padding-top:3.25rem}}@media (min-width: 720px){.wrapper[data-v-e39c13e0]{padding-right:0;padding-left:0}}.container[data-v-e39c13e0]{margin:0 auto;max-width:392px}@media (min-width: 720px){.container[data-v-e39c13e0]{max-width:960px}}.features[data-v-e39c13e0]{display:flex;flex-wrap:wrap;margin:-20px -24px}.feature[data-v-e39c13e0]{flex-shrink:0;padding:20px 24px;width:100%}@media (min-width: 720px){.feature[data-v-e39c13e0]{width:calc(100% / 3)}}.title[data-v-e39c13e0]{margin:0;border-bottom:0;line-height:1.4;font-size:1.25rem;font-weight:500}@media (min-width: 420px){.title[data-v-e39c13e0]{font-size:1.4rem}}.details[data-v-e39c13e0]{margin:0;line-height:1.6;font-size:1rem;color:var(--c-text-light)}.title+.details[data-v-e39c13e0]{padding-top:.25rem}.footer[data-v-30918238]{margin:0 auto;max-width:960px}@media (min-width: 720px){.footer[data-v-30918238]{padding:0 1.5rem}}.container[data-v-30918238]{padding:2rem 1.5rem 2.25rem}.home-hero+.footer .container[data-v-30918238],.home-features+.footer .container[data-v-30918238],.home-content+.footer .container[data-v-30918238]{border-top:1px solid var(--c-divider)}@media (min-width: 420px){.container[data-v-30918238]{padding:3rem 1.5rem 3.25rem}}.text[data-v-30918238]{margin:0;text-align:center;line-height:1.4;font-size:.9rem;color:var(--c-text-light)}.home[data-v-10122c92]{padding-top:var(--header-height)}.home-content[data-v-10122c92]{max-width:960px;margin:0 auto;padding:0 1.5rem}.nav-bar-title[data-v-cc01ef16]{font-size:1.3rem;font-weight:600;color:var(--c-text);display:flex;justify-content:center;align-items:center}.nav-bar-title[data-v-cc01ef16]:hover{text-decoration:none}.logo[data-v-cc01ef16]{margin-right:.75rem;height:1.3rem;vertical-align:bottom}.item[data-v-bbc27490]{display:block;padding:0 1.5rem 0 2.5rem;line-height:32px;font-size:.9rem;font-weight:500;color:var(--c-text);white-space:nowrap}@media (min-width: 720px){.item[data-v-bbc27490]{padding:0 24px 0 12px;line-height:32px;font-size:.85rem;font-weight:500;color:var(--c-text);white-space:nowrap}.item.active .arrow[data-v-bbc27490]{opacity:1}}.item[data-v-bbc27490]:hover,.item.active[data-v-bbc27490]{text-decoration:none;color:var(--c-brand)}.item.external[data-v-bbc27490]:hover{border-bottom-color:transparent;color:var(--c-text)}@media (min-width: 720px){.arrow[data-v-bbc27490]{display:inline-block;margin-right:8px;border-top:6px solid #ccc;border-right:4px solid transparent;border-bottom:0;border-left:4px solid transparent;vertical-align:middle;opacity:0;transform:translateY(-2px) rotate(-90deg)}}.nav-dropdown-link[data-v-56bf3a3f]{position:relative;height:36px;overflow:hidden;cursor:pointer}@media (min-width: 720px){.nav-dropdown-link[data-v-56bf3a3f]{height:auto;overflow:visible}.nav-dropdown-link:hover .dialog[data-v-56bf3a3f]{display:block}}.nav-dropdown-link.open[data-v-56bf3a3f]{height:auto}.button[data-v-56bf3a3f]{display:block;border:0;padding:0 1.5rem;width:100%;text-align:left;line-height:36px;font-family:var(--font-family-base);font-size:1rem;font-weight:600;color:var(--c-text);white-space:nowrap;background-color:transparent;cursor:pointer}.button[data-v-56bf3a3f]:focus{outline:0}@media (min-width: 720px){.button[data-v-56bf3a3f]{border-bottom:2px solid transparent;padding:0;line-height:24px;font-size:.9rem;font-weight:500}}.button-arrow[data-v-56bf3a3f]{display:inline-block;margin-top:-1px;margin-left:8px;border-top:6px solid #ccc;border-right:4px solid transparent;border-bottom:0;border-left:4px solid transparent;vertical-align:middle}.button-arrow.right[data-v-56bf3a3f]{transform:rotate(-90deg)}@media (min-width: 720px){.button-arrow.right[data-v-56bf3a3f]{transform:rotate(0)}}.dialog[data-v-56bf3a3f]{margin:0;padding:0;list-style:none}@media (min-width: 720px){.dialog[data-v-56bf3a3f]{display:none;position:absolute;top:26px;right:-8px;border-radius:6px;padding:12px 0;min-width:128px;background-color:var(--c-bg);box-shadow:var(--shadow-3)}}.nav-links[data-v-eab3edfe]{padding:.75rem 0;border-bottom:1px solid var(--c-divider)}@media (min-width: 720px){.nav-links[data-v-eab3edfe]{display:flex;padding:6px 0 0;align-items:center;border-bottom:0}.item+.item[data-v-eab3edfe]{padding-left:24px}}.sidebar-button{position:absolute;top:.6rem;left:1rem;display:none;padding:.6rem;cursor:pointer}.sidebar-button .icon{display:block;width:1.25rem;height:1.25rem}@media screen and (max-width: 719px){.sidebar-button{display:block}}.nav-bar[data-v-675d8756]{position:fixed;top:0;right:0;left:0;z-index:var(--z-index-navbar);display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--c-divider);padding:.7rem 1.5rem .7rem 4rem;height:var(--header-height);background-color:var(--c-bg)}@media (min-width: 720px){.nav-bar[data-v-675d8756]{padding:.7rem 1.5rem}}.flex-grow[data-v-675d8756]{flex-grow:1}.nav[data-v-675d8756]{display:none}@media (min-width: 720px){.nav[data-v-675d8756]{display:block}}.sidebar[data-v-83e92a68]{position:fixed;top:var(--header-height);bottom:0;left:0;z-index:var(--z-index-sidebar);border-right:1px solid var(--c-divider);width:16.4rem;background-color:var(--c-bg);overflow-y:auto;transform:translate(-100%);transition:transform .25s ease}@media (min-width: 720px){.sidebar[data-v-83e92a68]{transform:translate(0)}}@media (min-width: 960px){.sidebar[data-v-83e92a68]{width:20rem}}.sidebar.open[data-v-83e92a68]{transform:translate(0)}.nav[data-v-83e92a68]{display:block}@media (min-width: 720px){.nav[data-v-83e92a68]{display:none}}.link[data-v-1ed99556]{display:inline-block;font-size:1rem;font-weight:500;color:var(--c-text-light)}.link[data-v-1ed99556]:hover{text-decoration:none;color:var(--c-brand)}.icon[data-v-1ed99556]{margin-left:4px}.last-updated[data-v-abce3432]{display:inline-block;margin:0;line-height:1.4;font-size:.9rem;color:var(--c-text-light)}@media (min-width: 960px){.last-updated[data-v-abce3432]{font-size:1rem}}.prefix[data-v-abce3432]{display:inline-block;font-weight:500}.datetime[data-v-abce3432]{display:inline-block;margin-left:6px;font-weight:400}.page-footer[data-v-07c132fc]{padding-top:1rem;padding-bottom:1rem;overflow:auto}@media (min-width: 960px){.page-footer[data-v-07c132fc]{display:flex;justify-content:space-between;align-items:center}}.updated[data-v-07c132fc]{padding-top:4px}@media (min-width: 960px){.updated[data-v-07c132fc]{padding-top:0}}.next-and-prev-link[data-v-38ede35f]{padding-top:1rem}.container[data-v-38ede35f]{display:flex;justify-content:space-between;border-top:1px solid var(--c-divider);padding-top:1rem}.prev[data-v-38ede35f],.next[data-v-38ede35f]{display:flex;flex-shrink:0;width:50%}.prev[data-v-38ede35f]{justify-content:flex-start;padding-right:12px}.next[data-v-38ede35f]{justify-content:flex-end;padding-left:12px}.link[data-v-38ede35f]{display:inline-flex;align-items:center;max-width:100%;font-size:1rem;font-weight:500}.text[data-v-38ede35f]{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.icon[data-v-38ede35f]{display:block;flex-shrink:0;width:16px;height:16px;fill:var(--c-text);transform:translateY(1px)}.icon-prev[data-v-38ede35f]{margin-right:8px}.icon-next[data-v-38ede35f]{margin-left:8px}.page[data-v-7eddb2c4]{padding-top:var(--header-height)}@media (min-width: 720px){.page[data-v-7eddb2c4]{margin-left:16.4rem}}@media (min-width: 960px){.page[data-v-7eddb2c4]{margin-left:20rem}}.container[data-v-7eddb2c4]{margin:0 auto;padding:0 1.5rem 4rem;max-width:48rem}.content[data-v-7eddb2c4]{padding-bottom:1.5rem}@media (max-width: 420px){.content[data-v-7eddb2c4]{clear:both}}#ads-container{margin:0 auto}@media (min-width: 420px){#ads-container{position:relative;right:0;float:right;margin:-8px -8px 24px 24px;width:146px}}@media (max-width: 420px){#ads-container{height:105px;margin:1.75rem 0}}@media (min-width: 1400px){#ads-container{position:fixed;right:8px;bottom:8px}}.sql-dropdown{border-radius:8px;margin-left:1rem;padding:.5rem .4rem;cursor:pointer;color:var(--c-text);outline:none;background:var(--c-white-dark)}.toggle-dark[data-v-f817ae2e]{border-radius:50%;width:2rem;height:2rem;display:flex;align-items:center;justify-content:center;margin-left:.5rem;cursor:pointer;color:var(--c-text);transition:color .2s ease-in-out,background-color .2s ease-in-out}.toggle-dark[data-v-f817ae2e]:hover{color:var(--c-brand)}.toggle-dark svg[data-v-f817ae2e]{width:1.3rem;vertical-align:middle}.fade-enter-active[data-v-f817ae2e],.fade-leave-active[data-v-f817ae2e]{transition:opacity .2s ease}.fade-enter-from[data-v-f817ae2e],.fade-leave-to[data-v-f817ae2e]{opacity:0}@media (prefers-reduced-motion: reduce){.fade-enter-active[data-v-f817ae2e],.fade-leave-active[data-v-f817ae2e]{transition:none}}:root{--c-brand: #d26b38;--c-white-dark: #f5f5f5;--c-white-darker: #c5bab5;--c-brand-light: #ff8144;--font-family-base: "Poppins", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;--font-family-mono: "Source Code Pro", source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;--code-font-size: 14px;--code-line-height: 22px;--c-text-light-2: #cd7244;--c-text-light-3: #d26a38;--c-text-dark-1: #7f7f7f;--code-bg-color: #362f2d;--code-inline-bg-color: rgb(68 52 47 / 5%)}html{font-size:14px}.dark{--c-white: #2a2420;--c-white-dark: #342c27;--c-white-darker: #201d1c;--c-black: #ffffff;--c-text-light-1: #c5bab5;--c-text-light-2: #dfa486;--c-text-light-3: #d26a38;--c-text-dark-1: #9f9f9f;--code-bg-color: #201d1c;--code-inline-bg-color: #201d1c;color-scheme:dark}.nav-bar .item{font-size:1rem}.nav-bar .nav-bar-title{font-size:1.4rem}.sidebar>.sidebar-links>.sidebar-link+.sidebar-link{padding-top:.3rem}.sidebar>.sidebar-links>.sidebar-link>a.sidebar-link-item{font-weight:600}.sidebar>.sidebar-links>.sidebar-link>.sidebar-link-item{padding:.35rem 1.5rem .35rem 1.25rem}a.header-anchor{float:left;margin-top:.125em;margin-left:-1.1em;padding-right:.23em;font-size:.85em;opacity:0}.custom-block.warning{border-color:var(--c-brand);color:#914926;background-color:#d26a3826}.custom-block.warning .custom-block-title{color:#d66026}.custom-block.info{background-color:#fbf6f4}.dark .custom-block.info{background-color:#3a3532}.dark .custom-block.warning{color:var(--c-text-light-1);background-color:#462414}.dark .custom-block.warning .custom-block-title{color:#d66026}.home-hero{max-width:42rem;margin-left:auto!important;margin-right:auto!important}.home-hero .image{animation:spin 5s linear infinite;animation-play-state:paused}.home-hero:hover .image{animation-play-state:running}.theme .container{max-width:54rem}.container-home{max-width:42rem;margin:0 auto;background:var(--c-white-dark);padding:.5rem 2rem;border-radius:6px}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}[data-dialect]:before{content:attr(data-dialect)}.language-sql code{color:#ccc} diff --git a/changelog.html b/changelog.html new file mode 100644 index 00000000..41db40b6 --- /dev/null +++ b/changelog.html @@ -0,0 +1,24 @@ + + + + + + Changelog | Knex.js + + + + + + + + + + + + +

Changelog

2.5.1 - 12 July, 2023

Bug fixes

2.5.0 - 08 July, 2023

New features

  • Add uuid helper function #5617
  • Add nativeBindings option to better-sqlite3 options #5461
  • Add QueryBuilder#updateFrom #5386
  • Add readonly transaction access mode #5445
  • Add readonly option to Better-SQLite3 #5530
  • Add EXCEPT as a valid keyword #5357
  • Add ability to prepend query comments #5289
  • Add fetchAsString option #5484

Bug fixes

  • Avoid password leaks on query logs #5559
  • Add knex.mjs to files in package.json #5518
  • Handle numeric array elements in .orderBy() #5551
  • Attach error handler early enough #5552
  • Fix Returning * in Oracle #5598
  • Fix indexType option in Postgres #5601
  • Add mjs extension type #5616
  • Use implicit check on json fields for OracleDB #5478
  • Fix when manually close source stream #5466
  • Fix case sensitive issue with get table #5509

Typings

  • Add Object syntax overload to increment method #5512
  • Add object syntax overload to decrement method #5555
  • Fix typing for toSql #5594
  • Add ResolveTableType for .merge() #5605
  • Add missing types for havingNull and havingNotNull #5529
  • Add collate to the columnbuilder interface #5568
  • TableBuilder methods return the SchemaBuilder. #5486

2.4.2 - 22 January, 2023

Bug fixes

  • CLI: Fix incorrent EOL causing errors on Linux #5455

2.4.1 - 18 January, 2023

Bug fixes

  • Fix Postgres Malformed array literal 2.4.0 Regression - #5439

2.4.0 - 6 January, 2023

New features

  • Support partial unique indexes #5316
  • Make compiling SQL in error message optional #5282

Bug fixes

  • Insert array into json column #5321
  • Fix unexpected max acquire-timeout #5377
  • Fix: orWhereJson #5361
  • MySQL: Add assertion for basic where clause not to be object or array #1227
  • SQLite: Fix changing the default value of a boolean column in SQLite #5319

Typings

  • add missing type for 'expirationChecker' on PgConnectionConfig #5334

2.3.0 - 31 August, 2022

New features

  • PostgreSQL: Explicit jsonb support for custom pg clients #5201
  • SQLite: Support returning with sqlite3 and better-sqlite3 #5285
  • MSSQL: Implement mapBinding mssql dialect option #5292

Typings

  • Update types for TS 4.8 #5279
  • Fix typo #5267
  • Fix WhereJsonObject withCompositeTableType #5306
  • Fix AnalyticFunction type #5304
  • Infer specific column value type in aggregations #5297

2.2.0 - 18 July, 2022

New features

  • Inline primary key creation for postgres flavours #5233
  • SQLite: Add warning for undefined connection file #5223
  • MSSQL: Add JSON parameter support for connection #5200

Bug fixes

  • PostgreSQL: add primaryKey option for uuid #5212

Typings

  • Add promisable and better types #5222
  • Update raw query bind parameter type #5208

2.1.0 - 26 May, 2022

New features

  • Improve bundling experience to safely import dialects while using static paths #5142
  • Implement extendable builders #5041
  • PostgreSQL: Refresh materialized view concurrently #5166

Bug fixes

  • Use correct paths in package.json browser field #5174
  • MariaDB: Fix 'NULL' returned instead of NULL on MariaDB 10.2.6+ #5181
  • MySQL: fix hasColumn Error (hasColumn ('a_id') is true, but hasColumn('a_Id') is false) #5148
  • MSSQL: Fix .hasTable result when using .withSchema #5176
  • Oracle: correctly INSERTS Buffer #4869

Typings

  • Update type definitions for pg connection #5139

2.0.0 - 21 April, 2022

Breaking changes

  • Restore sqlite3 package #5136

Test / internal changes

  • Migrate Husky from 4 to 7 #5137
  • Migrate Jake to 10.8.5 #5138

1.0.7 - 13 March, 2022

Bug fixes

  • CLI: Fix cli migrate:make SQLite dependency #5106

1.0.6 - 12 March, 2022

Bug fixes

  • PostgreSQL: Wait for search path to be set before returning connection #5107
  • CLI: No client override during migrate:make #5109

1.0.5 - 05 March, 2022

New features

  • Override knexfile options with CLI options #4047

Bug fixes

  • Stringify json value in update #5063
  • Fix isModuleType() for yarn #4447
  • Wrapped Unions Fixes #5072
  • SQLite: Fix @vscode-sqlite3 error message #5081
  • CLI: Fix completed migration listing #5060

Typings

  • Make default generic parameters of Knex match the generic parameter types of knex #5021
  • Update knex types for TS 4.7 #5095

1.0.4 - 13 March, 2022

New features

  • Add whereLike functions #5044

Bug fixes

  • Fix orWhereJsonPath clause #5022
  • Subquery in on clause missing parenthesis #5049
  • Rework Union Wrapping #5030
  • Oracle: Fix batch inserts with DEFAULT values with OracleDB #2592 #5037

Typings

  • Fix types for "returning" methods #5031
  • createTableLike callback should be optional #5055

Documentation

1.0.3 - 11 February, 2022

Bug fixes

  • Fix error message for missing migration files #4937
  • Add withMaterialized and withNotMaterialized to method-constants #5009
  • PostgreSQL: Fix whereJsonPath queries #5011
  • PostgreSQL: Fix delete joins #5016
  • CockroachDB: Fix whereJsonPath queries #5011
  • MySQL: Create primary keys in same statement #5017

Typings

  • Fix type definition for getMigration in MigrationSource #4998
  • Fix argument type of alter method #4996

Improvements

  • Use async / await syntax in seeds as default #5005

Documentation

1.0.2 - 02 February, 2022

New features

  • Support of MATERIALIZED and NOT MATERIALIZED with WITH/CTE #4940
  • Add raw support in onConflict clause #4960
  • Alter nullable constraint when alterNullable is set to true #4730
  • Add alterType parameter for alter function #4967
  • Support string json in json values #4988
  • MySQL: add with clause #4508

Bug fixes

  • Fix error message for missing migration files #4937
  • Move deferrable to after on update/on delete #4976
  • Do not use sys.tables to find if a table exists #2328
  • PostgreSQL: Fix Order nulls #4989
  • MySQL: Fix collation when renaming column #2666
  • SQLite: Same boolean handling in better-sqlite3 as in sqlite3 #4982

Typings

  • WhereILike - fix typo #4941

1.0.1 - 16 January, 2022

Bug fixes

  • Fix package.json metadata

1.0.0 - 16 January, 2022

Breaking changes

  • Dropped support for Node 10;
  • Replaced unsupported sqlite3 driver with @vscode/sqlite3;
  • Changed data structure from RETURNING operation to be consistent with SELECT;
  • Changed Migrator to return list of migrations as objects consistently.

New features

  • Support fromRaw #4781
  • Support zero precision in timestamp/datetime #4784
  • Support whereLike and whereILike #4779
  • Add JSDoc (TS flavor) to stub files #4809
  • Allow skip binding in limit and offset #4811
  • Support creating a new table in the database based on another table #4821
  • Accept Raw on onIn joins #4830
  • Implement support for custom seed sources #4842
  • Add binary uuid option #4836
  • ForUpdate array parameter #4882
  • Add camel case to timestamps method #4803
  • Advanced JSON support #4859
  • Add type to TypeScript knexfile #4909
  • Checks Constraints Support #4874
  • Support creating multiple PKs with increments #4903
  • Enable wrapIdentifier for SQLite .hasTable #4915
  • MSSQL: Add support for unique constraint #4887
  • SQLite: New dialect, using better-sqlite3 driver #4871
  • SQLite: Switch to @vscode/sqlite3 #4866
  • SQLite: Support createViewOrReplace #4856
  • SQLite: Support RETURNING statements for better-sqlite3 driver #4934
  • PostgreSQL: Support JOIN and USING syntax for Delete Statement #4800

Bug fixes

  • Fix overzealous warning on use of whereNot with "in" or "between" #4780
  • Fix Union all + first syntax error #4799
  • Make view columns optional in create view like #4829
  • Insert lock row fix during migration #4865
  • Fix for createViewOrReplace #4856
  • SQLite: Fix foreign key constraints when altering a table #4189
  • MySQL: Validate connection fix #4794
  • MySQL: Set comment size warning limit to 1024 #4867

Typings

  • Allow string indexType in index creation #4791
  • Add missing ints typings #4832
  • Returning method types #4881
  • Improve columnInfo type #4868

0.95.15 - 22 December, 2021

Bug fixes

  • Oracle:
  • MariaDB: lock row fix during migration in MariaDB and Oracle #4865

0.95.14 - 09 November, 2021

Bug fixes

  • MySQL: mysql2 dialect validate connection fix #4794

0.95.13 - 02 November, 2021

Bug fixes

  • PostgreSQL: Support zero precision in timestamp/datetime #4784

Typings

  • Allow string indexType in index creation #4791

0.95.12 - 28 October, 2021

New features

  • New dialect: CockroachDB #4742
  • New dialect: pg-native #4327
  • CockroachDB: add support for upsert #4767
  • PostgreSQL: Support SELECT .. FOR NO KEY UPDATE / KEY SHARE row level locking clauses #4755
  • PostgreSQL: Add support for 'CASCADE' in PostgreSQL 'DROP SCHEMA' queries #4713
  • MySQL: Add storage engine index Type support to index() and unique() schema #4756
  • MSSQL: Support table.primary, table.unique variant with options object #4710
  • SQLite: Add setNullable support to SQLite #4684
  • Add geometry column building #4776
  • Add support for creating table copies #1373
  • Implement support for views and materialized views #1626
  • Implement partial index support #4768
  • Support for 'is null' in 'order by' #3667

Bug fixes

  • Fix support for Oracle connections passed via knex.connection() #4757
  • Avoid inserting multiple locks if a migration lock already exists #4694

Typings

  • Some TableBuilder methods return wrong types #4764
  • Update JoinRaw bindings type to accept arrays #4752
  • fix onDelete/onUpdate for ColumnBuilder #4656

0.95.11 - 03 September, 2021

New features

  • Add support for nullability modification via schema builder (table.setNullable() and table.dropNullable()) #4657
  • MySQL: Add support for mysql/mariadb-client JSON parameters in connectionURIs #4629
  • MSSQL: Support comments as MS_Description properties #4632

Bug fixes

  • Fix Analytic orderBy and partitionBy to follow the SQL documentation #4602
  • CLI: fix migrate:up for migrations disabling transactions #4550
  • SQLite: Fix adding a column with a foreign key constraint in SQLite #4649
  • MSSQL: columnInfo() support case-sensitive database collations #4633
  • MSSQL: Generate valid SQL for withRecursive() #4514
  • Oracle: withRecursive: omit invalid RECURSIVE keyword, include column list #4514

Improvements

  • Add .mjs migration and seed stubs #4631
  • SQLite: Clean up DDL handling and move all operations to the parser-based approach #4648

0.95.10 - 20 August, 2021

Improvements

  • Use sys info function instead of connection db name #4623

Typings

  • Deferrable and withkeyName should not be in ColumnBuilder #4600

0.95.9 - 31 July, 2021

New features

  • Oracle: support specifying schema for dropTable and dropSequence #4596
  • Oracle: support specifying schema for autoincrement #4594

Typings

  • Add TypeScript support for deferrable, new Primary/Unique syntax #4589

0.95.8 - 25 July, 2021

New features

  • Add deferrable support for constraint #4584
  • Implement delete with join #4568
  • Add DPI error codes for Oracle #4536

Bug fixes

  • Fixing PostgreSQL datetime and timestamp column created with wrong format #4578

Typings

  • Improve analytic types #4576
  • MSSQL: Add trustServerCertificate option #4500

0.95.7 - 10 July, 2021

New features

  • Add ability to omit columns on an onConflict().ignore() #4557
  • CLI: Log error message #4534

Typings

  • Export Knex.TransactionConfig #4498
  • Include options object in count(Distinct) typings #4491
  • Add types for analytic functions #4544

0.95.6 - 17 May, 2021

Typings

  • Export TransactionProvider type #4489

0.95.5 - 11 May, 2021

New features

  • SQLite: Add support for file open flags #4446
  • Add .cjs extension to Seeder.js to support Node ESM #4381 #4382

Bug fixes

  • Remove peerDependencies to avoid auto-install on npm 7 #4480

Typings

  • Fix typing for increments and bigIncrements #4406
  • Add typings for on JoinClause for onVal #4436
  • Adding Type Definition for isTransaction #4418
  • Export client class from knex namespace #4479

0.95.4 - 26 March, 2021

Typings

  • Fix mistyping of stream #4400

0.95.3 - 25 March, 2021

New features

  • PostgreSQL: Add "same" as operator #4372
  • MSSQL: Improve an estimate of the max comment length #4362
  • Throw an error if negative offset is provided #4361

Bug fixes

  • Fix timeout method #4324
  • SQLite: prevent dropForeign from being silently ignored #4376

Typings

  • Allow config.client to be non-client instance #4367
  • Add dropForeign arg type for single column #4363
  • Update typings for TypePreservingAggregation and stream #4377

0.95.2 - 11 March, 2021

New features

  • Improve ESM import support #4350

Bug fixes

  • CLI: update ts.stub files to new TypeScript namespace #4344
  • CLI: fix TypeScript migration stub after 0.95.0 changes #4366

Typings

  • Move QueryBuilder and KnexTimeoutError into knex namespace #4358

Test / internal changes

  • Unify db test helpers #4356

0.95.1 - 04 March, 2021

Bug fixes

  • CLI: fix knex init not finding default knexfile #4339

0.95.0 - 03 March, 2021

Note: there are many breaking changes in this version, particularly in TypeScript support. Please see UPGRADING.md for details.

New features

  • Add transaction isolation support #4185
  • Add analytic functions #4188
  • Change default to not trigger a promise rejection for transactions with a specified handler #4195
  • Make toSQL().toNative() work for Raw to match the API for QueryBuilder #4058
  • Allow 'match' operator #3569
  • Support optimizer hints #4243
  • Add parameter to prevent autoincrement columns from being primary keys #4266
  • Make "first" and "pluck" mutually exclusive #4280
  • Added merge strategy to allow selecting columns to upsert. #4252
  • Throw error if the array passed to insert is empty #4289
  • Events: introduce queryContext on query-error #4301
  • CLI: Use UTC timestamp for new migrations #4245
  • MSSQL: Replace MSSQL dialect with Tedious.js implementation #2857 #4281
  • MSSQL: Use "nvarchar(max)" for ".json()" #4278
  • MSSQL: Schema builder - add predictable constraint names for default values #4319
  • MSSQL: Schema builder - attempt to drop default constraints when changing default value on columns #4321
  • SQLite: Fallback to json for sqlite3 when using jsonb #4186
  • SQLite: Return complete list of DDL commands for creating foreign keys #4194
  • SQLite: Support dropping composite foreign keys #4202
  • SQLite: Recreate indices when altering a table #4277
  • SQLite: Add support for altering columns #4322

Bug fixes

  • Fix issue with .withSchema usage with joins on a subquery #4267
  • Fix issue with schema usage with FROM clause contain QueryBuilder, function or Raw #4268
  • CLI: Address raised security warnings by dropping liftoff #4122
  • CLI: Fix an issue with npm@7 and ESM when type was set to 'module' in package.json #4295
  • PostgreSQL: Add check to only create native enum once #3658
  • SQLite: Fix foreign key "on delete" when altering a table #4225
  • SQLite: Made the constraint detection case-insensitive #4330
  • MySQL: Keep auto increment after rename #4266
  • MSSQL: don't raise query-error twice #4314
  • MSSQL: Alter column must have its own query #4317

Typings

  • TypeScript 4.1+ is now required
  • Add missing onConflict overrides #4182
  • Introduce the "infamous triplet" export #4181
  • Fix type definition of Transaction #4172
  • Add typedefinitions for havingNotIn #4265
  • Include 'name' property in MigratorConfig #4300
  • Improve join and conflict types #4318
  • Fix ArrayIfAlready type #4331

Test / internal changes

  • Drop global Knex.raw #4180
  • Stop using legacy url.parse API #3702
  • Various internal refactorings #4175 #4177 #4178 #4192
  • Refactor to classes #4190 #4191 #4193 #4210 #4253
  • Move transaction type tests to TSD #4208
  • Clean up destroy logic #4248
  • Colorize code snippets in readme files #4234
  • Add "Ecosystem" documentation for Knex plugins #4183
  • Documentation cleanup
  • SQLite: Use SQLite "rename column" instead of a DDL helper #4200
  • SQLite: Simplify reinsert logic when altering a table #4272

0.21.19 - 02 March, 2021

  • SQLite: Made the constraint detection case-insensitive #4332

0.21.18 - 22 February, 2021

  • CLI: Fix an issue with npm@7 and ESM when type was set to 'module' in package.json #4295

0.21.17 - 30 January, 2021

Bug fixes

  • SQLite: Fix SQLite foreign on delete when altering a table #4261

New features

0.21.16 - 17 January, 2021

Bug fixes

  • MSSQL: Avoid passing unsupported pool param. Fixes node-mssql 7+ support #4236

0.21.15 - 26 December, 2020

New features

  • SQLite: Add primary/foreign support on alterTable #4162
  • SQLite: Add dropPrimary/dropForeign support on alterTable #4162

Typings

  • Add "after" and "first" to columnBuilder types #3549 #4169

Test / internal changes

  • Extract knex config resolution logic #4166
  • Run CI using GitHub Actions #4168
  • Add Node.js 15 to CI matrix #4173

0.21.14 - 18 December, 2020

New features

  • MSSQL: support "returning" on inserts, updates and deletes on tables with triggers #4152
  • Use esm import if package.json type is "module" #4158

Bug fixes

  • Make sure query-response and query-error events contain _knexTxId #4160

Test / internal changes

  • Improved integration test framework #4161

0.21.13 - 12 December, 2020

New features

  • SQLite: Add support for dropForeign #4092
  • Add support for WHERE clauses to "upsert" queries #4148

Bug fixes

  • MSSQL: Avoid connection getting stuck on socket hangup #4157
  • Oracle: Support specifying non-default DB port #4147
  • Oracle: Support inserts with only default values (empty body) #4092
  • CLI: fix irregular seed file execution order #4156
  • Fix performance of asyncStackTraces with enable-source-maps node flag #4154

Typings

  • PostgreSQL: Add support for application_name #4153
  • Fix types for insert to allow array #4105
  • Add types for userParams and withUserParams #4119
  • Added type for withKeyName #4139
  • Fix batchInsert definitions #4131
  • Fix types for WhereIn signature (value or query builder) #3863
  • Add types for connection config of mysql2 driver #4144

Test / internal changes

0.21.12 - 02 November, 2020

Typings

  • Reintroduce support for globally defining table/record mapping #4100
  • Add a few missing types for MSSQL Connection #4103
  • Make .ignore() and .merge() return QueryBuilder rather than QueryInterface #4102
  • Use tarn config TS types instead of generic-pool #4064

0.21.11 - 01 November, 2020

Typings

  • Revert support for globally defining table/record mapping #4099

0.21.10 - 31 October, 2020

New features

  • Upsert support (Postgres/MySQL/Sqlite) #3763

Bug fixes

  • Switch to non-uuid knexQueryUids to avoid issues when mocking global date #4089

Typings

  • Allow to globally define table/record mapping #4071

0.21.9 - 27 October, 2020

New features

  • add method clear(statement) to QueryBuilder #4051

Bug fixes

  • CLI: fix help text being printed twice #4072
  • Oracle: columnInfo() no longer requires an Owner User #4053
  • Add missing "start" event propagation from transaction #4087

0.21.8 - 27 October, 2020

Bug fixes

  • MSSQL: Escape properly if literal '?' is needed #4053
  • Make toQuery behavior consistent with pre-0.21.7 (do not break on empty builder) #4083
  • Fix comment escaping for MySQL and PostgreSQL #4084

0.21.7 - 25 October, 2020

New features

  • CLI: Add migration stub for .cjs extension #4065

Bug fixes

  • MSSQL: Add dynamic scaling for decimal values and prevents a UInt64 overflow #3910
  • MSSQL: Fix apostrophe escaping #4077
  • Ensure that semicolon is not appended to statements that already end with a semicolon #4052

Typings

  • Add arguments to QueryCallback in Where #4034

Test / internal changes

  • Replace lodash type-checks with native solutions #4056
  • Replace mkdirp with native recursive flag #4060
  • Replace inherits package with builtin utility #4059

0.21.6 - 27 September, 2020

New features

  • CLI: New config parameter / CLI flag to prefixing seed filename with timestamp #3873
  • CLI: throw an error when specific seed file cannot be found #4011
  • Warn if whereNot is used with 'in' or 'between' #4038

Bug fixes

  • CLI: Fix double merging of config for migrator #4040

Typings

  • Unify SeedsConfig and SeederConfig #4003
  • Allow string[] type for directory in SeedsConfig #4033

0.21.5 - 17 August, 2020

New features

  • CLI: Improve Esm interop #3985
  • CLI: Improve mjs module support #3980

Test / internal changes

  • Bump version of dtslint #3984
  • Test/document esm interop mixed formats (knexfile/migrations/seeds) #3986

0.21.4 - 10 August, 2020

New features

  • CLI: Add new option for seed: recursive #3974

Bug fixes

  • CLI: Do not load seeds from subfolders recursively by default #3974

0.21.3 - 08 August, 2020

New features

  • CLI: Support multiple directories for seeds #3967

Bug fixes

  • Ensure DB stream is destroyed when the PassThrough is destroyed #2324
  • Support postProcessResponse for streams #3931
  • Fix ESM module interop for calling module/package of type 'module' #3938
  • CLI: Fix migration source name in rollback all #3956
  • Fix getMergedConfig calls to include client logger #3920
  • Escape single quoted values passed to defaultTo function #3899

Typings

  • Add .timeout(ms) to .raw()'s typescript typings #3885
  • Add typing for double table column builder #3950
  • Add a phantom tag to Ref type to mark received type parameters as used #3934
  • Add null as valid binding type #3946

Test / internal changes

  • Change query lab link to https #3933

0.21.2 - 10 July, 2020

New features

  • Warn user if custom migration source is being reset #3839
  • Prefer void as return type on migration generator ts stub #3865
  • MSSQL: Added the removal of a columns default constraint, before dropping the column #3855

Typings

  • Fix definition for raw querybuilders #3846

Test / internal changes

  • Refactor migration logic to use async/await #3838

0.21.1 - 28 April, 2020

New features

  • CLI: Add migrate:unlock command, truncate on forceFreeMigrationsLock #3822
  • CLI: Add support for cjs files by default #3829

Bug fixes

  • CLI: Fix inference of seed/migration extension from knexfile extension #3814
  • rewrite delay to not node-only version. Fixes compatibility with browsers #3820

Test / internal changes

  • Update dependencies. Explicitly support Node.js 14 #3825 #3830

0.21.0 - 18 April, 2020

Improvements

  • Reduce size of lodash in bundle #3804

Breaking changes

  • Dropped support for Node 8
  • Breaking upstream change in pg-query-stream: Changed stream.close to stream.destroy which is the official way to terminate a readable stream. This is a breaking change if you rely on the stream.close method on pg-query-stream...though should be just a find/replace type operation to upgrade as the semantics remain very similar (not exactly the same, since internals are rewritten, but more in line with how streams are "supposed" to behave).

Test / internal changes

  • Updated Tarn.js to a version 3.0.0
  • Updated mkdirp to a version 1.0.4
  • Updated examples to use ES2015 style #3810

0.20.15 - 16 April, 2020

Bug fixes

  • Support for .finally(..) on knex's Promise-alikes #3800

Typings

  • Add types for .distinctOn #3784

0.20.14 - 13 April, 2020

New features

  • CLI: adds support for asynchronous knexfile loading #3748
  • Add clearGroup method #3771

Typings

  • Support Raw types for insert, where, update #3730
  • Add typings for MigrationSource #3756
  • Update signature of orderBy to support QueryBuilder inside array #3757
  • Add toSQL and toString to SchemaBuilder #3758
  • interface Knex and function Knex should have the same types #3787
  • Fix minor issues around typings #3765

Test / internal changes

  • Minor test internal enhancements #3747
  • Minor improvements on the usage of fs utilities #3749
  • Split tests in groups #3785

0.20.13 - 23 March, 2020

Bug fixes

  • Correctly handle dateToString escaping without timezone passed #3742
  • Make protocol length check more defensive #3744

Typings

  • Make the ChainableInterface conform to Promise<T> #3724

0.20.12 - 19 March, 2020

Bug fixes

  • Added missing call to _reject in Transactor#transaction #3706
  • Fix method binding on knex proxy #3717
  • Oracle: Transaction_OracleDB can use config.connection #3731

Typings

  • Fix incorrect type signature of Having #3719

Test / internal changes

  • Cleanup/remove transaction stalling #3716
  • Rewrote Transaction#acquireConnection() methods to use async #3707

0.20.11 - 26 February, 2020

Breaking changes

  • Knex returns native JS promises instead of Bluebird ones. This means that you no longer use such methods as map, spread and reduce on QueryBuilder instance.

New features

  • Oracle: Add OracleDB handling for buffer type in fetchAsString #3685

Bug fixes

  • Fix race condition in non-container transactions #3671

Typings

  • Mark knex arguments of composite/collection types to be readonly #3680

Test / internal changes

  • Remove dependency on Bluebird methods from sources #3683
  • Cleanup and extract Transaction Workflow logic #3674

0.20.10 - 13 February, 2020

Bug fixes

  • Oracle: commit was a no-op causing race conditions #3668
  • CLI: Knex calls process.chdir() before opening Knexfile #3661
  • Fixed unresolved promise in cancelQuery() #3666

Typings

  • fn.now takes optionally a precision argument. #3662
  • PG: Include SSL in connection definition #3659

Test / internal changes

  • replace Bluebird.timeout #3634

0.20.9 - 08 February, 2020

Bug fixes

  • CLI: Improve Support for Liftoff's Preloaders - this should fix some cases like using TS for your migrations #3613

Typings

  • MSSQL: Add enableArithAbort to MsSqlConnectionConfig

Test / internal changes

  • Refactor more tests to use cli-testlab #3640
  • Update QueryCompiler implementation to use classes #3647

0.20.8 - 14 January, 2020

New features

  • CLI: Support ES6 modules via flag --esm #3616

Bug fixes

  • CLI: Print help only when there are no arguments #3617

Typings

  • Fix incorrect type of QueryBuilder.first('*') result #3621

0.20.7 - 07 January, 2020

New features

  • Throw better error when trying to modify schema while using unsupported dialect #3609

Bug fixes

  • Oracle: dispose connection on connection error #3611
  • Oracle: fix not releasing connection from pool on disconnect #3605
  • CLI: prevent warning with root command #3604

Typings

  • Add create/drop schema methods to SchemaBuilder #3579

0.20.6 - 29 December, 2019

Bug fixes

  • Enforce Unix (lf) line terminators #3598

0.20.5 - 29 December, 2019

New features

  • Return more information about empty updates #3597

Bug fixes

  • Fix colors in debug logs #3592

Test / internal changes

  • Use more efficient algorithm for generating internal ids #3595 #3596
  • Use Buffer.alloc() instead of deprecated constructor #3574

0.20.4 - 08 December, 2019

Bug fixes

  • Fix debug logger messing up queries with % #3566
  • Make logger methods mutually consistent #3567

Typings

  • Add missing methods to client type #3565
  • Fix queryContext function defintion #3562
  • Fix QueryBuilder.extend this type #3526 #3528

Test / internal changes

  • Remove bluebird.using #3552

0.20.3 - 27 November, 2019

New features

  • MSSQL, MySQL: Add connection string qs to connection params #3547

Bug fixes

  • Oracle: Fix issue retrieving BLOB from database #3545
  • PostgreSQL: Timeout for postgresql use cancel instead of terminate #3518
  • Make sure CLI works for namespaced knex packages #2539

Typings

  • Lift up dialect specific methods in the CreateTableBuilder #3532
  • Add client property to QueryBuilder type #3541
  • Support 'only' option #3551

0.20.2 - 14 November, 2019

New features

  • Add support for distinct on for postgres #3513

Bug fixes

  • Make sqlite3 hasColumn case insensitive #3435

Typings

  • Fix PoolConfig typing #3505
  • Expand SeedsConfig types #3531
  • Make the default type parameters of QueryBuilder less strict #3520
  • Fix regression in older version of node when Promise#finally was not available #3507

0.20.1 - 29 October, 2019

New features

  • Declare drivers as optional peerDependencies #3081
  • Dynamic connection configuration resolution #3497

Bug fixes

  • Wrap subQuery with parenthesis when it appears as table name #3496
  • Fix Oracle error codes #3498

Typings

  • Add interface for PG Connection object #3372
  • Gracefully handle global promise pollution #3502

0.20.0 - 25 October, 2019

New features

  • orderBy accepts QueryBuilder #3491
  • Add validation in .offset() #2908
  • disable_migrations_list_validation feature #3448

Bug fixes

  • Fix oracledb driver v4 support #3480
  • Fix some issues around seed and migration generation #3479
  • Fix bugs in replacement logic used when dropping columns in SQLite #3476

Typings

  • Add types to the Migrator interface #3459
  • Fix typings of index and dropIndex TableBuilder methods #3486
  • Fixes types for Seeder#run #3438

Test / internal changes

  • Execute CI on Node.js 13
  • Bluebird: remove usage of return, reflect, fromCallback methods #3483
  • Bluebird: remove Bluebird.bind #3477
  • Bluebird: use util.promisify instead of Bluebird.promisify #3470
  • Bluebird: remove Bluebird.each #3471
  • Bluebird: remove Bluebird.map and Bluebird.mapSeries #3474
  • Bluebird: replace Bluebird.map with Promise.all #3469
  • Update badges #3482

0.19.5 - 06 October, 2019

New features

  • CLI: Migrations up/down commands - filename parameter #3416
  • Oracle: Support stored procedures #3449

Bug fixes

  • MSSQL: Escape column ids correctly in all cases (reported by Snyk Security Research Team) #3382
  • SQLite: Fix handling of multiline SQL in SQLite3 schema #3411
  • Fix concurrent child transactions failing #2213 #3440

Typings

  • Add missing Migrator.list typing #3460
  • Fix Typescript type inference for to better support wildcard (*) calls #3444
  • Make options argument optional in timeout #3442

Test / internal changes

  • Enable linting in CI #3450

0.19.4 - 09 September, 2019

New features

  • Add undefined columns to undefined binding(s) error #3425

Typings

  • Add specific to SeederConfig type #3429
  • Fix some issues with QueryBuilder types #3427

0.19.3 - 25 August, 2019

Bug fixes

  • Fix migrations for native enums to use table schema #3307

New features

  • Add ability to manually define schema for native enums #3307
  • Add SSL/TLS support for Postgres connection string #3410
  • CLI: new command that lists all migrations with status #3390

Typings

  • Include schemaName in EnumOptions #3415
  • Allow ColumnBuilder.defaultTo() to be null #3407

Other Changes

  • migrate: Refactor _lockMigrations to avoid forUpdate - makes migrations compatible with CockroachDB #3395

0.19.2 - 17 August, 2019

Other Changes

  • Make transaction rejection consistent across dialects #3399
  • More consistent handling of nested transactions #3393

New features

  • Fallback to JSON when using JSONB in MySQL #3394

0.19.1 - 23 July, 2019

New features

  • Allow to extend knex query builder #3334
  • Add .isCompleted() to transaction #3368
  • Minor enhancements around aliasing of aggregates #3354

Typings

  • Update configuration typings to allow for oracle db connectionstring #3361
  • Update Knex.raw type to be any by default because the actual type is dialect specific #3349

0.19.0 - 11 July, 2019

Other Changes

  • Pooling: tarn.js connection pool was updated to version 2.0.0. This fixes issue with destroying connections and introduces support for connection pool event handlers. Please see tarn.js documentation for more details #3345
  • Pooling: Passing unsupported pooling configuration options now throws an error
  • Pooling: beforeDestroy configuration option was removed

0.18.4 - 10 July, 2019

New features

  • Seeds: Option to run specific seed file #3335
  • Implement "skipLocked()" and "noWait()" #2961

Bug fixes

  • CLI: Respect the knexfile stub option while generating a migration #3337
  • Fix mssql import not being ignored, breaking webpack builds #3336

0.18.3 - 04 July, 2019

New features

  • CLI: add --stub option to migration:make #3316

Bug fixes

  • Fix return duplicate transaction promise for standalone transactions #3328

0.18.2 - 03 July, 2019

Bug fixes

  • Fix remove duplicate transaction rejection #3324
  • Fix issues around specifying default values for columns #3318
  • CLI: Fix empty --version output #3312

0.18.1 - 30 June, 2019

Bug fixes

  • Do not reject duplicate promise on transaction rollback #3319

0.18.0 - 26 June, 2019

Bug fixes

  • Do not reject promise on transaction rollback (by default only for new, non-callback, style of transactions for now to avoid breaking old code) #3235

New features

  • Added doNotRejectOnRollback options for starting transactions, to prevent rejecting promises on rollback for callback-style transactions.
  • Use extension from knexfile for generating migrations unless overriden #3282
  • Use migrations.extension from config when generating migration #3242
  • Expose executionPromise for transactors #3297

Bug fixes

  • Oracle: Updated handling of connection errors for disposal #2608
  • Fix extension resolution from env configs #3294

Test / internal changes

Typings

  • Add workarounds for degraded inference when strictNullChecks is set to false #3275
  • Add stub type definition for Migrator config #3279
  • Add stub to seeds type #3296
  • Fix MSSQL config typings #3269
  • Add pgsql specific table builder method typings #3146

0.17.5 - 8 June, 2019

Typings

  • Include result.d.ts in published package #3271

0.17.4 - 8 June, 2019

Typings

  • Fix some cases of left-to-right inference causing type mismatch #3265
  • Improve count typings #3249

Bug fixes

  • Fix error message bubbling up on seed error #3248

0.17.3 - 2 June, 2019

Typings

  • Improve typings for aggregations #3245
  • Add decimalNumbers to MySqlConnectionConfig interface #3244

0.17.2 - 1 June, 2019

Typings

  • Improve count typings #3239

Bug fixes

  • "colorette" dependency breaks browserify builds #3238

0.17.1 - 31 May, 2019

New features

  • Add migrate:down functionality #3228

Typings

  • Update type of aggregation results to not be arrays when first has been invoked before #3237
  • Include undefined in type of single row results #3231
  • Fix incorrect type definitions for single row queries #3230

0.17.0 - 28 May, 2019

New features

  • Add support for returning started transaction without immediately executing it #3099
  • Add support for passing transaction around with only starting it when needed #3099
  • Add clearHaving function #3141
  • Add --all flag for rollback in CLI #3187
  • Add error detail log to knex CLI #3149
  • Support multi-column whereIn in sqlite through values clause #3220
  • Allow users to specify the migrations "tableName" parameter via the CLI #3214
  • Unify object options handling for datetime/timestamp across dialects #3181
  • Add "up" command for migrations #3205

Typings

  • Add default values for generic types (fixes backwards compatibility broken by 0.16.6) #3189
  • Make function types generic in type definitions #3168
  • Add missing types to MigratorConfig #3174
  • Add types for havingBetween, orHavingBetween, havingNotBetween and orHavingNotBetween #3144
  • Update Knex.Config types to include log #3221
  • Fix some more cases of missing typings #3223
  • Support type safe refs #3215
  • Expose some utility types #3211
  • Fix issues with typings of joins and some conflicts with Bluebird typings #3209

Bug fixes

  • Fix order of migration rollback #3172

Test / internal changes

  • Execute CI tests on Node.js 12 #3171
  • Docker-based test dbs #3157
  • Use cli-testlab for testing CLI #3191

0.16.5 - 11 Apr, 2019

  • Bundle polyfills with knex for 0.16.x line again #3139

0.16.4 - 11 Apr, 2019

New features

  • Boolean param for rollback() to rollback all migrations #2968
  • seed:run print the file name of the failing seed #2972 #2973
  • verbose option to CLI commands #2887
  • add intersect() #3023
  • Improved format for TS stubs #3080
  • MySQL: Support nullable timestamps #3100
  • MySQL: Warn .returning() does not have any effect #3039

Bug fixes

  • Respect "loadExtensions" configuration #2969
  • Fix event listener duplication when using Migrator #2982
  • Fix fs-migrations breaking docs #3022
  • Fix sqlite3 drop/renameColumn() breaks with postProcessResponse #3040
  • Fix transaction support for migrations #3084
  • Fix queryContext not being passed to raw queries #3111
  • Typings: Allow to pass query builders, identifiers and raw in various places as parameters #2960
  • Typings: toNative() definition #2996
  • Typings: asCallback() definition #2963
  • Typings: queryContext() type definition Knex.Raw #3002
  • Typings: Add "constraintName" arg to primary() definition #3006
  • Typings: Add missing schemaName in MigratorConfig #3016
  • Typings: Add missing supported parameter types and toSQL method #2960
  • Typings: Update enum arguments to reflect latest signature #3043
  • Typings: Add size parameter to integer method #3074
  • Typings: Add 'string' as accepted Knex constructor type definition #3105
  • Typings: Add boolean as a column name in join #3121
  • Typings: Add missing clearOrder & clearCounters types #3109
  • Dependencies: Fix security warning #3082
  • Do not use unsupported column width/length arguments on data types int and tinyint in MSSQL #2738

Other Changes

  • Make unionAll()'s call signature match union() #3055

Test / internal changes

  • Swap chalk→colorette / minimist→getopts #2718
  • Always use well documented pg client query() config argument #3004
  • Do not bundle polyfills with knex #3024

0.16.3 - 19 Dec, 2018

Bug fixes

  • @babel/polyfill loaded multiple times #2955
  • Resolve migrations and seeds relatively to knexfile directory when specified (the way it used to be before 0.16.1) #2952

0.16.2 - 10 Dec, 2018

Bug fixes

  • Add TypeScript types to the "files" entry so they are properly included in the release #2943

0.16.1 - 28 Nov, 2018

Breaking changes

  • Use datetime2 for MSSQL datetime + timestamp types. This change is incompatible with MSSQL older than 2008 #2757
  • Knex.VERSION() method was removed, run "require('knex/package').version" instead #2776
  • Knex transpilation now targets Node.js 6, meaning it will no longer run on older Node.js versions #2813
  • Add json type support for SQLite 3.9+ (tested to work with Node package 'sqlite3' 4.0.2+) #2814

New features

  • Support passing explicit connection to query builder (#2817)
  • Introduced abstraction for getting migrations to make migration bundling easier #2775
  • Allow timestamp with timezone on mssql databases #2724
  • Allow specifying multiple migration directories #2735
  • Allow cloning query builder with .userParams({}) assigned to it #2802
  • Allow chaining of increment, decrement, and update #2740
  • Allow table names with forUpdate/forShare #2834
  • Added whereColumn and the associated not / and / or methods for using columns on the right side of a where clause #2837
  • Added whereRecursive method to make self-referential CTEs possible #2889
  • Added support for named unique, primary and foreign keys to SQLite3 #2840
  • Added support for generating new migration and seed files without knexfile #2884 #2905 #2935
  • Added support for multiple columns in .orderBy() #2881
  • Added option of existingType to .enum() method to support repeated use of enums #2719
  • Added option to pass indexType for MySQL dialect #2890
  • Added onVal and the associated not / and / or methods for using values in on clauses within joins #2746
  • Kill queries after timeout for PostgreSQL #2636
  • Manage TypeScript types internally #2845
  • Support 5.0.0+ versions of mssql driver #2861
  • Typescript migration stub #2816
  • Options object for passing timestamp parameters + regression tests #2919

Bug fixes

  • Implement fail-fast logic for dialect resolution #2776
  • Fixed identifier wrapping for using(). Use columnize instead of wrap in using() #2713
  • Fix issues with warnPromise when migration does not return a promise #2730
  • Compile with before update so that bindings are put in correct order #2733
  • Fix join using builder withSchema #2744
  • Throw instead of process.exit when client module missing #2843
  • Display correct filename of a migration that failed #2910
  • Fixed support of knexSnakeCaseWrappers in migrations #2914
  • SQlite3 renameColunm quote fix #2833
  • Adjust typing for forUpdate()/forShare() variant with table names #2858
  • Fix execution of Oracle tests on Node 11 #2920
  • Fix failures in oracle test bench and added it back to mandatory CI tests #2924
  • Knex client knexfile resolution fix #2923
  • Add queryContext to type declarations #2931

Test / internal changes

0.16.0 - 27 Nov, 2018

Other Changes

  • THIS RELEASE WAS UNPUBLISHED FROM NPM BECAUSE IT HAD BROKEN MIGRATIONS USING postprocessResponse FEATURE (#2644)

0.15.2 - 19 Jul, 2018

Other Changes

  • Rolled back changes introduced by #2542, in favor of opt-in behavior by adding a precision option in date / timestamp / datetime / knex.fn.now (#2715, #2721)

0.15.1 - 12 Jul, 2018

Bug fixes

  • Fix warning erroneously displayed for mysql #2705

0.15.0 - 1 Jul, 2018

Breaking changes

  • Stop executing tests on Node 4 and 5. #2451 (not supported anymore)
  • json data type is no longer converted to text within a schema builder migration for MySQL databases (note that JSON data type is only supported for MySQL 5.7.8+) #2635
  • Removed WebSQL dialect #2461
  • Drop mariadb support #2681
  • Primary Key for Migration Lock Table #2569. This shouldn't affect to old loc tables, but if you like to have your locktable to have primary key, delete the old table and it will be recreated when migrations are ran next time.
  • Ensure knex.destroy() returns a bluebird promise #2589
  • Increment floats #2614
  • Testing removal of 'skim' #2520, Now rows are not converted to plain js objects, returned row objects might have changed type with oracle, mssql, mysql and sqlite3
  • Drop support for strong-oracle #2487
  • Timeout errors doesn't silently ignore the passed errors anymore #2626
  • Removed WebSQL dialect #2647
  • Various fixes to mssql dialect to make it compatible with other dialects #2653, Unique constraint now allow multiple null values, float type is now float instaed of decimal, rolling back transaction with undefined rejects with Error, select for update and select for share actually locks selected row, so basically old schema migrations will work a lot different and produce different schema like before. Also now MSSQL is included in CI tests.

Bug fixes

  • Fixes onIn with empty values array #2513
  • fix wrapIdentifier not being called in postgres alter column #2612
  • fixes wrapIdentifier to work with postgres returning statement 2630 #2642
  • Fix mssql driver crashing in certain cases when conneciton is closed unexpectedly #2637
  • Removed semicolon from rollback stmt for oracle #2564
  • Make the stream catch errors in the query #2638

New features

  • Create timestamp columns with microsecond precision on MySQL 5.6 and newer #2542
  • Allow storing stacktrace, where builder is initialized to be able trace back where certain query was created #2500 #2505
  • Added 'ref' function #2509, no need for knex.raw('??', ['id']) anymore, one can do knex.ref('id')
  • Support postgresql connection uri protocol #2609
  • Add support for native enums on Postgres #2632
  • Allow overwriting log functions #2625

Test / internal changes

  • chore: cache node_modules #2595
  • Remove babel-plugin-lodash #2634
  • Remove readable-stream and safe-buffer #2640
  • chore: add Node.js 10 #2594
  • add homepage field to package.json #2650

0.14.6 - 12 Apr, 2018

Bug fixes

  • Restored functionality of query event #2566 (#2549)

0.14.5 - 8 Apr, 2018

Bug fixes

  • Fix wrapping returning column on oracledb #2554

New features

  • Support passing DB schema name for migrations #2499 #2559
  • add clearOrder method #2360 #2553
  • Added knexTxId to query events and debug calls #2476
  • Support multi-column whereIn with query #1390
  • Added error if chaining update/insert/etc with first() #2506
  • Checks for an empty, undefined or null object on transacting #2494
  • countDistinct with multiple columns #2449

Test / internal changes

  • Added npm run test:oracledb command that runs oracledb tests in docker #2491
  • Runnin mssql tests in docker #2496
  • Update dependencies #2561

0.14.4 - 19 Feb, 2018

Bug fixes

  • containsUndefined only validate plain objects. Fixes #1898 (#2468)
  • Add warning when using .returning() in sqlite3. Fixes #1660 (#2471)
  • Throw an error if .update() results in an empty sql (#2472)
  • Removed unnecessary createTableIfNotExist and replaced with createTable (#2473)

New features

  • Allow calling lock procedures (such as forUpdate) outside of transaction. Fixes #2403. (#2475)
  • Added test and documentation for Event 'start' (#2488)

Test / internal changes

  • Added stress test, which uses TCP proxy to simulate flaky connection #2460
  • Removed old docker tests, new stress test setup (#2474)
  • Removed unused property __cid on the base client (#2481)
  • Changed rm to rimraf in 'npm run dev' (#2483)
  • Changed babel preset and use latest node as target when running dev (#2484)

0.14.3 - 8 Feb, 2018

Bug fixes

  • Use tarn as pool instead of generic-pool which has been given various problems #2450
  • Fixed mysql issue where add columns failed if using both after and collate #2432
  • CLI sets exit-code 1 if the command supplied was not parseable #2358
  • Set toNative() to be not enumerable #2388
  • Use wrapIdentifier in columnInfo. fixes #2402 #2405
  • Fixed a bug when using .returning (OUTPUT) in an update query with joins in MSSQL #2399
  • Better error message when running migrations fail before even starting run migrations #2373
  • Read oracle's UV_THREADPOOL_SIZE env variable correctly #2372
  • Added decimal variable precision / scale support #2353

New features

  • Added queryContext to schema and query builders #2314
  • Added redshift dialect #2233
  • Added warning when one uses .createTableIfNotExist and deprecated it from docs #2458

Test / internal changes

  • Update dependencies and fix ESLint warnings accordingly #2433
  • Disable oracledb tests from non LTS nodes #2407
  • Update dependencies #2422

0.14.2 - 24 Nov, 2017

Bug fixes

  • Fix sqlite3 truncate method to work again #2348

0.14.1 - 19 Nov, 2017

Bug fixes

  • Fix support for multiple schema names in in postgres searchPath #2340
  • Fix create new connection to pass errors to query instead of retry loop #2336
  • Fix recognition of connections closed by server #2341

0.14.0 - 6 Nov, 2017

Breaking changes

  • Remove sorting of statements from update queries #2171
  • Updated allowed operator list with some missing operators and make all to lower case #2239
  • Use node-mssql 4.0.0 #2029
  • Support for enum columns to SQlite3 dialect #2055
  • Better identifier quoting in Sqlite3 #2087
  • Migration Errors - Display filename of of failed migration #2272

Other Features

  • Post processing hook for query result #2261
  • Build native SQL where binding parameters are dialect specific #2237
  • Configuration option to allow override identifier wrapping #2217
  • Implemented select syntax: select({ alias: 'column' }) #2227
  • Allows to filter seeds and migrations by extensions #2168
  • Reconnecting after database server disconnect/reconnect + tests #2017
  • Removed filering from allowed configuration settings of mysql2 #2040
  • Allow raw expressions in query builder aggregate methods #2257
  • Throw error on non-string table comment #2126
  • Support for mysql stream query options #2301

Bug fixes

  • Allow update queries and passing query builder to with statements #2298
  • Fix escape table name in SQLite columnInfo call #2281
  • Preventing containsUndefined from going to recursion loop #1711
  • Fix error caused by call to knex.migrate.currentVersion #2123
  • Upgraded generic-pool to 3.1.7 (did resolve some memory issues) #2208
  • Allow using NOT ILIKE operator #2195
  • Fix postgres searchPath to be case-sensitive #2172
  • Fix drop of multiple columns in sqlite3 #2107
  • Fix adding multiple columns in Oracle #2115
  • Use selected schema when dropping indices in Postgres. #2105
  • Fix hasTable for MySQL to not do partial matches #2097
  • Fix setting autoTransaction in batchInsert #2113
  • Fix connection error propagation when streaming #2199
  • Fix comments not being applied to increments columns #2243
  • Fix mssql wrong binding order of queries that combine a limit with select raw or update #2066
  • Fixed mysql alter table attributes order #2062

Test / internal changes

  • Update each out-of-date dependency according to david-dm.org #2297
  • Update v8flags to version 3.0.0 #2288
  • Update interpret version #2283
  • Fix debug output typo #2187
  • Docker CI tests #2164
  • Unit test for right/rightOuterJoin combination #2117
  • Unit test for fullOuterJoin #2118
  • Unit tests for table comment #2098
  • Test referencing non-existent column with sqlite3 #2104
  • Unit test for renaming column in postgresql #2099
  • Unit test for cross-join #2102
  • Fix incorrect parameter name #2068

0.13.0 - 29 Apr, 2017

Breaking changes

  • Multiple concurrent migration runners blocks instead of throwing error when possible #1962
  • Fixed transaction promise mutation issue #1991

Other Changes

  • Allow passing version of connected db in configuration file #1993
  • Bugfixes on batchInsert and transactions for mysql/maria #1992
  • Add fetchAsString optional parameter to oracledb dialect #1998
  • fix: escapeObject parameter order for Postgres dialect. #2003

0.12.9 - 23 Mar, 2017

  • Fixed unhandled exception in batchInsert when the rows to be inserted resulted in duplicate key violation #1880

0.12.8 - 15 Mar, 2017

  • Added clearSelect and clearWhere to query builder #1912
  • Properly close Postgres query streams on error #1935
  • Transactions should never reject with undefined #1970
  • Clear acquireConnectionTimeout if an error occurs when acquiring a connection #1973

0.12.7 - 17 Feb, 2017

Accidental Breaking Change

  • Ensure that 'client' is provided in knex config object #1822

Other Changes

  • Support custom foreign key names #1311, #1726
  • Fixed named bindings to work with queries containing :-chars #1890
  • Exposed more promise functions #1896
  • Pass rollback errors to transaction promise in mssql #1885
  • ONLY keyword support for PostgreSQL (for table inheritance) #1874
  • Fixed Mssql update with join syntax #1777
  • Replace migrations and seed for react-native packager #1813
  • Support knexfile, migration and seeds in TypeScript #1769
  • Fix float to integer conversion of decimal fields in MSSQL #1781
  • External authentication capability when using oracledb driver #1716
  • Fixed MSSQL incorect query build when locks are used #1707
  • Allow to use first method as aliased select #1784
  • Alter column for nullability, type and default value #46, #1759
  • Add more having* methods / join clause on* methods #1674
  • Compatibility fixes and cleanups #1788, #1792, #1794, #1814, #1857, #1649

0.12.6 - 19 Oct, 2016

  • Address warnings mentioned in #1388 (#1740)
  • Remove postinstall script (#1746)

0.12.5 - 12 Oct, 2016

  • Fix broken 0.12.4 build (removed from npm)
  • Fix #1733, #920, incorrect postgres array bindings

0.12.3 - 9 Oct, 2016

  • Fix #1703, #1694 - connections should be returned to pool if acquireConnectionTimeout is triggered
  • Fix #1710 regression in postgres array escaping

0.12.2 - 27 Sep, 2016

  • Restore pool min: 1 for sqlite3, #1701
  • Fix for connection error after it's closed / released, #1691
  • Fix oracle prefetchRowCount setting, #1675

0.12.1 - 16 Sep, 2016

  • Fix MSSQL sql execution error, #1669
  • Added DEBUG=knex:bindings for debugging query bindings, #1557

0.12.0 - 13 Sep, 2016

  • Remove build / built files, #1616
  • Upgrade to Babel 6, #1617
  • Reference Bluebird module directly, remove deprecated .exec method, #1618
  • Remove documentation files from main repo
  • Fix broken behavior on WebSQL build, #1638
  • Oracle id sequence now handles manual inserts, #906
  • Cleanup PG escaping, fix #1602, #1548
  • Added with to builder for common table expressions, #1599
  • Fix #1619, pluck with explicit column names
  • Switching back to generic-pool for pooling resource management
  • Removed index.html, please direct all PR's for docs against the files in knex/documentation

0.11.10 - 9 Aug, 2016

0.11.9 - 21 Jul, 2016

  • Reverted knex client breaking change (commit b74cd69e906), fixes #1587

0.11.8 - 21 Jul, 2016

  • Oracledb dialect #990
  • Documentation fix #1532
  • Allow named bindings to be escaped. #1576
  • Several bugs with MS SQL schema creation and installing from gihub fix #1577
  • Fix incorrect escaping of backslashes in SqlString.escape #1545

0.11.7 - 19 Jun, 2016

  • Add missing dependency. #1516

0.11.6 - 18 Jun, 2016

  • Allow cancellation on timeout (MySQL) #1454
  • Better bigint support. (MSSQL) #1445
  • More consistent handling of undefined values in QueryBuilder#where and Raw. #1459
  • Fix Webpack build. #1447
  • Fix code that triggered Bluebird warnings. #1460, #1489
  • Fix ping function. (Oracle) #1486
  • Fix columnInfo. (MSSQL) #1464
  • Fix ColumnCompiler#binary. (MSSQL) #1464
  • Allow connection strings that do not contain a password. #1473
  • Fix race condition in seed stubs. #1493
  • Give each query a UUID. #1510

0.11.5 - 26 May, 2016

  • Bugfix: Using Raw or QueryBuilder as a binding to Raw now works as intended

0.11.4 - 22 May, 2016

  • Bugfix: Inconsistency of .primary() and .dropPrimary() between dialects #1430
  • Feature: Allow using custom Client/Dialect (you can pass your own client in knex config) #1428
  • Docs: Add documentation for .dropTimestamps #1432
  • Bugfix: Fixed passing undefined fields for insert/update inside transaction #1423
  • Feature: batchInsert with existing transaction #1354
  • Build: eslint instead of jshint #1416
  • Bugfix: Pooled connections not releasing #1382
  • Bugfix: Support passing knex.raw to .whereNot #1402
  • Docs: Fixed list of dialects which supports .returning #1398
  • Bugfix: rename table does not fail anymore even with schema defined #1403

0.11.3 - 14 May, 2016

  • Support nested joins. #1397

0.11.2 - 14 May, 2016

  • Prevent crash on knex seed:make. #1389
  • Improvements to batchInsert. #1391
  • Improvements to inserting DEFAULT with undefined binding. #1396
  • Correct generated code for adding/dropping multiple columns. (MSSQL) #1401

0.11.1 - 6 May, 2016

  • Fix error in CLI command migrate:make. #1386

0.11.0 - 5 May, 2016

Breaking changes

  • QueryBuilder#orWhere joins multiple arguments with AND. #1164

Other Changes

  • Collate for columns. (MySQL) #1147
  • Add QueryBuilder#timeout, Raw#timeout. #1201 #1260
  • Exit with error code when appropriate. #1238
  • MSSQL connection accepts host as an alias for server in accordance with other dialects. #1239
  • Add query-response event. #1231
  • Correct behaviour of sibling nested transactions. #1226
  • Support RETURNING with UPDATE. (Oracle) #1253
  • Throwing callbacks from transactions automatically rolls them back. #1257
  • Fixes to named Raw bindings. #1251
  • timestamps accepts an argument to set NOT NULL and default to current timestamp.
  • Add TableBuilder#inherits for PostgreSQL. #601
  • Wrap index names. #1289
  • Restore coffeescript knexfiles and configurations. #1292
  • Add andWhereBetween and andWhereNotBetween #1132
  • Fix valueForUndefined failure. #1269
  • renameColumn no longer drops default value or nullability. #1326
  • Correct MySQL2 error handling. #1315
  • Fix MSSQL createTableIfNotExists. #1362
  • Fix MSSQL URL parsing. #1342
  • Update Lodash to 4.6.0 #1242
  • Update Bluebird to 3.3.4 #1279

0.10.0 - 15 Feb, 2016

Breaking changes

  • insert and update now ignore undefined values. Back compatibility is provided through the option useNullAsDefault. #1174, #1043

Other Changes

  • countDistinct, avgDistinct and sumDistinct. #1046
  • Add schema.jsonb. Deprecated schema.json(column, true). #991
  • Support binding identifiers with ??. #1103
  • Restore query event when triggered by transactions. #855
  • Correct question mark escaping in rendered queries. #519, #1058
  • Add per-dialect escaping, allowing quotes to be escaped correctly. #886, #1095
  • Add MSSQL support. #1090
  • Add migration locking. #1094
  • Allow column aliases to contain .. #1181
  • Add batchInsert. #1182
  • Support non-array arguments to knex.raw.
  • Global query-error event. #1163
  • Add batchInsert. #1182
  • Better support for Mysql2 dialect options. #980
  • Support for acquireConnectionTimeout default 60 seconds preventing #1040 from happening. #1177
  • Fixed constraint name escaping when dropping a constraint. #1177
  • Show also .raw queries in debug output. #1169
  • Support for cli to use basic configuration without specific environment set. #1101

0.9.0 - Nov 2, 2015

  • Fix error when merging knex.raw instances without arguments. #853
  • Fix error that caused the connection to time out while streaming. #849
  • Correctly parse SSL query parameter for PostgreSQL. #852
  • Pass compress option to MySQL2. #843
  • Schema: Use timestamp with timezone by default for time, datetime and timestamp for Oracle. #876
  • Add QueryBuilder#modify #881
  • Add LiveScript and Early Gray support for seeds and migrations.
  • Add QueryBuilder#withSchema #518
  • Allow escaping of ? in knex.raw queries. #946
  • Allow 0 in join clause. #953
  • Add migration config to allow disabling/enabling transactions per migration. #834

0.8.6 - May 20, 2015

0.8.5 - May 14, 2015

  • Pool should be initialized if no pool options are specified

0.8.4 - May 13, 2015

  • Pool should not be initialized if {max: 0} is sent in config options

0.8.3 - May 2, 2015

  • Alias postgresql -> postgres in connection config options

0.8.2 - May 1, 2015

  • Fix regression in using query string in connection config

0.8.1 - May 1, 2015

  • Warn rather than error when implicit commits wipe out savepoints in mysql / mariadb, #805.
  • Fix for incorrect seed config reference, #804

0.8.0 - Apr 30, 2015

New features

  • Fixes several major outstanding bugs with the connection pool, switching to Pool2 in place of generic-pool-redux
  • strong-oracle module support
  • Nested transactions automatically become savepoints, with commit & rollback releasing or rolling back the current savepoint.
  • Database seed file support, #391
  • Improved support for sub-raw queries within raw statements
  • Migrations are now wrapped in transactions where possible
  • Subqueries supported in insert statements, #627
  • Support for nested having, #572
  • Support object syntax for joins, similar to "where" #743

Major Changes

  • Transactions are immediately invoked as A+ promises, #470 (this is a feature and should not actually break anything in practice)
  • Heavy refactoring internal APIs (public APIs should not be affected)

Other Changes

  • Allow mysql2 to use non-default port, #588
  • Support creating & dropping extensions in PostgreSQL, #540
  • CLI support for knexfiles that do not provide environment keys, #527
  • Added sqlite3 dialect version of whereRaw/andWhereRaw (#477)

0.7.5 - Mar 9, 2015

  • Fix bug in validateMigrationList, (#697)

0.7.4 - Feb 25, 2015

  • Fix incorrect order of query parameters when using subqueries, #704
  • Properly handle limit 0, (#655)
  • Apply promise args from then instead of explicitly passing.
  • Respect union parameter as last argument (#660).
  • Added sqlite3 dialect version of whereRaw/andWhereRaw (#477).
  • Fix SQLite dropColumn doesn't work for last column (#544).
  • Add POSIX operator support for Postgres (#562)
  • Sample seed files now correctly (#391)

0.7.3 - Oct 3, 2014

  • Support for join(table, rawOrBuilder) syntax.
  • Fix for regression in PostgreSQL connection (#516)

0.7.2 - Oct 1, 2014

  • Fix for regression in migrations

0.7.1 - Oct 1, 2014

  • Better disconnect handling & pool removal for MySQL clients, #452

0.7.0 - Oct 1, 2014

New features

  • Oracle support, #419
  • Database seed file support, #391
  • Improved support for sub-raw queries within raw statements

Breaking changes

  • "collate nocase" no longer used by default in sqlite3 #396

Other Changes

  • Bumping Bluebird to ^2.x
  • Transactions in websql are now a no-op (unsupported) #375
  • Improved test suite
  • knex.fn namespace as function helper (knex.fn.now), #372
  • Better handling of disconnect errors
  • Support for offset without limit, #446
  • Chainable first method for mysql schema, #406
  • Support for empty array in whereIn
  • Create/drop schema for postgres, #511
  • Inserting multiple rows with default values, #468
  • Join columns are optional for cross-join, #508
  • Flag for creating jsonb columns in Postgresql, #500

0.6.22 - July 10, 2014

  • Bug fix for properly binding postgresql streaming queries, (#363)

0.6.21 - July 9, 2014

  • Bug fix for raw queries not being transaction context aware, (#351).
  • Properly forward stream errors in sqlite3 runner, (#359)

0.6.20 - June 30, 2014

  • Allow case insensitive operators in sql clauses, (#344)

0.6.19 - June 27, 2014

  • Add groupByRaw / orderByRaw methods, better support for raw statements in group / order (#282).
  • Support more config options for node-mysql2 dialect (#341).
  • CLI help text fix, (#342)

0.6.18 - June 25, 2014

  • Patch for the method, calling without a handler should return the stream, not a promise (#337)

0.6.17 - June 23, 2014

  • Adding missing map / reduce proxies to bluebird's implementation

0.6.16 - June 18, 2014

  • Increment / decrement returns the number of affectedRows (#330).
  • Allow --cwd option flag to be passed to CLI tool (#326)

0.6.15 - June 14, 2014

  • Added the as method for aliasing subqueries

0.6.14 - June 14, 2014

  • whereExists / whereNotExists may now take a query builder instance as well as a callback

0.6.13 - June 12, 2014

  • Fix regression with onUpdate / onDelete in PostgreSQL, (#308).
  • Add missing Promise require to knex.js, unit test for knex.destroy (#314)

0.6.12 - June 10, 2014

  • Fix for regression with boolean default types in PostgreSQL

0.6.11 - June 10, 2014

  • Fix for regression with queries containing multiple order by statements in sqlite3

0.6.10 - June 10, 2014

  • Fix for big regression in memoization of column names from 0.5 -> 0.6

0.6.9 - June 9, 2014

  • Fix for regression in specificType method

0.6.8 - June 9, 2014

  • Package.json fix for CLI

0.6.7 - June 9, 2014

  • Adds support for node-mysql2 library.
  • Bundles CLI with the knex install, various related migrate CLI fixes

0.6.6 - June 9, 2014

  • console.warn rather than throw when adding foreignKeys in SQLite3.
  • Add support for dropColumn in SQLite3.
  • Document raw.wrap

0.6.5 - June 9, 2014

  • Add missing _ require to WebSQL builds

0.6.4 - June 9, 2014

  • Fix & document schema.raw method

0.6.3 - June 6, 2014

  • Schema methods on transaction object are now transaction aware (#301).
  • Fix for resolved value from transactions, (#298).
  • Undefined columns are not added to builder

0.6.2 - June 4, 2014

  • Fix regression in raw query output, (#297).
  • Fix regression in "pluck" method (#296).
  • Document first method

0.6.1 - June 4, 2014

  • Reverting to using .npmignore, the "files" syntax forgot the knex.js file

0.6.0 - June 4, 2014

Major Library refactor

  • Major internal overhaul to clean up the various dialect code.
  • Improved unit test suite.
  • Support for the mariasql driver.
  • More consistent use of raw query bindings throughout the library.
  • Queries are more composable, may be injected in various points throughout the builder.
  • Added streaming interface
  • Deprecated 5 argument join in favor of additional join methods.
  • The wrapValue function to allow for array column operations in PostgreSQL (#287).
  • An explicit connection can be passed for any query (#56).
  • Drop column support for sqlite3
  • All schema actions are run sequentially on the same connection if chained.
  • Schema actions can now be wrapped in a transaction
  • .references(tableName.columnName) as shorthand for .references(columnName).inTable(tableName)
  • .join('table.column', 'otherTable.column') as shorthand for .join('table.column', '=', 'otherTable.column')
  • Streams are supported for selects, passing through to the streaming capabilities of node-mysql and node-postgres
  • For More information, see this pull-request

0.5.15 - June 4, 2014

  • Dropped indexes feature now functions correctly, (#278)

0.5.14 - May 6, 2014

  • Remove the charset encoding if it's utf8 for mysql, as it's the default but also currently causes some issues in recent versions of node-mysql

0.5.13 - April 2, 2014

  • Fix regression in array bindings for postgresql (#228)

0.5.12 - Mar 31, 2014

  • Add more operators for where clauses, including && (#226)

0.5.11 - Mar 25, 2014

  • .where(col, 'is', null) or .where(col, 'is not', null) are not supported (#221).
  • Case insensitive where operators now allowed (#212).
  • Fix bug in increment/decrement truncating to an integer (#210).
  • Disconnected connections are now properly handled & removed from the pool (#206).
  • Internal tweaks to binding concatenations for performance (#207)

0.5.10 - Mar 19, 2014

  • Add the .exec method to the internal promise shim

0.5.9 - Mar 18, 2014

  • Remove error'ed connections from the connection pool (#206), added support for node-postgres-pure (pg.js) (#200)

0.5.8 - Feb 27, 2014

  • Fix for chaining on forUpdate / forShare, adding map & reduce from bluebird

0.5.7 - Feb 18, 2014

  • Fix for a null limit / offset breaking query chain (#182)

0.5.6 - Feb 5, 2014

  • Bump bluebird dependency to ~1.0.0, fixing regression in Bluebird 1.0.2 (#176)

0.5.5 - Jan 28, 2014

  • Fix for the exit code on the migrations cli (#151).
  • The init method in knex.migrate now uses this.config if one isn't passed in (#156)

0.5.4 - Jan 7, 2014

  • Fix for using raw statements in defaultTo schema builder methods (#146)

0.5.3 - Jan 2, 2014

  • Fix for incorrectly formed sql when aggregates are used with columns (#144)

0.5.2 - Dec 18, 2013

  • Adding passthrough "catch", "finally" to bluebird implementations, use bluebird's "nodeify" internally for exec

0.5.1 - Dec 12, 2013

  • The returning in PostgreSQL may now accept * or an array of columns to return. If either of these are passed, the response will be an array of objects rather than an array of values. Updates may also now use a returning value. (#132)
  • Added bigint and bigserial type to PostgreSQL. (#111)
  • Fix for the specificType schema call (#118)
  • Several fixes for migrations, including migration file path fixes, passing a Promise constructor to the migration up and down methods, allowing the "knex" module to be used globally, file ordering on migrations, and other small improvements. (#112-115, #125, #135)

0.5.0 - Nov 25, 2013

  • Initial pass at a migration api.
  • Aggregate methods are no longer aliased as "aggregate", but may now be aliased and have more than one aggregate in a query (#108, #110).
  • Adding bigint and bigserial to PostgreSQL (#111).
  • Bugfix on increment/decrement values (#100).
  • Bugfix with having method (#107).
  • Switched from when.js to bluebird for promise implementation, with shim for backward compatibility.
  • Switched from underscore to lodash, for semver reliability

0.4.13 - Oct 31, 2013

  • Fix for aggregate methods on toString and clone, (#98)

0.4.12 - Oct 29, 2013

  • Fix incorrect values passed to float in MySQL and decimal in PostgreSQL

0.4.11 - Oct 15, 2013

  • Fix potential sql injection vulnerability in orderBy, thanks to @sebgie

0.4.10 - Oct 14, 2013

  • Added forUpdate and forShare for select modes in transactions. (#84)
  • Fix bug where current query chain type is not copied on clone. (#90)
  • Charset and collate are now added as methods on the schema builder. (#89)
  • Added into as an alias of from, for builder syntax of: insert(value).into(tableName)
  • Internal pool fixes. (#90)

0.4.9 - Oct 7, 2013

  • Fix for documentation of hasColumn, ensure that hasColumn works with MySQL (#87).
  • More cleanup of error messages, showing the original error message concatenated with the sql and bindings

0.4.8 - Oct 2, 2013

  • Connections are no longer pushed back into the pool if they never existed to begin with (#85)

0.4.7 - Sep 27, 2013

  • The column is now a documented method on the builder api, and takes either an individual column or an array of columns to select

0.4.6 - Sep 25, 2013

  • Standardizing handling of errors for easier debugging, as noted in (#39)

0.4.5 - Sep 24, 2013

  • Fix for hasTable always returning true in MySQL (#82), fix where sql queries were duplicated with multiple calls on toSql with the schema builder

0.4.4 - Sep 22, 2013

  • Fix for debug method not properly debugging individual queries

0.4.3 - Sep 18, 2013

  • Fix for underscore not being defined in various grammar files

0.4.2 - Sep 17, 2013

  • Fix for an error being thrown when an initialized ClientBase instance was passed into Knex.initialize. pool.destroy now optionally accepts a callback to notify when it has completed draining and destroying all connections

0.4.1 - Sep 16, 2013

  • Cleanup from the 0.4.0 release, fix a potential exploit in "where" clauses pointed out by Andri Möll, fix for clients not being properly released from the pool #70, fix for where("foo", "<>", null) doing an "IS NULL" statement

0.4.0 - Sep 13, 2013

Breaking changes

  • Global state is no longer stored in the library, an instance is returned from Knex.initialize, so you will need to call this once and then reference this knex client elsewhere in your application.
  • Lowercasing of knex.raw, knex.transaction, and knex.schema.
  • Created columns are now nullable by default, unless notNullable is chained as an option.
  • Keys created with increments are now assumed to be unsigned (MySQL) by default.
  • The destroyAllNow is no longer called by the library on process.exit event. If you need to call it explicitly yourself, you may use knex.client.destroyPool

0.2.6 - Aug 29, 2013

  • Reject the transaction promise if the transaction "commit" fails, (#50)

0.2.5 - Aug 25, 2013

  • Fix error if a callback isn't specified for exec, (#49)

0.2.4 - Aug 22, 2013

  • Fix SQLite3 delete not returning affected row count, (#45)

0.2.3 - Aug 22, 2013

  • Fix insert with default values in PostgreSQL and SQLite3, (#44)

0.2.2 - Aug 20, 2013

  • Allowing Raw queries to be passed as the primary table names

0.2.1 - Aug 13, 2013

  • Fix for an array passed to insert being mutated

0.2.0 - Aug 7, 2013

Breaking changes

  • hasTable now returns a boolean rather than a failed promise.
  • Changed syntax for insert in postgresql, where the id is not assumed on inserts (#18). The second parameter of insert is now required to return an array of insert id's for the last insert.
  • The timestamp method on the schema builder now uses a dateTime rather than a timestamp

0.1.8 - July 7, 2013

  • Somehow missing the != operator. Using _.find rather than _.where in getCommandsByName(#22)

0.1.7 - June 12, 2013

  • Ensures unhandled errors in the exec callback interface are re-thrown

0.1.6 - June 9, 2013

  • Renaming beforeCreate to afterCreate. Better handling of errors in the connection pooling

0.1.5 - June 9, 2013

  • Added the ability to specify beforeCreate and beforeDestroy hooks on the initialize's options.pool to perform any necessary database setup/teardown on connections before use (#14). where and having may now accept Knex.Raw instances, for consistency (#15). Added an orHaving method to the builder. The ability to specify bindings on Raw queries has been removed

0.1.4 - May 22, 2013

  • defaultTo now accepts "false" for boolean columns, allows for empty strings as default values

0.1.3 - May 18, 2013

  • Enabling table aliases (#11). Fix for issues with transactions not functioning (#12)

0.1.2 - May 15, 2013

  • Bug fixes for groupBy (#7). Mysql using collation, charset config settings in createTable. Added engine on schemaBuilder specifier (#6). Other doc fixes, tests

0.1.1 - May 14, 2013

  • Bug fixes for sub-queries, minor changes to initializing "main" instance, adding "pg" as a valid parameter for the client name in the connection settings

0.1.0 - May 13, 2013

  • Initial Knex release
+ + + + + \ No newline at end of file diff --git a/faq/index.html b/faq/index.html new file mode 100644 index 00000000..71cffb37 --- /dev/null +++ b/faq/index.html @@ -0,0 +1,28 @@ + + + + + + F.A.Q. | Knex.js + + + + + + + + + + + + +

F.A.Q.

How do I help contribute?

Glad you asked! Pull requests, or feature requests, though not always implemented, are a great way to help make Knex even better than it is now. If you're looking for something specific to help out with, there's a number of unit tests that aren't implemented yet, the library could never have too many of those. If you want to submit a fix or feature, take a look at the Contributing readme in the Github and go ahead and open a ticket.

How do I debug?

Knex is beginning to make use of the debug module internally, so you can set the DEBUG environment variable to knex:* to see all debugging, or select individual namespaces DEBUG=knex:query,knex:tx to constrain a bit.

If you pass {debug: true} as one of the options in your initialize settings, you can see all of the query calls being made. Sometimes you need to dive a bit further into the various calls and see what all is going on behind the scenes. I'd recommend node-inspector, which allows you to debug code with debugger statements like you would in the browser.

At the start of your application code will catch any errors not otherwise caught in the normal promise chain handlers, which is very helpful in debugging.

How do I run the test suite?

The test suite looks for an environment variable called KNEX_TEST for the path to the database configuration. If you run the following command:

$ export KNEX_TEST='/path/to/your/knex_config.js'
+$ npm test
+

replacing with the path to your config file, and the config file is valid, the test suite should run properly.

My tests are failing because slow DB connection and short test timeouts! How to extend test timeouts?

Sometimes, e.g. when running CI on travis, test suite's default timeout of 5 seconds might be too short. In such cases an alternative test timeout value in milliseconds can be specified using the KNEX_TEST_TIMEOUT environment variable.

$ export KNEX_TEST_TIMEOUT=30000
+$ npm test
+

I found something broken with Amazon Redshift! Can you help?

Because there is no testing platform available for Amazon Redshift, be aware that it is included as a dialect but is unsupported. With that said, please file an issue if something is found to be broken that is not noted in the documentation, and we will do our best.

+ + + + + \ No newline at end of file diff --git a/faq/recipes.html b/faq/recipes.html new file mode 100644 index 00000000..24aac874 --- /dev/null +++ b/faq/recipes.html @@ -0,0 +1,155 @@ + + + + + + Recipes | Knex.js + + + + + + + + + + + + +

Recipes

Using non-standard database that is compatible with PostgreSQL wire protocol (such as CockroachDB)

Specify PostgreSQL version that database you are using is compatible with protocol-wise using version option, e. g.:

const knex = require('knex')({
+  client: 'pg',
+  version: '7.2',
+  connection: {
+    host: '127.0.0.1',
+    user: 'your_database_user',
+    password: 'your_database_password',
+    database: 'myapp_test'
+  }
+});
+

Note that value of version option should be not the version of the database that you are using, but version of PostgreSQL that most closely matches functionality of the database that you are using. If not provided by database vendor, try using '7.2' as a baseline and keep increasing (within the range of existing PostgreSQL versions) until it starts (or stops) working.

There are also known incompatibilities with migrations for databases that do not support select for update. See https://github.com/tgriesser/knex/issues/2002 for a workaround.

Connecting to MSSQL on Azure SQL Database

{encrypt: true} should be included in options branch of connection configuration:

knex({
+  client : 'mssql',
+  connection: {
+    database: 'mydatabase',
+    server: 'myserver.database.windows.net',
+    user: 'myuser',
+    password: 'mypass',
+    port: 1433,
+    connectionTimeout: 30000,
+    options: {
+      encrypt: true
+    }
+  }
+});
+

See all of node-mssql's connection options

Adding a full-text index for PostgreSQL

exports.up = (knex) => {
+  return knex.schema.createTable('foo', (table) => {
+    table.increments('id');
+    table.specificType('fulltext', 'tsvector');
+    table.index('fulltext', null, 'gin');
+  })
+};
+

DB access using SQLite and SQLCipher

After you build the SQLCipher source and the npm SQLite3 package, and encrypt your DB (look elsewhere for these things), then anytime you open your database, you need to provide your encryption key using the SQL statement:

PRAGMA KEY = 'secret'
+

This PRAGMA is more completely documented in the SQLCipher site. When working with Knex this is best done when opening the DB, via the following:

const myDBConfig = {
+  client: "sqlite3",
+  connection: {
+    filename: "myEncryptedSQLiteDbFile.db"
+  },
+  pool: {
+    afterCreate: function(conn, done) {
+      conn.run("PRAGMA KEY = 'secret'");
+      done();
+    }  
+  } 
+};
+const knex = require('knex')(myDBConfig);
+

Of course embedding the key value in your code is a poor security practice. Instead, retrieve the 'secret' from elsewhere.

The key Knex thing to note here is the "afterCreate" function. This is documented in the knexjs.org site, but is not in the Table of Contents at this time, so do a browser find when on the site to get to it. It allows auto-updating DB settings when creating any new pool connections (of which there will only ever be one per file for Knex-SQLite).

If you don't use the "afterCreate" configuration, then you will need to run a knex.raw statement with each and every SQL you execute, something like as follows:

return knex.raw("PRAGMA KEY = 'secret'")
+  .then(() => knex('some_table')
+    .select()
+    .on('query-error', function(ex, obj) {
+      console.log(
+        "KNEX select from some_table ERR ex:", 
+        ex, 
+        "obj:", 
+        obj
+      );
+    })
+  );
+

Maintaining changelog for seeds (version >= 0.16.0-next1)

In case you would like to use Knex.js changelog functionality to ensure your environments are only seeded once, but don't want to mix seed files with migration files, you can specify multiple directories as a source for your migrations:

await knex.migrate.latest({
+    directory: [
+      'src/services/orders/database/migrations',
+      'src/services/orders/database/seeds'
+    ],
+    sortDirsSeparately: true,
+    tableName: 'orders_migrations',
+    schemaName: 'orders',  
+})
+

Using explicit transaction management together with async code

await knex.transaction(trx => {
+  async function stuff() {
+    trx.rollback(new Error('Foo'));
+  };
+  stuff()
+    .then(() => {
+      // do something
+    });
+});
+

Or alternatively:

try {
+  await knex.transaction(trx => {
+    async function stuff() {
+      trx.rollback(new Error('always explicit rollback this time'));
+    }
+    stuff();
+  }); 
+  // transaction was committed
+  } catch (err) {
+    // transaction was rolled back 
+  }
+

(note that promise for knex.transaction resolves after transaction is rolled back or committed)

Using parentheses with AND operator

In order to generate query along the lines of

SELECT "firstName", "lastName", "status"
+FROM "userInfo" 
+WHERE "status" = 'active'
+AND ("firstName" ILIKE '%Ali%' OR "lastName" ILIKE '%Ali%');
+

you need to use following approach:

queryBuilder
+  .where('status', status.uuid)
+  .andWhere((qB) => qB
+    .where('firstName', 'ilike', `%${q}%`)
+    .orWhere('lastName', 'ilike', `%${q}%`)
+  )
+

Calling an oracle stored procedure with bindout variables

How to call and retrieve output from an oracle stored procedure

const oracle = require('oracledb');
+const bindVars = {
+  input_var1: 6,
+  input_var2: 7,
+  output_var: {
+    dir: oracle.BIND_OUT
+  },
+  output_message: {
+    dir: oracle.BIND_OUT
+  }
+};
+
+const sp = 'BEGIN MULTIPLY_STORED_PROCEDURE(:input_var1, :input_var2, :output_var, :output_message); END;';
+const results = await knex.raw(sp, bindVars);
+console.log(results[0]); // 42
+console.log(results[1]); // 6 * 7 is the answer to life
+

Node instance doesn't stop after using knex

Make sure to close knex instance after execution to avoid Node process hanging due to open connections:

async function migrate() {
+  try {
+    await knex.migrate.latest({/**config**/})
+  } catch (e) {
+    process.exit(1)
+  } finally {
+    try {
+      knex.destroy()
+    } catch (e) {
+      // ignore
+    }
+  }
+}
+
+migrate()
+

Manually Closing Streams

When using Knex's stream interface, you can typically just pipe the return stream to any writable stream. However, with HTTPIncomingMessage, you'll need to take special care to handle aborted requests.

An HTTPIncomingMessage object is typically called request. This is the first argument in 'request' events emitted on http.Server instances. Express's req implements a compatible interface and Hapi exposes this object on its request objects as request.raw.req.

You need to explicitly handle the case where an HTTPIncomingMessage is closed prematurely when streaming from a database with Knex. The easiest way to cause this is:

  1. Visit an endpoint that takes several seconds to fully transmit a response
  2. Close the browser window immediately after beginning the request

When this happens while you are streaming a query to a client, you need to manually tell Knex that it can release the database connection in use back to the connection pool.

server.on('request', function (request, response) {
+  const stream = knex.select('*').from('items').stream();
+  request.on('close', stream.end.bind(stream));
+});
+
+ + + + + \ No newline at end of file diff --git a/faq/support.html b/faq/support.html new file mode 100644 index 00000000..b0141b69 --- /dev/null +++ b/faq/support.html @@ -0,0 +1,24 @@ + + + + + + Support | Knex.js + + + + + + + + + + + + +

Support

Have questions about the library? Come join us in the #bookshelf freenode IRC channel for support on knex.js and bookshelf.js, or post an issue on Stack Overflow or in the GitHub issue tracker.

+ + + + + \ No newline at end of file diff --git a/guide/extending.html b/guide/extending.html new file mode 100644 index 00000000..84426c1e --- /dev/null +++ b/guide/extending.html @@ -0,0 +1,57 @@ + + + + + + Extending | Knex.js + + + + + + + + + + + + +

Extending

To extend knex's builders, we have the following methods

knex.SchemaBuilder.extend("functionName", function() {
+    console.log('Custom Schema Builder Function');
+    return this;
+});
+knex.TableBuilder.extend("functionName", function() {
+    console.log('Custom Table Builder Function');
+    return this;
+});
+knex.ViewBuilder.extend("functionName", function() {
+    console.log('Custom View Builder Function');
+    return this;
+});
+knex.ColumnBuilder.extend("functionName", function() {
+    console.log('Custom Column Builder Function');
+    return this;
+});
+

To add typescript support you can add the following (.d.ts):

import "knex";
+declare module "knex" {
+    namespace Knex {
+        interface SchemaBuilder {
+            functionName (): Knex.SchemaBuilder;
+        }
+        interface TableBuilder {
+            functionName (): Knex.TableBuilder;
+        }
+        interface ViewBuilder {
+            functionName (): Knex.ViewBuilder;
+        }
+        interface ColumnBuilder {
+            functionName (): Knex.ColumnBuilder;
+        }
+    }
+}
+
+ + + + + \ No newline at end of file diff --git a/guide/index.html b/guide/index.html new file mode 100644 index 00000000..3aa247d0 --- /dev/null +++ b/guide/index.html @@ -0,0 +1,380 @@ + + + + + + Installation | Knex.js + + + + + + + + + + + + +

Installation

Knex can be used as an SQL query builder in both Node.JS and the browser, limited to WebSQL's constraints (like the inability to drop tables or read schemas). Composing SQL queries in the browser for execution on the server is highly discouraged, as this can be the cause of serious security vulnerabilities. The browser builds outside of WebSQL are primarily for learning purposes - for example, you can pop open the console and build queries on this page using the knex object.

Node.js

The primary target environment for Knex is Node.js, you will need to install the knex library, and then install the appropriate database library: pg for PostgreSQL, CockroachDB and Amazon Redshift, pg-native for PostgreSQL with native C++ libpq bindings (requires PostgresSQL installed to link against), mysql for MySQL or MariaDB, sqlite3 for SQLite3, or tedious for MSSQL.

$ npm install knex --save
+
+# Then add one of the following (adding a --save) flag:
+$ npm install pg
+$ npm install pg-native
+$ npm install sqlite3
+$ npm install better-sqlite3
+$ npm install mysql
+$ npm install mysql2
+$ npm install oracledb
+$ npm install tedious
+

If you want to use CockroachDB or Redshift instance, you can use the pg driver.

If you want to use a MariaDB instance, you can use the mysql driver.

Browser

Knex can be built using a JavaScript build tool such as browserify or webpack. In fact, this documentation uses a webpack build which includes knex. View source on this page to see the browser build in-action (the global knex variable).

Configuration Options

The knex module is itself a function which takes a configuration object for Knex, accepting a few parameters. The client parameter is required and determines which client adapter will be used with the library.

const knex = require('knex')({
+  client: 'mysql',
+  connection: {
+    host : '127.0.0.1',
+    port : 3306,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  }
+});
+

The connection options are passed directly to the appropriate database client to create the connection, and may be either an object, a connection string, or a function returning an object:

PostgreSQL

Knex's PostgreSQL client allows you to set the initial search path for each connection automatically using an additional option "searchPath" as shown below.

const pg = require('knex')({
+  client: 'pg',
+  connection: process.env.PG_CONNECTION_STRING,
+  searchPath: ['knex', 'public'],
+});
+

When using the PostgreSQL driver, another usage pattern for instantiating the Knex configuration object could be to use a connection: {} object details to specify various flags such as enabling SSL, a connection string, and individual connection configuration fields all in the same object. Consider the following example:

PostgreSQL

If connectionString is highest priority to use. If left unspecified then connection details will be determined using the individual connection fields (host, port, etc), and finally an SSL configuration will be enabled based on a truthy value of config["DB_SSL"] which will also accept self-signed certificates.

const pg = require('knex')({
+  client: 'pg',
+  connection: {
+    connectionString: config.DATABASE_URL,
+    host: config["DB_HOST"],
+    port: config["DB_PORT"],
+    user: config["DB_USER"],
+    database: config["DB_NAME"],
+    password: config["DB_PASSWORD"],
+    ssl: config["DB_SSL"] ? { rejectUnauthorized: false } : false,
+  }
+});
+

The following are SQLite usage patterns for instantiating the Knex configuration object:

SQLite3 or Better-SQLite3

When you use the SQLite3 or Better-SQLite3 adapter, there is a filename required, not a network connection. For example:

const knex = require('knex')({
+  client: 'sqlite3', // or 'better-sqlite3'
+  connection: {
+    filename: "./mydb.sqlite"
+  }
+});
+

You can also run either SQLite3 or Better-SQLite3 with an in-memory database by providing :memory: as the filename. For example:

const knex = require('knex')({
+  client: 'sqlite3', // or 'better-sqlite3'
+  connection: {
+    filename: ":memory:"
+  }
+});
+

SQLite3

When you use the SQLite3 adapter, you can set flags used to open the connection. For example:

const knex = require('knex')({
+  client: 'sqlite3',
+  connection: {
+    filename: "file:memDb1?mode=memory&cache=shared",
+    flags: ['OPEN_URI', 'OPEN_SHAREDCACHE']
+  }
+});
+

Better-SQLite3

With the Better-SQLite3 adapter, you can use options.nativeBinding to specify the location of the adapter's compiled C++ addon. This can be useful when your build system does a lot of transformation/relocation of files.

Example use:

const knex = require('knex')({
+  client: 'better-sqlite3',
+  connection: {
+    filename: ":memory:",
+    options: {
+      nativeBinding: "/path/to/better_sqlite3.node",
+    },
+  },
+});
+

Additionally, you can open the database in read-only mode using options.readonly:

const knex = require('knex')({
+  client: 'better-sqlite3',
+  connection: {
+    filename: "/path/to/db.sqlite3",
+    options: {
+      readonly: true,
+    },
+  },
+});
+

For more information, see the Better-SQLite3 documentation on database connection options.

MSSQL

When you use the MSSQL client, you can define a mapBinding function to define your own logic for mapping from knex query parameters to tedious types. Returning undefined from the function will fallback to the default mapping.

import { TYPES } from 'tedious';
+
+const knex = require('knex')({
+  client: 'mssql',
+  connection: {
+    options: {
+      mapBinding: value => {
+        // bind all strings to varchar instead of nvarchar
+        if (typeof value === 'string') {
+          return {
+            type: TYPES.VarChar,
+            value
+          };
+        }
+
+        // allow devs to pass tedious type at query time
+        if (value != null && value.type) {
+          return {
+            type: value.type,
+            value: value.value
+          };
+        }
+
+        // undefined is returned; falling back to default mapping function
+      }
+    }
+  }
+});
+

INFO

The database version can be added in knex configuration, when you use the PostgreSQL adapter to connect a non-standard database.

const knex = require('knex')({
+  client: 'pg',
+  version: '7.2',
+  connection: {
+    host : '127.0.0.1',
+    port : 5432,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  }
+});
+
const knex = require('knex')({
+  client: 'mysql',
+  version: '5.7',
+  connection: {
+    host : '127.0.0.1',
+    port : 3306,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  }
+});
+

INFO

When using a custom PostgreSQL client like knex-aurora-data-api-client, you can explicitly state if it supports jsonb column types

const knex = require('knex')({
+    client: require('knex-aurora-data-api-client').postgres,
+    connection: { resourceArn, secretArn, database: `mydb` },
+    version: 'data-api',
+    jsonbSupport: true
+})
+

A function can be used to determine the connection configuration dynamically. This function receives no parameters, and returns either a configuration object or a promise for a configuration object.

const knex = require('knex')({
+  client: 'sqlite3',
+  connection: () => ({
+    filename: process.env.SQLITE_FILENAME
+  })
+});
+

By default, the configuration object received via a function is cached and reused for all connections. To change this behavior, an expirationChecker function can be returned as part of the configuration object. The expirationChecker is consulted before trying to create new connections, and in case it returns true, a new configuration object is retrieved. For example, to work with an authentication token that has a limited lifespan:

const knex = require('knex')({
+  client: 'postgres',
+  connection: async () => {
+    const { 
+      token, 
+      tokenExpiration 
+    } = await someCallToGetTheToken();
+
+    return {
+      host : 'your_host',
+      port : 5432,
+      user : 'your_database_user',
+      password : token,
+      database : 'myapp_test',
+      expirationChecker: () => {
+        return tokenExpiration <= Date.now();
+      }
+    };
+  }
+});
+

You can also connect via a unix domain socket, which will ignore host and port.

const knex = require('knex')({
+  client: 'mysql',
+  connection: {
+    socketPath : '/path/to/socket.sock',
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  }
+});
+

userParams is an optional parameter that allows you to pass arbitrary parameters which will be accessible via knex.userParams property:

const knex = require('knex')({
+  client: 'mysql',
+  connection: {
+    host : '127.0.0.1',
+    port : 3306,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  },
+  userParams: {
+    userParam1: '451'
+  }
+});
+

Initializing the library should normally only ever happen once in your application, as it creates a connection pool for the current database, you should use the instance returned from the initialize call throughout your library.

Specify the client for the particular flavour of SQL you are interested in.

const pg = require('knex')({client: 'pg'});
+
+knex('table')
+  .insert({a: 'b'})
+  .returning('*')
+  .toString();
+// "insert into "table" ("a") values ('b')"
+
+pg('table')
+  .insert({a: 'b'})
+  .returning('*')
+  .toString();
+// "insert into "table" ("a") values ('b') returning *"
+

withUserParams

You can call method withUserParams on a Knex instance if you want to get a copy (with same connections) with custom parameters (e. g. to execute same migrations with different parameters)

const knex = require('knex')({
+  // Params
+});
+
+const knexWithParams = knex.withUserParams({ 
+  customUserParam: 'table1'
+});
+const customUserParam = knexWithParams
+  .userParams
+  .customUserParam;
+

debug

Passing a debug: true flag on your initialization object will turn on debugging for all queries.

asyncStackTraces

Passing an asyncStackTraces: true flag on your initialization object will turn on stack trace capture for all query builders, raw queries and schema builders. When a DB driver returns an error, this previously captured stack trace is thrown instead of a new one. This helps to mitigate default behaviour of await in node.js/V8 which blows the stack away. This has small performance overhead, so it is advised to use only for development. Turned off by default.

pool

The client created by the configuration initializes a connection pool, using the tarn.js library. This connection pool has a default setting of a min: 2, max: 10 for the MySQL and PG libraries, and a single connection for sqlite3 (due to issues with utilizing multiple connections on a single file). To change the config settings for the pool, pass a pool option as one of the keys in the initialize block.

Note that the default value of min is 2 only for historical reasons. It can result in problems with stale connections, despite tarn's default idle connection timeout of 30 seconds, which is only applied when there are more than min active connections. It is recommended to set min: 0 so all idle connections can be terminated.

Checkout the tarn.js library for more information.

const knex = require('knex')({
+  client: 'mysql',
+  connection: {
+    host : '127.0.0.1',
+    port : 3306,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  },
+  pool: { min: 0, max: 7 }
+});
+

If you ever need to explicitly teardown the connection pool, you may use knex.destroy([callback]). You may use knex.destroy by passing a callback, or by chaining as a promise, just not both. To manually initialize a destroyed connection pool, you may use knex.initialize([config]), if no config is passed, it will use the first knex configuration used.

afterCreate

afterCreate callback (rawDriverConnection, done) is called when the pool aquires a new connection from the database server. done(err, connection) callback must be called for knex to be able to decide if the connection is ok or if it should be discarded right away from the pool.

const knex = require('knex')({
+  client: 'pg',
+  connection: {/*...*/},
+  pool: {
+    afterCreate: function (conn, done) {
+      // in this example we use pg driver's connection API
+      conn.query('SET timezone="UTC";', function (err) {
+        if (err) {
+          // first query failed, 
+          // return error and don't try to make next query
+          done(err, conn);
+        } else {
+          // do the second query...
+          conn.query(
+            'SELECT set_limit(0.01);', 
+            function (err) {
+              // if err is not falsy, 
+              //  connection is discarded from pool
+              // if connection aquire was triggered by a 
+              // query the error is passed to query promise
+              done(err, conn);
+            });
+        }
+      });
+    }
+  }
+});
+

acquireConnectionTimeout

acquireConnectionTimeout defaults to 60000ms and is used to determine how long knex should wait before throwing a timeout error when acquiring a connection is not possible. The most common cause for this is using up all the pool for transaction connections and then attempting to run queries outside of transactions while the pool is still full. The error thrown will provide information on the query the connection was for to simplify the job of locating the culprit.

const knex = require('knex')({
+  client: 'pg',
+  connection: {/*...*/},
+  pool: {/*...*/},
+  acquireConnectionTimeout: 10000
+});
+

fetchAsString

Utilized by Oracledb. An array of types. The valid types are 'DATE', 'NUMBER' and 'CLOB'. When any column having one of the specified types is queried, the column data is returned as a string instead of the default representation.

const knex = require('knex')({
+  client: 'oracledb',
+  connection: {/*...*/},
+  fetchAsString: [ 'number', 'clob' ]
+});
+

migrations

For convenience, any migration configuration may be specified when initializing the library. Read the Migrations section for more information and a full list of configuration options.

const knex = require('knex')({
+  client: 'mysql',
+  connection: {
+    host : '127.0.0.1',
+    port : 3306,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  },
+  migrations: {
+    tableName: 'migrations'
+  }
+});
+

postProcessResponse

Hook for modifying returned rows, before passing them forward to user. One can do for example snake_case -> camelCase conversion for returned columns with this hook. The queryContext is only available if configured for a query builder instance via queryContext.

const knex = require('knex')({
+  client: 'mysql',
+  // overly simplified snake_case -> camelCase converter
+  postProcessResponse: (result, queryContext) => {
+    // TODO: add special case for raw results 
+    // (depends on dialect)
+    if (Array.isArray(result)) {
+      return result.map(row => convertToCamel(row));
+    } else {
+      return convertToCamel(result);
+    }
+  }
+});
+

wrapIdentifier

Knex supports transforming identifier names automatically to quoted versions for each dialect. For example 'Table.columnName as foo' for PostgreSQL is converted to "Table"."columnName" as "foo".

With wrapIdentifier one may override the way how identifiers are transformed. It can be used to override default functionality and for example to help doing camelCase -> snake_case conversion.

Conversion function wrapIdentifier(value, dialectImpl, context): string gets each part of the identifier as a single value, the original conversion function from the dialect implementation and the queryContext, which is only available if configured for a query builder instance via builder.queryContext, and for schema builder instances via schema.queryContext or table.queryContext. For example, with the query builder, knex('table').withSchema('foo').select('table.field as otherName').where('id', 1) will call wrapIdentifier converter for following values 'table', 'foo', 'table', 'field', 'otherName' and 'id'.

const knex = require('knex')({
+  client: 'mysql',
+  // overly simplified camelCase -> snake_case converter
+  wrapIdentifier: (
+    value, 
+    origImpl, 
+    queryContext
+  ) => origImpl(convertToSnakeCase(value))
+});
+

log

Knex contains some internal log functions for printing warnings, errors, deprecations, and debug information when applicable. These log functions typically log to the console, but can be overwritten using the log option and providing alternative functions. Different log functions can be used for separate knex instances.

const knex = require('knex')({
+  log: {
+    warn(message) {
+    },
+    error(message) {
+    },
+    deprecate(message) {
+    },
+    debug(message) {
+    },
+  }
+});
+

compileSqlOnError

Knex builds an error message in case of query error. By default Knex adds compiled SQL (SELECT * FROM users WHERE password = 'myPassword') to the error message. This can be changed to parameterized SQL (SELECT * FROM users WHERE password = ?) by setting compileSqlOnError to false.

const knex = require('knex')({
+  compileSqlOnError: false
+});
+

TypeScript

While knex is written in JavaScript, officially supported TypeScript bindings are available (within the knex npm package).

However it is to be noted that TypeScript support is currently best-effort. Knex has a very flexible API and not all usage patterns can be type-checked and in most such cases we err on the side of flexibility. In particular, lack of type errors doesn't currently guarantee that the generated queries will be correct and therefore writing tests for them is recommended even if you are using TypeScript.

Many of the APIs accept TRecord and TResult type parameters, using which we can specify the type of a row in the database table and the type of the result of the query respectively. This is helpful for auto-completion when using TypeScript-aware editors like VSCode.

To reduce boilerplate and add inferred types, you can augment Tables interface in 'knex/types/tables' module.

import { Knex } from 'knex';
+
+declare module 'knex/types/tables' {
+  interface User {
+    id: number;
+    name: string;
+    created_at: string;
+    updated_at: string;
+  }
+  
+  interface Tables {
+    // This is same as specifying `knex<User>('users')`
+    users: User;
+    // For more advanced types, you can specify separate type
+    // for base model, "insert" type and "update" type.
+    // But first: notice that if you choose to use this, 
+    // the basic typing showed above can be ignored.
+    // So, this is like specifying
+    //    knex
+    //    .insert<{ name: string }>({ name: 'name' })
+    //    .into<{ name: string, id: number }>('users')
+    users_composite: Knex.CompositeTableType<
+      // This interface will be used for return type and 
+      // `where`, `having` etc where full type is required 
+      User,
+      // Specifying "insert" type will also make sure
+      // data matches interface in full. Meaning
+      // if interface is `{ a: string, b: string }`,
+      // `insert({ a: '' })` will complain about missing fields.
+      // 
+      // For example, this will require only "name" field when inserting
+      // and make created_at and updated_at optional.
+      // And "id" can't be provided at all.
+      // Defaults to "base" type.
+      Pick<User, 'name'> & Partial<Pick<User, 'created_at' | 'updated_at'>>,
+      // This interface is used for "update()" calls.
+      // As opposed to regular specifying interface only once,
+      // when specifying separate update interface, user will be
+      // required to match it  exactly. So it's recommended to
+      // provide partial interfaces for "update". Unless you want to always
+      // require some field (e.g., `Partial<User> & { updated_at: string }`
+      // will allow updating any field for User but require updated_at to be
+      // always provided as well.
+      // 
+      // For example, this wil allow updating all fields except "id".
+      // "id" will still be usable for `where` clauses so
+      //      knex('users_composite')
+      //      .update({ name: 'name2' })
+      //      .where('id', 10)`
+      // will still work.
+      // Defaults to Partial "insert" type
+      Partial<Omit<User, 'id'>>
+    >;
+  }
+}
+
+ + + + + \ No newline at end of file diff --git a/guide/interfaces.html b/guide/interfaces.html new file mode 100644 index 00000000..524ed4c6 --- /dev/null +++ b/guide/interfaces.html @@ -0,0 +1,183 @@ + + + + + + Interfaces | Knex.js + + + + + + + + + + + + +

Interfaces

Knex.js provides several options to deal with query output. The following methods are present on the query builder, schema builder, and the raw builder:

Promises

Promises are the preferred way of dealing with queries in knex, as they allow you to return values from a fulfillment handler, which in turn become the value of the promise. The main benefit of promises are the ability to catch thrown errors without crashing the node app, making your code behave like a .try / .catch / .finally in synchronous code.

knex.select('name')
+  .from('users')
+  .where('id', '>', 20)
+  .andWhere('id', '<', 200)
+  .limit(10)
+  .offset(x)
+  .then(function(rows) {
+    return _.pluck(rows, 'name');
+  })
+  .then(function(names) {
+    return knex.select('id')
+      .from('nicknames')
+      .whereIn('nickname', names);
+  })
+  .then(function(rows) {
+    console.log(rows);
+  })
+  .catch(function(error) {
+    console.error(error)
+  });
+

then

.then(onFulfilled, [onRejected])

Coerces the current query builder chain into a promise state, accepting the resolve and reject handlers as specified by the Promises/A+ spec. As stated in the spec, more than one call to the then method for the current query chain will resolve with the same value, in the order they were called; the query will not be executed multiple times.

knex.select('*')
+  .from('users')
+  .where({name: 'Tim'})
+  .then(function(rows) {
+    return knex
+      .insert({user_id: rows[0].id, name: 'Test'}, 'id')
+      .into('accounts');
+  })
+  .then(function(id) {
+    console.log('Inserted Account ' + id);
+  })
+  .catch(function(error) { console.error(error); });
+

catch

.catch(onRejected)

Coerces the current query builder into a promise state, catching any error thrown by the query, the same as calling .then(null, onRejected).

return knex.insert({id: 1, name: 'Test'}, 'id')
+  .into('accounts')
+  .catch(function(error) {
+    console.error(error);
+  })
+  .then(function() {
+    return knex.select('*')
+      .from('accounts')
+      .where('id', 1);
+  })
+  .then(function(rows) {
+    console.log(rows[0]);
+  })
+  .catch(function(error) {
+    console.error(error);
+  });
+

Callbacks

asCallback

.asCallback(callback)

If you'd prefer a callback interface over promises, the asCallback function accepts a standard node style callback for executing the query chain. Note that as with the then method, subsequent calls to the same query chain will return the same result.

knex.select('name').from('users')
+  .where('id', '>', 20)
+  .andWhere('id', '<', 200)
+  .limit(10)
+  .offset(x)
+  .asCallback(function(err, rows) {
+    if (err) return console.error(err);
+    knex.select('id')
+      .from('nicknames')
+      .whereIn('nickname', _.pluck(rows, 'name'))
+      .asCallback(function(err, rows) {
+        if (err) return console.error(err);
+        console.log(rows);
+      });
+  });
+

Streams

Streams are a powerful way of piping data through as it comes in, rather than all at once. You can read more about streams here at substack's stream handbook. See the following for example uses of stream & pipe. If you wish to use streams with PostgreSQL, you must also install the pg-query-stream module. If you wish to use streams with the pgnative dialect, please be aware that the results will not be streamed as they are received, but rather streamed after the entire result set has returned. On an HTTP server, make sure to manually close your streams if a request is aborted.

stream

.stream([options], [callback])

If called with a callback, the callback is passed the stream and a promise is returned. Otherwise, the readable stream is returned.
When the stream is consumed as an iterator, if the loop terminates with a break, return, or a throw, the stream will be destroyed. In other terms, iterating over a stream will consume the stream fully.

// Retrieve the stream:
+const stream = knex.select('*')
+  .from('users')
+  .stream();
+stream.pipe(writableStream);
+
// With options:
+const stream = knex.select('*')
+  .from('users')
+  .stream({highWaterMark: 5});
+stream.pipe(writableStream);
+
// Use as an iterator
+const stream = knex.select('*')
+  .from('users')
+  .stream();
+
+for await (const row of stream) {
+  /* ... */
+}
+
// Use as a promise:
+const stream = knex.select('*')
+  .from('users')
+  .where(knex.raw('id = ?', [1]))
+  .stream(function(stream) {
+    stream.pipe(writableStream);
+  })
+  .then(function() { /* ... */ })
+  .catch(function(e) { console.error(e); });
+

pipe

.pipe(writableStream)

Pipe a stream for the current query to a writableStream.

const stream = knex.select('*')
+  .from('users')
+  .pipe(writableStream);
+

Events

query

A query event is fired just before a query takes place, providing data about the query, including the connection's __knexUid / __knexTxId properties and any other information about the query as described in toSQL. Useful for logging all queries throughout your application.

knex.select('*')
+  .from('users')
+  .on('query', function(data) {
+    app.log(data);
+  })
+  .then(function() {
+    // ...
+  });
+

query-error

A query-error event is fired when an error occurs when running a query, providing the error object and data about the query, including the connection's __knexUid / __knexTxId properties and any other information about the query as described in toSQL. Useful for logging all query errors throughout your application.

knex.select(['NonExistentColumn'])
+  .from('users')
+  .on('query-error', function(error, obj) {
+    app.log(error);
+  })
+  .then(function() { /* ... */ })
+  .catch(function(error) {
+    // Same error object as the query-error event provides.
+  });
+

query-response

A query-response event is fired when a successful query has been run, providing the response of the query and data about the query, including the connection's __knexUid / __knexTxId properties and any other information about the query as described in toSQL, and finally the query builder used for the query.

knex.select('*')
+  .from('users')
+  .on('query-response', function(response, obj, builder) {
+    // ...
+  })
+  .then(function(response) {
+    // Same response as the emitted event
+  })
+  .catch(function(error) { });
+

start

A start event is fired right before a query-builder is compiled.

INFO

While this event can be used to alter a builders state prior to compilation it is not to be recommended. Future goals include ways of doing this in a different manner such as hooks.

knex.select('*')
+  .from('users')
+  .on('start', function(builder) {
+    builder
+    .where('IsPrivate', 0)
+  })
+  .then(function(Rows) {
+    //Only contains Rows where IsPrivate = 0
+  })
+  .catch(function(error) { });
+

Other

toString

.toString()

Returns an array of query strings filled out with the correct values based on bindings, etc. Useful for debugging, but should not be used to create queries for running them against DB.

const toStringQuery = knex.select('*')
+  .from('users')
+  .where('id', 1)
+  .toString();
+
+// Outputs: console.log(toStringQuery); 
+// select * from "users" where "id" = 1
+

toSQL

.toSQL()
.toSQL().toNative()

Returns an array of query strings filled out with the correct values based on bindings, etc. Useful for debugging and building queries for running them manually with DB driver. .toSQL().toNative() outputs object with sql string and bindings in a dialects format in the same way that knex internally sends them to underlying DB driver.

knex.select('*')
+  .from('users')
+  .where(knex.raw('id = ?', [1]))
+  .toSQL()
+// Outputs:
+// {
+//   bindings: [1],
+//   method: 'select',
+//   sql: 'select * from "users" where id = ?',
+//   options: undefined,
+//   toNative: function () {}
+// }
+
+knex.select('*')
+  .from('users')
+  .where(knex.raw('id = ?', [1]))
+  .toSQL()
+  .toNative()
+// Outputs for postgresql dialect:
+// {
+//   bindings: [1],
+//   sql: 'select * from "users" where id = $1',
+// }
+
+ + + + + \ No newline at end of file diff --git a/guide/migrations.html b/guide/migrations.html new file mode 100644 index 00000000..c00e5ae5 --- /dev/null +++ b/guide/migrations.html @@ -0,0 +1,315 @@ + + + + + + Migrations | Knex.js + + + + + + + + + + + + +

Migrations

Migrations allow for you to define sets of schema changes so upgrading a database is a breeze.

Migration CLI

The migration CLI is bundled with the knex install, and is driven by the node-liftoff module. To install globally, run:

$ npm install knex -g
+

The migration CLI accepts the following general command-line options. You can view help text and additional options for each command using --help. E.g. knex migrate:latest --help.

  • --debug: Run with debugging
  • --knexfile [path]: Specify the knexfile path
  • --knexpath [path]: Specify the path to the knex instance
  • --cwd [path]: Specify the working directory
  • --client [name]: Set the DB client
  • --connection [address]: Set the DB connection
  • --migrations-table-name: Set the migration table name
  • --migrations-directory: Set the migrations directory
  • --env: environment, default: process.env.NODE_ENV || development
  • --esm: Enables ESM module interoperability
  • --help: Display help text for a particular command and exit.

Migrations use a knexfile, which specify various configuration settings for the module. To create a new knexfile, run the following:

$ knex init
+
+# or for .ts
+
+$ knex init -x ts
+

will create a sample knexfile.js - the file which contains our various database configurations. Once you have a knexfile.js, you can use the migration tool to create migration files to the specified directory (default migrations). Creating new migration files can be achieved by running:

$ knex migrate:make migration_name 
+
+# or for .ts
+
+$ knex migrate:make migration_name -x ts
+
  • you can also create your migration using a specific stub file, this serves as a migration template to speed up development for common migration operations
  • if the --stub option is not passed, the CLI will use either the knex default stub for the chosen extension, or the config.stub file
$ knex migrate:make --stub 
+
+# or
+
+$ knex migrate:make --stub 
+
  • if a stub path is provided, it must be relative to the knexfile.[js, ts, etc] location
  • if a is used, the stub is selected by its file name. The CLI will look for this file in the config.migrations.directory folder. If the config.migrations.directory is not defined, this operation will fail

Once you have finished writing the migrations, you can update the database matching your NODE_ENV by running:

$ knex migrate:latest
+

You can also pass the --env flag or set NODE_ENV to select an alternative environment:

$ knex migrate:latest --env production
+
+# or
+
+$ NODE_ENV=production knex migrate:latest
+

To rollback the last batch of migrations:

$ knex migrate:rollback
+

To rollback all the completed migrations:

$ knex migrate:rollback --all
+

To run the next migration that has not yet been run

$ knex migrate:up
+

To run the specified migration that has not yet been run

$ knex migrate:up 001_migration_name.js
+

To undo the last migration that was run

$ knex migrate:down
+

To undo the specified migration that was run

$ knex migrate:down 001_migration_name.js
+

To list both completed and pending migrations:

$ knex migrate:list
+

Seed files

Seed files allow you to populate your database with test or seed data independent of your migration files.

Seed CLI

To create a seed file, run:

$ knex seed:make seed_name
+

Seed files are created in the directory specified in your knexfile.js for the current environment. A sample seed configuration looks like:

module.exports = {
+  // ...
+  development: {
+    client: {/* ... */},
+    connection: {/* ... */},
+    seeds: {
+        directory: './seeds/dev'
+    }
+  }
+  // ...
+  }
+

If no seeds.directory is defined, files are created in ./seeds. Note that the seed directory needs to be a relative path. Absolute paths are not supported (nor is it good practice).

To run seed files, execute:

$ knex seed:run
+

Seed files are executed in alphabetical order. Unlike migrations, every seed file will be executed when you run the command. You should design your seed files to reset tables as needed before inserting data.

To run specific seed files, execute:

$ knex seed:run --specific=seed-filename.js --specific=another-seed-filename.js
+

knexfile.js

A knexfile.js generally contains all of the configuration for your database. It can optionally provide different configuration for different environments. You may pass a --knexfile option to any of the command line statements to specify an alternate path to your knexfile.

Basic configuration

module.exports = {
+  client: 'pg',
+  connection: process.env.DATABASE_URL || { 
+    user: 'me', 
+    database: 'my_app' 
+  }
+};
+

You can also use an async function to get connection details for your configuration. This is useful when you need to fetch credentials from a secure location like vault.

const getPassword = async () => {
+  // TODO: implement me
+  return 'my_pass'
+}
+
+module.exports = {
+  client: 'pg',
+  connection: async () => {
+    const password = await getPassword()
+    return { user: 'me', password }
+  },
+  migrations: {}
+};
+

Environment configuration

module.exports = {
+  development: {
+    client: 'pg',
+    connection: { user: 'me', database: 'my_app' }
+  },
+  production: { 
+    client: 'pg', 
+    connection: process.env.DATABASE_URL 
+  }
+};
+

Custom migration

You may provide a custom migration stub to be used in place of the default option.

module.exports = {
+  client: 'pg',
+  migrations: {
+    stub: 'migration.stub'
+  }
+};
+

Custom migration name

You may provide a custom migration name to be used in place of the default option.

module.exports = {
+  client: 'pg',
+  migrations: {
+    getNewMigrationName: (name) => {
+      return `${+new Date()}-${name}.js`;
+    }
+  }
+};
+

Generated migration extension

You can control extension of generated migrations.

module.exports = {
+  client: 'pg',
+  migrations: {
+    extension: 'ts'
+  }
+};
+

Knexfile in other languages

Knex uses Liftoff to support knexfile written in other compile-to-js languages.

Depending on the language, this may require you to install additional dependencies. The complete list of dependencies for each supported language can be found here.

Most common cases are typescript (for which typescript and ts-node packages are recommended), and coffeescript (for which coffeescript dependency is required).

If you don't specify the extension explicitly, the extension of generated migrations/seed files will be inferred from the knexfile extension

Migration API

knex.migrate is the class utilized by the knex migrations cli.

Each method takes an optional config object, which may specify the following properties:

  • directory: a relative path to the directory containing the migration files. Can be an array of paths (default ./migrations)
  • extension: the file extension used for the generated migration files (default js)
  • tableName: the table name used for storing the migration state (default knex_migrations)
  • schemaName: the schema name used for storing the table with migration state (optional parameter, only works on DBs that support multiple schemas in a single DB, such as PostgreSQL)
  • disableTransactions: don't run migrations inside transactions (default false)
  • disableMigrationsListValidation: do not validate that all the already executed migrations are still present in migration directories (default false)
  • sortDirsSeparately: if true and multiple directories are specified, all migrations from a single directory will be executed before executing migrations in the next folder (default false)
  • loadExtensions: array of file extensions which knex will treat as migrations. For example, if you have typescript transpiled into javascript in the same folder, you want to execute only javascript migrations. In this case, set loadExtensions to ['.js'] (Notice the dot!) (default ['.co', '.coffee', '.eg', '.iced', '.js', '.litcoffee', '.ls', '.ts'])
  • migrationSource: specify a custom migration source, see Custom Migration Source for more info (default filesystem)

Transactions in migrations

By default, each migration is run inside a transaction. Whenever needed, one can disable transactions for all migrations via the common migration config option config.disableTransactions or per-migration, via exposing a boolean property config.transaction from a migration file:

exports.up = function(knex) {
+  return knex.schema
+    .createTable('users', function (table) {
+        table.increments('id');
+        table.string('first_name', 255).notNullable();
+        table.string('last_name', 255).notNullable();
+    })
+    .createTable('products', function (table) {
+        table.increments('id');
+        table.decimal('price').notNullable();
+        table.string('name', 1000).notNullable();
+    });
+};
+
+exports.down = function(knex) {
+  return knex.schema
+      .dropTable("products")
+      .dropTable("users");
+};
+
+exports.config = { transaction: false };
+

The same config property can be used for enabling transaction per-migration in case the common configuration has disableTransactions: true.

make

knex.migrate.make(name, [config])

Creates a new migration, with the name of the migration being added.

latest

knex.migrate.latest([config])

Runs all migrations that have not yet been run.

If you need to run something only after all migrations have finished their execution, you can do something like this:

knex.migrate.latest()
+  .then(function() {
+    return knex.seed.run();
+  })
+  .then(function() {
+    // migrations are finished
+  });
+

rollback

knex.migrate.rollback([config], [all])

Rolls back the latest migration group. If the all parameter is truthy, all applied migrations will be rolled back instead of just the last batch. The default value for this parameter is false.

up

knex.migrate.up([config])

Runs the specified (by config.name parameter) or the next chronological migration that has not yet be run.

down

knex.migrate.down([config])

Will undo the specified (by config.name parameter) or the last migration that was run.

currentVersion

knex.migrate.currentVersion([config])

Retrieves and returns the current migration version, as a promise. If there aren't any migrations run yet, returns "none" as the value for the currentVersion.

list

knex.migrate.list([config])

Will return list of completed and pending migrations

unlock

knex.migrate.forceFreeMigrationsLock([config])

Forcibly unlocks the migrations lock table, and ensures that there is only one row in it.

Notes about locks

A lock system is there to prevent multiple processes from running the same migration batch in the same time. When a batch of migrations is about to be run, the migration system first tries to get a lock using a SELECT ... FOR UPDATE statement (preventing race conditions from happening). If it can get a lock, the migration batch will run. If it can't, it will wait until the lock is released.

Please note that if your process unfortunately crashes, the lock will have to be manually removed with knex migrate:unlock in order to let migrations run again.

The locks are saved in a table called "tableName_lock"; it has a column called is_locked that knex migrate:unlock sets to 0 in order to release the lock. The index column in the lock table exists for compatibility with some database clusters that require a primary key, but is otherwise unused. There must be only one row in this table, or an error will be thrown when running migrations: "Migration table is already locked". Run knex migrate:unlock to ensure that there is only one row in the table.

Custom migration sources

Knex supports custom migration sources, allowing you full control of where your migrations come from. This can be useful for custom folder structures, when bundling with webpack/browserify and other scenarios.

// Create a custom migration source class
+class MyMigrationSource {
+  // Must return a Promise containing a list of migrations. 
+  // Migrations can be whatever you want, 
+  // they will be passed as arguments to getMigrationName 
+  // and getMigration
+  getMigrations() {
+    // In this example we are just returning migration names
+    return Promise.resolve(['migration1'])
+  }
+
+  getMigrationName(migration) {
+    return migration;
+  }
+
+  getMigration(migration) {
+    switch(migration) {
+      case 'migration1':
+        return {
+          up(knex)   { /* ... */ },
+          down(knex) { /* ... */ },
+        }
+    }
+  }
+}
+
+// pass an instance of your migration source as knex config
+knex.migrate.latest({ 
+  migrationSource: new MyMigrationSource() 
+})
+

Webpack migration source example

An example of how to create a migration source where migrations are included in a webpack bundle.

const path = require('path')
+
+class WebpackMigrationSource {
+  constructor(migrationContext) {
+    this.migrationContext = migrationContext
+  }
+
+  getMigrations() {
+    return Promise.resolve(
+      this.migrationContext.keys().sort()
+    )
+  }
+
+  getMigrationName(migration) {
+    return path.parse(migration).base
+  }
+
+  getMigration(migration) {
+    return this.migrationContext(migration)
+  }
+}
+
+// pass an instance of your migration source as knex config
+knex.migrate.latest({
+  migrationSource: new WebpackMigrationSource(
+    require.context('./migrations', false, /.js$/)
+  )
+})
+
+// with webpack >=5, require.context will add 
+// both the relative and absolute paths to the context
+// to avoid duplicate migration errors, you'll need 
+// to filter out one or the other this example filters 
+// out absolute paths, leaving only the relative 
+// ones(./migrations/*.js):
+knex.migrate.latest({
+  migrationSource: new WebpackMigrationSource(
+    require.context('./migrations', false, /^\.\/.*\.js$/)
+  )
+})
+

ECMAScript modules (ESM) Interoperability

ECMAScript Module support for knex CLI's configuration, migration and seeds
enabled by the --esm flag, ECMAScript Interoperability is provided by the 'esm' module.
You can find here more information about 'esm' superpowers.

Node 'mjs' files are handled by NodeJS own import mechanics
and do not require the use of the '--esm' flag.
But you might need it anyway for Node v10 under certain scenarios.
You can find details about NodeJS ECMAScript modules here

While it is possible to mix and match different module formats (extensions)
between your knexfile, seeds and migrations,
some format combinations will require specific NodeJS versions,
Notably mjs/cjs files will follow NodeJS import and require restrictions.
You can see here many possible scenarios,
and here some sample configurations

Node v10.* require the use of the '--experimental-module' flag in order to use the 'mjs' or 'cjs' extension.

# launching knex on Node v10 to use mjs/cjs modules
+node --experimental-modules ./node_modules/.bin/knex $@
+

When using migration and seed files with '.cjs' or '.mjs' extensions, you will need to specify that explicitly:

/** 
+ * knexfile.mjs
+ */
+export default {      
+  migrations: {
+    // ... client, connection,etc .... 
+    directory: './migrations',
+    loadExtensions: ['.mjs'] // 
+  }
+}
+

When using '.mjs' extensions for your knexfile and '.js' for the seeds/migrations, you will need to specify that explicitly.

/** 
+ * knexfile.mjs
+ */
+export default {      
+  migrations: {
+    // ... client, connection,etc .... 
+    directory: './migrations',
+    loadExtensions: ['.js'] // knex will search for 'mjs' file by default
+  }
+}
+

For the knexfile you can use a default export,
it will take precedence over named export.

/**
+ * filename: knexfile.js
+ * For the knexfile you can use a default export
+ **/        
+export default {
+  client: 'sqlite3',
+  connection: {
+    filename: '../test.sqlite3',
+  },
+  migrations: {
+    directory: './migrations',
+  },
+  seeds: {
+    directory: './seeds',
+  },
+}
+
+/**
+ * filename: knexfile.js
+ * Let knex find the configuration by providing named exports,
+ * but if exported a default, it will take precedence, and it will be used instead
+ **/
+const config = {
+  client: 'sqlite3',
+  connection: {
+    filename: '../test.sqlite3',
+  },
+  migrations: {
+    directory: './migrations',
+  },
+  seeds: {
+    directory: './seeds',
+  },
+};
+/** this will be used, it has precedence over named export */
+export default config;
+/** Named exports, will be used if you didn't provide a default export */
+export const { client, connection, migrations, seeds } = config;
+

Seed and migration files need to follow Knex conventions

// file: seed.js
+/** 
+ * Same as with the CommonJS modules
+ * You will need to export a "seed" named function.
+ * */
+export function seed(knex) {
+  // ... seed logic here
+}
+
+// file: migration.js
+/** 
+ * Same as the CommonJS version, the miration file should export 
+ * "up" and "down" named functions
+ */
+export function up(knex) {
+  // ... migration logic here
+}
+export function down(knex) {
+// ... migration logic here
+}
+

Seed API

knex.seed is the class utilized by the knex seed CLI.

Each method takes an optional config object, which may specify the following properties:

  • directory: a relative path to the directory containing the seed files. Can be an array of paths (default ./seeds)
  • loadExtensions: array of file extensions which knex will treat as seeds. For example, if you have typescript transpiled into javascript in the same folder, you want to execute only javascript seeds. In this case, set loadExtensions to ['.js'] (Notice the dot!) (default ['.co', '.coffee', '.eg', '.iced', '.js', '.litcoffee', '.ls', '.ts'])
  • recursive: if true, will find seed files recursively in the directory / directories specified
  • specific: a specific seed file or an array of seed files to run from the seeds directory, if its value is undefined it will run all the seeds (default undefined). If an array is specified, seed files will be run in the same order as the array
  • sortDirsSeparately: if true and multiple directories are specified, all seeds from a single directory will be executed before executing seeds in the next folder (default false)
  • seedSource: specify a custom seed source, see Custom Seed Source for more info (default filesystem)
  • extension: extension to be used for newly generated seeds (default js)
  • timestampFilenamePrefix: whether timestamp should be added as a prefix for newly generated seeds (default false)

make

knex.seed.make(name, [config])

Creates a new seed file, with the name of the seed file being added. If the seed directory config is an array of paths, the seed file will be generated in the latest specified.

run

knex.seed.run([config])

Runs all seed files for the current environment.

Custom seed sources

Knex supports custom seed sources, allowing you full control of where your seeds come from. This can be useful for custom folder structures, when bundling with webpack/browserify and other scenarios.

// Create a custom seed source class
+class MySeedSource {
+  // Must return a Promise containing a list of seeds. 
+  // Seeds can be whatever you want, they will be passed as
+  // arguments to getSeed
+  getSeeds() {
+    // In this example we are just returning seed names
+    return Promise.resolve(['seed1'])
+  }
+
+  getSeed(seed) {
+    switch(seed) {
+      case 'seed1':
+        return (knex) => { /* ... */ }
+    }
+  }
+}
+
+// pass an instance of your seed source as knex config
+knex.seed.run({ seedSource: new MySeedSource() })
+
+ + + + + \ No newline at end of file diff --git a/guide/query-builder.html b/guide/query-builder.html new file mode 100644 index 00000000..eef2e5ec --- /dev/null +++ b/guide/query-builder.html @@ -0,0 +1,1435 @@ + + + + + + Knex Query Builder | Knex.js + + + + + + + + + + + + +

Knex Query Builder

The heart of the library, the knex query builder is the interface used for building and executing standard SQL queries, such as select, insert, update, delete.

Identifier Syntax

In many places in APIs identifiers like table name or column name can be passed to methods.

Most commonly one needs just plain tableName.columnName, tableName or columnName, but in many cases one also needs to pass an alias how that identifier is referred later on in the query.

There are two ways to declare an alias for identifier. One can directly give as aliasName suffix for the identifier (e.g. identifierName as aliasName) or one can pass an object { aliasName: 'identifierName' }.

If the object has multiple aliases { alias1: 'identifier1', alias2: 'identifier2' }, then all the aliased identifiers are expanded to comma separated list.

INFO

Identifier syntax has no place for selecting schema, so if you are doing schemaName.tableName, query might be rendered wrong. Use .withSchema('schemaName') instead.

knex({ a: 'table', b: 'table' })
+  .select({
+    aTitle: 'a.title',
+    bTitle: 'b.title'
+  })
+  .whereRaw('?? = ??', ['a.column_1', 'b.column_2'])
+

Common

knex

knex(tableName, options={only: boolean})
knex.[methodName]

The query builder starts off either by specifying a tableName you wish to query against, or by calling any method directly on the knex object. This kicks off a jQuery-like chain, with which you can call additional query builder methods as needed to construct the query, eventually calling any of the interface methods, to either convert toString, or execute the query with a promise, callback, or stream. Optional second argument for passing options:* only: if true, the ONLY keyword is used before the tableName to discard inheriting tables' data.

WARNING

Only supported in PostgreSQL for now.

Usage with TypeScript

If using TypeScript, you can pass the type of database row as a type parameter to get better autocompletion support down the chain.

interface User {
+  id: number;
+  name: string;
+  age: number;
+}
+
+knex('users')
+  .where('id')
+  .first(); // Resolves to any
+
+knex<User>('users') // User is the type of row in database
+  .where('id', 1) // Your IDE will be able to help with the completion of id
+  .first(); // Resolves to User | undefined
+

It is also possible to take advantage of auto-completion support (in TypeScript-aware IDEs) with generic type params when writing code in plain JavaScript through JSDoc comments.

/**
+ * @typedef {Object} User
+ * @property {number} id
+ * @property {number} age
+ * @property {string} name
+ *
+ * @returns {Knex.QueryBuilder<User, {}>}
+ */
+const Users = () => knex('Users')
+
+// 'id' property can be autocompleted by editor
+Users().where('id', 1) 
+
Caveat with type inference and mutable fluent APIs

Most of the knex APIs mutate current object and return it. This pattern does not work well with type-inference.

knex<User>('users')
+  .select('id')
+  .then((users) => { // Type of users is inferred as Pick<User, "id">[]
+    // Do something with users
+  });
+
+knex<User>('users')
+  .select('id')
+  .select('age')
+  .then((users) => { // Type of users is inferred as Pick<User, "id" | "age">[]
+    // Do something with users
+  });
+
+// The type of usersQueryBuilder is determined here
+const usersQueryBuilder = knex<User>('users').select('id');
+
+if (someCondition) {
+  // This select will not change the type of usersQueryBuilder
+  // We can not change the type of a pre-declared variable in TypeScript
+  usersQueryBuilder.select('age');
+}
+usersQueryBuilder.then((users) => {
+  // Type of users here will be Pick<User, "id">[]
+  // which may not be what you expect.
+});
+
+// You can specify the type of result explicitly through a second type parameter:
+const queryBuilder = knex<User, Pick<User, "id" | "age">>('users');
+
+// But there is no type constraint to ensure that these properties have actually been
+// selected.
+
+// So, this will compile:
+queryBuilder.select('name').then((users) => {
+  // Type of users is Pick<User, "id"> but it will only have name
+})
+

If you don't want to manually specify the result type, it is recommended to always use the type of last value of the chain and assign result of any future chain continuation to a separate variable (which will have a different type).

timeout

.timeout(ms, options={cancel: boolean})

Sets a timeout for the query and will throw a TimeoutError if the timeout is exceeded. The error contains information about the query, bindings, and the timeout that was set. Useful for complex queries that you want to make sure are not taking too long to execute. Optional second argument for passing options:* cancel: if true, cancel query if timeout is reached.

WARNING

Only supported in MySQL and PostgreSQL for now.

knex.select()
+  .from('books')
+  .timeout(1000)
+
+knex.select()
+  .from('books')
+  .timeout(1000, { 
+    cancel: true // MySQL and PostgreSQL only
+  }) 
+

select

.select([*columns])

Creates a select query, taking an optional array of columns for the query, eventually defaulting to * if none are specified when the query is built. The response of a select call will resolve with an array of objects selected from the database.

knex.select('title', 'author', 'year')
+  .from('books')
+
+knex.select()
+  .table('books')
+
Usage with TypeScript

We are generally able to infer the result type based on the columns being selected as long as the select arguments match exactly the key names in record type. However, aliasing and scoping can get in the way of inference.

knex.select('id')
+  .from<User>('users'); // Resolves to Pick<User, "id">[]
+
+knex.select('users.id')
+  .from<User>('users'); // Resolves to any[]
+// ^ TypeScript doesn't provide us a way to look into a string and infer the type
+//   from a substring, so we fall back to any
+
+// We can side-step this using knex.ref:
+knex.select(knex.ref('id').withSchema('users'))
+  .from<User>('users'); // Resolves to Pick<User, "id">[]
+
+knex.select('id as identifier')
+  .from<User>('users'); // Resolves to any[], for same reason as above
+
+// Refs are handy here too:
+knex.select(knex.ref('id').as('identifier'))
+  .from<User>('users'); // Resolves to { identifier: number; }[]
+

as

.as(name)

Allows for aliasing a subquery, taking the string you wish to name the current query. If the query is not a sub-query, it will be ignored.

knex.avg('sum_column1')
+  .from(function() {
+    this.sum('column1 as sum_column1')
+      .from('t1')
+      .groupBy('column1')
+      .as('t1')
+  })
+  .as('ignored_alias')
+

column

.column(columns)

Specifically set the columns to be selected on a select query, taking an array, an object or a list of column names. Passing an object will automatically alias the columns with the given keys.

knex.column('title', 'author', 'year')
+  .select()
+  .from('books')
+
+knex.column(['title', 'author', 'year'])
+  .select()
+  .from('books')
+
+knex.column('title', { by: 'author' }, 'year')
+  .select()
+  .from('books')
+

from

.from([tableName], options={only: boolean})

Specifies the table used in the current query, replacing the current table name if one has already been specified. This is typically used in the sub-queries performed in the advanced where or union methods. Optional second argument for passing options:* only: if true, the ONLY keyword is used before the tableName to discard inheriting tables' data.

WARNING

Only supported in PostgreSQL for now.

knex.select('*')
+  .from('users')
+

Usage with TypeScript

We can specify the type of database row through the TRecord type parameter

knex.select('id')
+  .from('users'); // Resolves to any[]
+
+knex.select('id')
+  .from<User>('users'); // Results to Pick<User, "id">[]
+

fromRaw

.fromRaw(sql, [bindings])

knex.select('*')
+  .fromRaw('(select * from "users" where "age" > ?)', '18')
+

with

.with(alias, [columns], callback|builder|raw)

Add a "with" clause to the query. "With" clauses are supported by PostgreSQL, Oracle, SQLite3 and MSSQL. An optional column list can be provided after the alias; if provided, it must include at least one column name.

knex
+  .with(
+    'with_alias', 
+    knex.raw(
+      'select * from "books" where "author" = ?', 
+      'Test'
+    )
+  )
+  .select('*')
+  .from('with_alias')
+
+knex
+  .with(
+    'with_alias', 
+    ["title"], 
+    knex.raw(
+      'select "title" from "books" where "author" = ?', 
+      'Test'
+    )
+  )
+  .select('*')
+  .from('with_alias')
+
+knex
+  .with('with_alias', (qb) => {
+    qb.select('*')
+      .from('books')
+      .where('author', 'Test')
+  })
+  .select('*')
+  .from('with_alias')
+

withRecursive

.withRecursive(alias, [columns], callback|builder|raw)

Identical to the with method except "recursive" is appended to "with" (or not, as required by the target database) to make self-referential CTEs possible. Note that some databases, such as Oracle, require a column list be provided when using an rCTE.

knex
+  .withRecursive('ancestors', (qb) => {
+    qb.select('*')
+      .from('people')
+      .where('people.id', 1)
+      .union((qb) => {
+        qb.select('*')
+          .from('people')
+          .join(
+            'ancestors', 
+            'ancestors.parentId', 
+            'people.id'
+          )
+      })
+  })
+  .select('*')
+  .from('ancestors')
+
+knex
+  .withRecursive('family', ['name', 'parentName'], (qb) => {
+    qb.select('name', 'parentName')
+      .from('folks')
+      .where({ name: 'grandchild' })
+      .unionAll((qb) =>
+        qb
+          .select('folks.name', 'folks.parentName')
+          .from('folks')
+          .join(
+            'family',
+            knex.ref('family.parentName'),
+            knex.ref('folks.name')
+          )
+      )
+  })
+  .select('name')
+  .from('family')
+

withMaterialized

.withMaterialized(alias, [columns], callback|builder|raw)

Add a "with" materialized clause to the query. "With" materialized clauses are supported by PostgreSQL and SQLite3. An optional column list can be provided after the alias; if provided, it must include at least one column name.

knex
+  .withMaterialized(
+    'with_alias', 
+    knex.raw(
+      'select * from "books" where "author" = ?', 
+      'Test'
+    )
+  )
+  .select('*')
+  .from('with_alias')
+
+knex
+  .withMaterialized(
+    'with_alias', 
+    ["title"], 
+    knex.raw(
+      'select "title" from "books" where "author" = ?', 
+      'Test'
+    )
+  )
+  .select('*')
+  .from('with_alias')
+
+knex
+  .withMaterialized('with_alias', (qb) => {
+    qb.select('*')
+      .from('books')
+      .where('author', 'Test')
+  })
+  .select('*')
+  .from('with_alias')
+

withNotMaterialized

.withNotMaterialized(alias, [columns], callback|builder|raw)

Add a "with" not materialized clause to the query. "With" not materialized clauses are supported by PostgreSQL and SQLite3. An optional column list can be provided after the alias; if provided, it must include at least one column name.

knex
+  .withNotMaterialized(
+    'with_alias', 
+    knex.raw(
+      'select * from "books" where "author" = ?', 
+      'Test'
+    )
+  )
+  .select('*')
+  .from('with_alias')
+
+knex
+  .withNotMaterialized(
+    'with_alias', 
+    ["title"], 
+    knex.raw(
+      'select "title" from "books" where "author" = ?', 
+      'Test'
+    )
+  )
+  .select('*')
+  .from('with_alias')
+
+knex
+  .withNotMaterialized('with_alias', (qb) => {
+    qb.select('*')
+      .from('books')
+      .where('author', 'Test')
+  })
+  .select('*')
+  .from('with_alias')
+

withSchema

.withSchema([schemaName])

Specifies the schema to be used as prefix of table name.

knex.withSchema('public')
+  .select('*')
+  .from('users')
+

jsonExtract

.jsonExtract(column|builder|raw|array[], path, [alias], [singleValue])

Extract a value from a json column given a JsonPath. An alias can be specified. The singleValue boolean can be used to specify, with Oracle or MSSQL, if the value returned by the function is a single value or an array/object value. An array of arrays can be used to specify multiple extractions with one call to this function.

knex('accounts')
+  .jsonExtract('json_col', '$.name')
+
+knex('accounts')
+  .jsonExtract('json_col', '$.name', 'accountName')
+
+knex('accounts')
+  .jsonExtract('json_col', '$.name', 'accountName', true)
+
+knex('accounts')
+  .jsonExtract([ 
+    ['json_col', '$.name', 'accountName'], 
+    ['json_col', '$.lastName', 'accountLastName'] 
+  ])
+

All json*() functions can be used directly from knex object and can be nested.

knex('cities')
+  .jsonExtract([
+    [
+      knex.jsonRemove('population', '$.min'), 
+      '$', 
+      'withoutMin'
+    ],
+    [
+      knex.jsonRemove('population', '$.max'), 
+      '$', 
+      'withoutMax'
+    ],
+    [
+      knex.jsonSet('population', '$.current', '1234'),
+      '$',
+      'currentModified',
+    ]
+  ])
+

jsonSet

.jsonSet(column|builder|raw, path, value, [alias])

Return a json value/object/array where a given value is set at the given JsonPath. Value can be single value or json object. If a value already exists at the given place, the value is replaced. Not supported by Redshift and versions before Oracle 21c.

knex('accounts')
+  .jsonSet('json_col', '$.name', 'newName', 'newNameCol')
+
+knex('accounts')
+  .jsonSet(
+    'json_col', 
+    '$.name', 
+    { "name": "newName" }, 
+    'newNameCol'
+  )
+

jsonInsert

.jsonInsert(column|builder|raw, path, value, [alias])

Return a json value/object/array where a given value is inserted at the given JsonPath. Value can be single value or json object. If a value exists at the given path, the value is not replaced. Not supported by Redshift and versions before Oracle 21c.

knex('accounts')
+  .jsonInsert('json_col', '$.name', 'newName', 'newNameCol')
+
+knex('accounts')
+  .jsonInsert(
+    'json_col', 
+    '$.name', 
+    { "name": "newName" }, 
+    'newNameCol'
+  )
+
+knex('accounts')
+  .jsonInsert(
+    knex.jsonExtract('json_col', '$.otherAccount'), 
+    '$.name', 
+    { "name": "newName" }, 
+    'newNameCol'
+  )
+

jsonRemove

.jsonRemove(column|builder|raw, path, [alias])

Return a json value/object/array where a given value is removed at the given JsonPath. Not supported by Redshift and versions before Oracle 21c.

knex('accounts')
+  .jsonRemove('json_col', '$.name', 'colWithRemove')
+
+knex('accounts')
+  .jsonInsert(
+    'json_col', 
+    '$.name', 
+    { "name": "newName" }, 
+    'newNameCol'
+  )
+

offset

.offset(value, options={skipBinding: boolean})

Adds an offset clause to the query. An optional skipBinding parameter may be specified which would avoid setting offset as a prepared value (some databases don't allow prepared values for offset).

knex.select('*')
+  .from('users')
+  .offset(10)
+
+knex.select('*')
+  .from('users')
+  .offset(10)
+  .toSQL()
+  .sql
+
+// Offset value isn't a prepared value.
+knex.select('*')
+  .from('users')
+  .offset(10, {skipBinding: true})
+  .toSQL()
+  .sql
+

limit

.limit(value, options={skipBinding: boolean})

Adds a limit clause to the query. An optional skipBinding parameter may be specified to avoid adding limit as a prepared value (some databases don't allow prepared values for limit).

knex.select('*')
+  .from('users')
+  .limit(10)
+  .offset(30)
+
+knex.select('*')
+  .from('users')
+  .limit(10)
+  .offset(30)
+  .toSQL()
+  .sql
+
+// Limit value isn't a prepared value.
+knex.select('*')
+  .from('users')
+  .limit(10, {skipBinding: true})
+  .offset(30)
+  .toSQL()
+  .sql
+

union

.union([*queries], [wrap])

Creates a union query, taking an array or a list of callbacks, builders, or raw statements to build the union statement, with optional boolean wrap. If the wrap parameter is true, the queries will be individually wrapped in parentheses.

knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .union(function() {
+    this.select('*')
+      .from('users')
+      .whereNull('first_name')
+  })
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .union([
+    knex.select('*')
+      .from('users')
+      .whereNull('first_name')
+  ])
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .union(
+    knex.raw(
+      'select * from users where first_name is null'
+    ),
+    knex.raw(
+      'select * from users where email is null'
+    )
+  )
+

If you want to apply orderBy, groupBy, limit, offset or having to inputs of the union you need to use knex.union as a base statement. If you don't do this, those clauses will get appended to the end of the union.

// example showing how clauses get appended to the end of the query
+knex('users')
+  .select('id', 'name')
+  .groupBy('id')
+  .union(
+    knex('invitations')
+      .select('id', 'name')
+      .orderBy('expires_at')
+  )
+
+knex.union([
+  knex('users').select('id', 'name').groupBy('id'),
+  knex('invitations').select('id', 'name').orderBy('expires_at')
+])
+

before and after

unionAll

.unionAll([*queries], [wrap])

Creates a union all query, with the same method signature as the union method. If the wrap parameter is true, the queries will be individually wrapped in parentheses.

knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .unionAll(function() {
+    this.select('*').from('users').whereNull('first_name');
+  })
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .unionAll([
+    knex.select('*').from('users').whereNull('first_name')
+  ])
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .unionAll(
+    knex.raw(
+      'select * from users where first_name is null'
+    ),
+    knex.raw(
+      'select * from users where email is null'
+    )
+  )
+

intersect

.intersect([*queries], [wrap])

Creates an intersect query, taking an array or a list of callbacks, builders, or raw statements to build the intersect statement, with optional boolean wrap. If the wrap parameter is true, the queries will be individually wrapped in parentheses. The intersect method is unsupported on MySQL.

knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .intersect(function() {
+    this.select('*').from('users').whereNull('first_name')
+  })
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .intersect([
+    knex.select('*').from('users').whereNull('first_name')
+  ])
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .intersect(
+    knex.raw(
+      'select * from users where first_name is null'
+    ),
+    knex.raw(
+      'select * from users where email is null'
+    )
+  )
+

except

.except([*queries], [wrap])

Creates an except query, taking an array or a list of callbacks, builders, or raw statements to build the except statement, with optional boolean wrap. If the wrap parameter is true, the queries will be individually wrapped in parentheses. The except method is unsupported on MySQL.

knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .except(function() {
+    this.select('*').from('users').whereNull('first_name')
+  })
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .except([
+    knex.select('*').from('users').whereNull('first_name')
+  ])
+
+knex.select('*')
+  .from('users')
+  .whereNull('last_name')
+  .except(
+    knex.raw(
+      'select * from users where first_name is null'
+    ),
+    knex.raw(
+      'select * from users where email is null'
+    )
+  )
+

insert

.insert(data, [returning], [options])

Creates an insert query, taking either a hash of properties to be inserted into the row, or an array of inserts, to be executed as a single insert command. If returning array is passed e.g. ['id', 'title'], it resolves the promise / fulfills the callback with an array of all the added rows with specified columns. It's a shortcut for returning method

// Returns [1] in "mysql", "sqlite", "oracle"; 
+// [] in "postgresql" 
+// unless the 'returning' parameter is set.
+knex('books').insert({title: 'Slaughterhouse Five'})
+
+// Normalizes for empty keys on multi-row insert:
+knex('coords').insert([{x: 20}, {y: 30},  {x: 10, y: 20}])
+
+// Returns [2] in "mysql", "sqlite"; [2, 3] in "postgresql"
+knex
+  .insert(
+    [
+      { title: 'Great Gatsby' }, 
+      { title: 'Fahrenheit 451' }
+    ], 
+    ['id']
+  )
+  .into('books')
+

For MSSQL, triggers on tables can interrupt returning a valid value from the standard insert statements. You can add the includeTriggerModifications option to get around this issue. This modifies the SQL so the proper values can be returned. This only modifies the statement if you are using MSSQL, a returning value is specified, and the includeTriggerModifications option is set.

// Adding the option includeTriggerModifications 
+// allows you to run statements on tables 
+// that contain triggers. Only affects MSSQL.
+knex('books')
+  .insert(
+    {title: 'Alice in Wonderland'}, 
+    ['id'], 
+    { includeTriggerModifications: true }
+  )
+

If one prefers that undefined keys are replaced with NULL instead of DEFAULT one may give useNullAsDefault configuration parameter in knex config.

const knex = require('knex')({
+  client: 'mysql',
+  connection: {
+    host : '127.0.0.1',
+    port : 3306,
+    user : 'your_database_user',
+    password : 'your_database_password',
+    database : 'myapp_test'
+  },
+  useNullAsDefault: true
+});
+
+knex('coords').insert([{x: 20}, {y: 30}, {x: 10, y: 20}])
+
insert into `coords` (`x`, `y`) values (20, NULL), (NULL, 30), (10, 20)"
+

onConflict

insert(..).onConflict(column)
insert(..).onConflict([column1, column2, ...])
insert(..).onConflict(knex.raw(...))

Implemented for the PostgreSQL, MySQL, and SQLite databases. A modifier for insert queries that specifies alternative behaviour in the case of a conflict. A conflict occurs when a table has a PRIMARY KEY or a UNIQUE index on a column (or a composite index on a set of columns) and a row being inserted has the same value as a row which already exists in the table in those column(s). The default behaviour in case of conflict is to raise an error and abort the query. Using this method you can change this behaviour to either silently ignore the error by using .onConflict().ignore() or to update the existing row with new data (perform an "UPSERT") by using .onConflict().merge().

INFO

For PostgreSQL and SQLite, the column(s) specified by this method must either be the table's PRIMARY KEY or have a UNIQUE index on them, or the query will fail to execute. When specifying multiple columns, they must be a composite PRIMARY KEY or have composite UNIQUE index. MySQL will ignore the specified columns and always use the table's PRIMARY KEY. For cross-platform support across PostgreSQL, MySQL, and SQLite you must both explicitly specify the columns in .onConflict() and those column(s) must be the table's PRIMARY KEY.

For PostgreSQL and SQLite, you can use knex.raw(...) function in onConflict. It can be useful to specify condition when you have partial index :

knex('tableName')
+  .insert({
+    email: "ignore@example.com",
+    name: "John Doe",
+    active: true
+  })
+    // ignore only on email conflict and active is true.
+  .onConflict(knex.raw('(email) where active'))
+  .ignore()
+

See documentation on .ignore() and .merge() methods for more details.

ignore

insert(..).onConflict(..).ignore()

Implemented for the PostgreSQL, MySQL, and SQLite databases. Modifies an insert query, and causes it to be silently dropped without an error if a conflict occurs. Uses INSERT IGNORE in MySQL, and adds an ON CONFLICT (columns) DO NOTHING clause to the insert statement in PostgreSQL and SQLite.

knex('tableName')
+  .insert({
+    email: "ignore@example.com",
+    name: "John Doe"
+  })
+  .onConflict('email')
+  .ignore()
+

merge

insert(..).onConflict(..).merge()
insert(..).onConflict(..).merge(updates)

Implemented for the PostgreSQL, MySQL, and SQLite databases. Modifies an insert query, to turn it into an 'upsert' operation. Uses ON DUPLICATE KEY UPDATE in MySQL, and adds an ON CONFLICT (columns) DO UPDATE clause to the insert statement in PostgreSQL and SQLite. By default, it merges all columns.

knex('tableName')
+  .insert({
+    email: "ignore@example.com",
+    name: "John Doe"
+  })
+  .onConflict('email')
+  .merge()
+

This also works with batch inserts:

knex('tableName')
+  .insert([
+    { email: "john@example.com", name: "John Doe" },
+    { email: "jane@example.com", name: "Jane Doe" },
+    { email: "alex@example.com", name: "Alex Doe" },
+  ])
+  .onConflict('email')
+  .merge()
+

It is also possible to specify a subset of the columns to merge when a conflict occurs. For example, you may want to set a 'created_at' column when inserting but would prefer not to update it if the row already exists:

const timestamp = Date.now();
+knex('tableName')
+  .insert({
+    email: "ignore@example.com",
+    name: "John Doe",
+    created_at: timestamp,
+    updated_at: timestamp,
+  })
+  .onConflict('email')
+  .merge(['email', 'name', 'updated_at'])
+

It is also possible to specify data to update separately from the data to insert. This is useful if you want to update with different data to the insert. For example, you may want to change a value if the row already exists:

const timestamp = Date.now();
+knex('tableName')
+  .insert({
+    email: "ignore@example.com",
+    name: "John Doe",
+    created_at: timestamp,
+    updated_at: timestamp,
+  })
+  .onConflict('email')
+  .merge({
+    name: "John Doe The Second",
+  })
+

For PostgreSQL/SQLite databases only, it is also possible to add a WHERE clause to conditionally update only the matching rows:

const timestamp = Date.now();
+knex('tableName')
+  .insert({
+    email: "ignore@example.com",
+    name: "John Doe",
+    created_at: timestamp,
+    updated_at: timestamp,
+  })
+  .onConflict('email')
+  .merge({
+    name: "John Doe",
+    updated_at: timestamp,
+  })
+  .where('updated_at', '<', timestamp)
+

upsert

.upsert(data, [returning], [options])

Implemented for the CockroachDB. Creates an upsert query, taking either a hash of properties to be inserted into the row, or an array of upserts, to be executed as a single upsert command. If returning array is passed e.g. ['id', 'title'], it resolves the promise / fulfills the callback with an array of all the added rows with specified columns. It's a shortcut for returning method

// insert new row with unique index on title column
+knex('books').upsert({title: 'Great Gatsby'})
+
+// update row by unique title 'Great Gatsby' 
+// and insert row with title 'Fahrenheit 451'
+knex('books').upsert([
+  {title: 'Great Gatsby'}, 
+  {title: 'Fahrenheit 451'}
+], ['id'])
+
+// Normalizes for empty keys on multi-row upsert, 
+// result sql: 
+// ("x", "y") values (20, default), (default, 30), (10, 20):
+knex('coords').upsert([{x: 20}, {y: 30}, {x: 10, y: 20}])
+

update

.update(data, [returning], [options]).update(key, value, [returning], [options])

Creates an update query, taking a hash of properties or a key/value pair to be updated based on the other query constraints. If returning array is passed e.g. ['id', 'title'], it resolves the promise / fulfills the callback with an array of all the updated rows with specified columns. It's a shortcut for returning method

knex('books')
+  .where('published_date', '<', 2000)
+  .update({
+    status: 'archived',
+    thisKeyIsSkipped: undefined
+  })
+
+// Returns [1] in "mysql", "sqlite", "oracle"; 
+// [] in "postgresql" 
+// unless the 'returning' parameter is set.
+knex('books').update('title', 'Slaughterhouse Five')
+
+/** Returns  
+ * [{ 
+ *   id: 42, 
+ *   title: "The Hitchhiker's Guide to the Galaxy" 
+ * }] **/
+knex('books')
+  .where({ id: 42 })
+  .update({ 
+    title: "The Hitchhiker's Guide to the Galaxy" 
+  }, ['id', 'title'])
+

For MSSQL, triggers on tables can interrupt returning a valid value from the standard update statements. You can add the includeTriggerModifications option to get around this issue. This modifies the SQL so the proper values can be returned. This only modifies the statement if you are using MSSQL, a returning value is specified, and the includeTriggerModifications option is set.

// Adding the option includeTriggerModifications allows you
+// to run statements on tables that contain triggers.
+// Only affects MSSQL.
+knex('books')
+  .update(
+    {title: 'Alice in Wonderland'}, 
+    ['id', 'title'], 
+    { includeTriggerModifications: true }
+  )
+

updateFrom

.updateFrom(tableName)

Can be used to define in PostgreSQL an update statement with explicit 'from' syntax which can be referenced in 'where' conditions.

knex('accounts')
+  .update({ enabled: false })
+  .updateFrom('clients')
+  .where('accounts.id', '=', 'clients.id')
+  .where('clients.active', '=', false)
+

del / delete

.del([returning], [options])

Aliased to del as delete is a reserved word in JavaScript, this method deletes one or more rows, based on other conditions specified in the query. Resolves the promise / fulfills the callback with the number of affected rows for the query.

knex('accounts')
+  .where('activated', false)
+  .del()
+

For MSSQL, triggers on tables can interrupt returning a valid value from the standard delete statements. You can add the includeTriggerModifications option to get around this issue. This modifies the SQL so the proper values can be returned. This only modifies the statement if you are using MSSQL, a returning value is specified, and the includeTriggerModifications option is set.

// Adding the option includeTriggerModifications allows you
+// to run statements on tables that contain triggers. 
+// Only affects MSSQL.
+knex('books')
+  .where('title', 'Alice in Wonderland')
+  .del(
+    ['id', 'title'], 
+    { includeTriggerModifications: true }
+  )
+

For PostgreSQL, Delete statement with joins is both supported with classic 'join' syntax and 'using' syntax.

knex('accounts')
+  .where('activated', false)
+  .join('accounts', 'accounts.id', 'users.account_id')
+  .del()
+

using

.using(tableName|tableNames)

Can be used to define in PostgreSQL a delete statement with joins with explicit 'using' syntax. Classic join syntax can be used too.

knex('accounts')
+  .where('activated', false)
+  .using('accounts')
+  .whereRaw('accounts.id = users.account_id')
+  .del()
+

returning

.returning(column, [options]).returning([column1, column2, ...], [options])

Utilized by PostgreSQL, MSSQL, SQLite, and Oracle databases, the returning method specifies which column should be returned by the insert, update and delete methods. Passed column parameter may be a string or an array of strings. The SQL result be reported as an array of objects, each containing a single property for each of the specified columns. The returning method is not supported on Amazon Redshift.

// Returns [ { id: 1 } ]
+knex('books')
+  .returning('id')
+  .insert({title: 'Slaughterhouse Five'})
+
+// Returns [{ id: 2 } ] in "mysql", "sqlite"; 
+// [ { id: 2 }, { id: 3 } ] in "postgresql"
+knex('books')
+  .returning('id')
+  .insert([
+    {title: 'Great Gatsby'}, 
+    {title: 'Fahrenheit 451'}
+  ])
+
+// Returns [ { id: 1, title: 'Slaughterhouse Five' } ]
+knex('books')
+  .returning(['id','title'])
+  .insert({title: 'Slaughterhouse Five'})
+

For MSSQL, triggers on tables can interrupt returning a valid value from the standard DML statements. You can add the includeTriggerModifications option to get around this issue. This modifies the SQL so the proper values can be returned. This only modifies the statement if you are using MSSQL, a returning value is specified, and the includeTriggerModifications option is set.

// Adding the option includeTriggerModifications allows you
+// to run statements on tables that contain triggers. 
+// Only affects MSSQL.
+knex('books')
+  .returning(
+    ['id','title'], 
+    { includeTriggerModifications: true }
+  )
+  .insert({title: 'Slaughterhouse Five'})
+

transacting

.transacting(transactionObj)

Used by knex.transaction, the transacting method may be chained to any query and passed the object you wish to join the query as part of the transaction for.

const Promise = require('bluebird');
+knex.transaction(function(trx) {
+  knex('books').transacting(trx).insert({name: 'Old Books'})
+    .then(function(resp) {
+      const id = resp[0];
+      return someExternalMethod(id, trx);
+    })
+    .then(trx.commit)
+    .catch(trx.rollback);
+})
+.then(function(resp) {
+  console.log('Transaction complete.');
+})
+.catch(function(err) {
+  console.error(err);
+});
+

forUpdate

.transacting(t).forUpdate()

Dynamically added after a transaction is specified, the forUpdate adds a FOR UPDATE in PostgreSQL and MySQL during a select statement. Not supported on Amazon Redshift due to lack of table locks.

knex('tableName')
+  .transacting(trx)
+  .forUpdate()
+  .select('*')
+

forShare

.transacting(t).forShare()

Dynamically added after a transaction is specified, the forShare adds a FOR SHARE in PostgreSQL and a LOCK IN SHARE MODE for MySQL during a select statement. Not supported on Amazon Redshift due to lack of table locks.

knex('tableName')
+  .transacting(trx)
+  .forShare()
+  .select('*')
+

forNoKeyUpdate

.transacting(t).forNoKeyUpdate()

Dynamically added after a transaction is specified, the forNoKeyUpdate adds a FOR NO KEY UPDATE in PostgreSQL.

knex('tableName')
+  .transacting(trx)
+  .forNoKeyUpdate()
+  .select('*')
+

forKeyShare

.transacting(t).forKeyShare()

Dynamically added after a transaction is specified, the forKeyShare adds a FOR KEY SHARE in PostgreSQL.

knex('tableName')
+  .transacting(trx)
+  .forKeyShare()
+  .select('*')
+

skipLocked

.skipLocked()

MySQL 8.0+, MariaDB-10.6+ and PostgreSQL 9.5+ only. This method can be used after a lock mode has been specified with either forUpdate or forShare, and will cause the query to skip any locked rows, returning an empty set if none are available.

knex('tableName')
+  .select('*')
+  .forUpdate()
+  .skipLocked()
+

noWait

.noWait()

MySQL 8.0+, MariaDB-10.3+ and PostgreSQL 9.5+ only. This method can be used after a lock mode has been specified with either forUpdate or forShare, and will cause the query to fail immediately if any selected rows are currently locked.

knex('tableName')
+  .select('*')
+  .forUpdate()
+  .noWait()
+

count

.count(column|columns|raw, [options])

Performs a count on the specified column or array of columns (note that some drivers do not support multiple columns). Also accepts raw expressions. The value returned from count (and other aggregation queries) is an array of objects like: [{'COUNT(*)': 1}]. The actual keys are dialect specific, so usually we would want to specify an alias (Refer examples below). Note that in Postgres, count returns a bigint type which will be a String and not a Number (more info).

knex('users').count('active')
+
+knex('users').count('active', {as: 'a'})
+
+knex('users').count('active as a')
+
+knex('users').count({ a: 'active' })
+
+knex('users').count({ a: 'active', v: 'valid' })
+
+knex('users').count('id', 'active')
+
+knex('users').count({ count: ['id', 'active'] })
+
+knex('users').count(knex.raw('??', ['active']))
+
Usage with TypeScript

The value of count will, by default, have type of string | number. This may be counter-intuitive but some connectors (eg. postgres) will automatically cast BigInt result to string when javascript's Number type is not large enough for the value.

    knex('users').count('age') // Resolves to: Record<string, number | string>
+    
+    knex('users').count({count: '*'}) // Resolves to { count?: string | number | undefined; }
+

Working with string | number can be inconvenient if you are not working with large tables. Two alternatives are available:

    // Be explicit about what you want as a result:
+    knex('users').count<Record<string, number>>('age');
+    
+    // Setup a one time declaration to make knex use number as result type for all
+    // count and countDistinct invocations (for any table)
+    declare module "knex/types/result" {
+        interface Registry {
+            Count: number;
+        }
+    }
+

Use countDistinct to add a distinct expression inside the aggregate function.

    knex('users').countDistinct('active')
+

min

.min(column|columns|raw, [options])

Gets the minimum value for the specified column or array of columns (note that some drivers do not support multiple columns). Also accepts raw expressions.

knex('users').min('age')
+
+knex('users').min('age', {as: 'a'})
+
+knex('users').min('age as a')
+
+knex('users').min({ a: 'age' })
+
+knex('users').min({ a: 'age', b: 'experience' })
+
+knex('users').min('age', 'logins')
+
+knex('users').min({ min: ['age', 'logins'] })
+
+knex('users').min(knex.raw('??', ['age']))
+

max

.max(column|columns|raw, [options])

Gets the maximum value for the specified column or array of columns (note that some drivers do not support multiple columns). Also accepts raw expressions.

knex('users').max('age')
+
+knex('users').max('age', {as: 'a'})
+
+knex('users').max('age as a')
+
+knex('users').max({ a: 'age' })
+
+knex('users').max('age', 'logins')
+
+knex('users').max({ max: ['age', 'logins'] })
+
+knex('users').max({ max: 'age', exp: 'experience' })
+
+knex('users').max(knex.raw('??', ['age']))
+

sum

.sum(column|columns|raw)

Retrieve the sum of the values of a given column or array of columns (note that some drivers do not support multiple columns). Also accepts raw expressions.

knex('users').sum('products')
+
+knex('users').sum('products as p')
+
+knex('users').sum({ p: 'products' })
+
+knex('users').sum('products', 'orders')
+
+knex('users').sum({ sum: ['products', 'orders'] })
+
+knex('users').sum(knex.raw('??', ['products']))
+

Use sumDistinct to add a distinct expression inside the aggregate function.

    knex('users').sumDistinct('products')
+

avg

.avg(column|columns|raw)

Retrieve the average of the values of a given column or array of columns (note that some drivers do not support multiple columns). Also accepts raw expressions.

knex('users').avg('age')
+
+knex('users').avg('age as a')
+
+knex('users').avg({ a: 'age' })
+
+knex('users').avg('age', 'logins')
+
+knex('users').avg({ avg: ['age', 'logins'] })
+
+knex('users').avg(knex.raw('??', ['age']))
+

Use avgDistinct to add a distinct expression inside the aggregate function.

    knex('users').avgDistinct('age')
+

increment

.increment(column, amount)

Increments a column value by the specified amount. Object syntax is supported for column.

knex('accounts')
+  .where('userid', '=', 1)
+  .increment('balance', 10)
+
+knex('accounts')
+  .where('id', '=', 1)
+  .increment({
+    balance: 10,
+    times: 1,
+  })
+

decrement

.decrement(column, amount)

Decrements a column value by the specified amount. Object syntax is supported for column.

knex('accounts')
+  .where('userid', '=', 1)
+  .decrement('balance', 5)
+
+knex('accounts')
+  .where('id', '=', 1)
+  .decrement({
+    balance: 50,
+  })
+

truncate

.truncate()

Truncates the current table.

knex('accounts').truncate()
+

pluck

.pluck(id)

This will pluck the specified column from each row in your results, yielding a promise which resolves to the array of values selected.

knex.table('users')
+  .pluck('id')
+  .then(function(ids) { console.log(ids); });
+

first

.first([columns])

Similar to select, but only retrieves & resolves with the first record from the query.

knex.table('users')
+  .first('id', 'name')
+  .then(function(row) { console.log(row); });
+

hintComment

.hintComment(hint|hints)

Add hints to the query using comment-like syntax /*+ ... */. MySQL and Oracle use this syntax for optimizer hints. Also various DB proxies and routers use this syntax to pass hints to alter their behavior. In other dialects the hints are ignored as simple comments.

knex('accounts')
+  .where('userid', '=', 1)
+  .hintComment('NO_ICP(accounts)')
+

comment

.comment(comment)

Prepend comment to the sql query using the syntax /* ... */. Some characters are forbidden such as /*, */ and ?.

knex('users')
+  .where('id', '=', 1)
+  .comment('Get user by id')
+

clone

.clone()

Clones the current query chain, useful for re-using partial query snippets in other queries without mutating the original.

denseRank

.denseRank(alias, ~mixed~)

Add a dense_rank() call to your query. For all the following queries, alias can be set to a falsy value if not needed.

String Syntax — .denseRank(alias, orderByClause, [partitionByClause]) :

knex('users')
+  .select('*')
+  .denseRank('alias_name', 'email', 'firstName')
+

It also accepts arrays of strings as argument :

knex('users')
+  .select('*')
+  .denseRank(
+    'alias_name', 
+    ['email', 'address'], 
+    ['firstName', 'lastName']
+  )
+

Raw Syntax — .denseRank(alias, rawQuery) :

knex('users').select('*')
+  .denseRank(
+    'alias_name', 
+    knex.raw('order by ??', ['email']
+  ))
+

Function Syntax — .denseRank(alias, function) :

Use orderBy() and partitionBy() (both chainable) to build your query :

knex('users')
+  .select('*')
+  .denseRank('alias_name', function() {
+    this.orderBy('email').partitionBy('firstName')
+  })
+

rank

.rank(alias, ~mixed~)

Add a rank() call to your query. For all the following queries, alias can be set to a falsy value if not needed.

String Syntax — .rank(alias, orderByClause, [partitionByClause]) :

knex('users')
+  .select('*')
+  .rank('alias_name', 'email', 'firstName')
+

It also accepts arrays of strings as argument :

knex('users')
+  .select('*')
+  .rank(
+    'alias_name', 
+    ['email', 'address'], 
+    ['firstName', 'lastName']
+  )
+

Raw Syntax — .rank(alias, rawQuery) :

knex('users')
+  .select('*')
+  .rank('alias_name', knex.raw('order by ??', ['email']))
+

Function Syntax — .rank(alias, function) :

Use orderBy() and partitionBy() (both chainable) to build your query :

knex('users').select('*').rank('alias_name', function() {
+  this.orderBy('email').partitionBy('firstName')
+})
+

rowNumber

.rowNumber(alias, ~mixed~)

Add a row_number() call to your query. For all the following queries, alias can be set to a falsy value if not needed.

String Syntax — .rowNumber(alias, orderByClause, [partitionByClause]) :

knex('users')
+  .select('*')
+  .rowNumber('alias_name', 'email', 'firstName')
+

It also accepts arrays of strings as argument :

knex('users')
+  .select('*')
+  .rowNumber(
+    'alias_name', 
+    ['email', 'address'], 
+    ['firstName', 'lastName']
+  )
+

Raw Syntax — .rowNumber(alias, rawQuery) :

knex('users')
+  .select('*')
+  .rowNumber(
+    'alias_name', 
+    knex.raw('order by ??', ['email'])
+  )
+

Function Syntax — .rowNumber(alias, function) :

Use orderBy() and partitionBy() (both chainable) to build your query :

knex('users')
+  .select('*')
+  .rowNumber('alias_name', function() {
+    this.orderBy('email').partitionBy('firstName')
+  })
+

partitionBy

.partitionBy(column, direction)

Partitions rowNumber, denseRank, rank after a specific column or columns. If direction is not supplied it will default to ascending order.

No direction sort :

knex('users')
+  .select('*')
+  .rowNumber('alias_name', function() {
+    this.partitionBy('firstName');
+  });
+

With direction sort :

knex('users')
+  .select('*')
+  .rowNumber('alias_name', function() {
+    this.partitionBy('firstName', 'desc');
+  });
+

With multiobject :

knex('users')
+  .select('*')
+  .rowNumber('alias_name', function() {
+    this.partitionBy([
+      { column: 'firstName', order: 'asc' }, 
+      { column: 'lastName', order: 'desc' }
+    ]);
+  });
+

modify

*.modify(fn, arguments)

Allows encapsulating and re-using query snippets and common behaviors as functions. The callback function should receive the query builder as its first argument, followed by the rest of the (optional) parameters passed to modify.

const withUserName = function(queryBuilder, foreignKey) {
+  queryBuilder.leftJoin(
+    'users', 
+    foreignKey, 
+    'users.id'
+  ).select('users.user_name');
+};
+knex.table('articles')
+  .select('title', 'body')
+  .modify(withUserName, 'articles_user.id')
+  .then(function(article) {
+    console.log(article.user_name);
+  });
+

columnInfo

.columnInfo([columnName])

Returns an object with the column info about the current table, or an individual column if one is passed, returning an object with the following keys:

  • defaultValue: the default value for the column
  • type: the column type
  • maxLength: the max length set for the column
  • nullable: whether the column may be null
knex('users').columnInfo().then(function(info) { /*...*/ });
+

debug

.debug([enabled])

Overrides the global debug setting for the current query chain. If enabled is omitted, query debugging will be turned on.

connection

.connection(dbConnection)

The method sets the db connection to use for the query without using the connection pool. You should pass to it the same object that acquireConnection() for the corresponding driver returns

const Pool = require('pg-pool');
+const pool = new Pool({ /* ... */ });
+const connection = await pool.connect();
+
+try {
+  return await knex.connection(connection); // knex here is a query builder with query already built
+} catch (error) {
+  // Process error
+} finally {
+  connection.release();
+}
+

options

.options()

Allows for mixing in additional options as defined by database client specific libraries:

    knex('accounts as a1')
+      .leftJoin('accounts as a2', function() {
+        this.on('a1.email', '<>', 'a2.email');
+      })
+      .select(['a1.email', 'a2.email'])
+      .where(knex.raw('a1.id = 1'))
+      .options({ nestTables: true, rowMode: 'array' })
+      .limit(2)
+      .then({ /*...*/ })
+

queryContext

.queryContext(context)

Allows for configuring a context to be passed to the wrapIdentifier and postProcessResponse hooks:

    knex('accounts as a1')
+      .queryContext({ foo: 'bar' })
+      .select(['a1.email', 'a2.email'])
+

The context can be any kind of value and will be passed to the hooks without modification. However, note that objects will be shallow-cloned when a query builder instance is cloned, which means that they will contain all the properties of the original object but will not be the same object reference. This allows modifying the context for the cloned query builder instance.

Calling queryContext with no arguments will return any context configured for the query builder instance.

Extending Query Builder

Important: this feature is experimental and its API may change in the future.

It allows to add custom function to the Query Builder.

Example:

const { knex } = require('knex');
+knex.QueryBuilder.extend('customSelect', function(value) {
+  return this.select(this.client.raw(`${value} as value`));
+});
+
+const meaningOfLife = await knex('accounts')
+  .customSelect(42);
+

If using TypeScript, you can extend the QueryBuilder interface with your custom method.

  1. Create a knex.d.ts file inside a @types folder (or any other folder).
// knex.d.ts
+
+import { Knex as KnexOriginal } from 'knex';
+
+declare module 'knex' {
+  namespace Knex {
+    interface QueryInterface {
+      customSelect<TRecord, TResult>(value: number): KnexOriginal.QueryBuilder<TRecord, TResult>;
+    }
+  }
+}
+
  1. Add the new @types folder to typeRoots in your tsconfig.json.
// tsconfig.json
+{
+  "compilerOptions": {
+    "typeRoots": [
+      "node_modules/@types",
+      "@types"
+    ],
+  }
+}
+

Where Clauses

Several methods exist to assist in dynamic where clauses. In many places functions may be used in place of values, constructing subqueries. In most places existing knex queries may be used to compose sub-queries, etc. Take a look at a few of the examples for each method for instruction on use:

Important: Supplying knex with an undefined value to any of the where functions will cause knex to throw an error during sql compilation. This is both for yours and our sake. Knex cannot know what to do with undefined values in a where clause, and generally it would be a programmatic error to supply one to begin with. The error will throw a message containing the type of query and the compiled query-string. Example:

knex('accounts')
+  .where('login', undefined)
+  .select()
+  .toSQL()
+

where

.where(~mixed~)
.orWhere

Object Syntax:

knex('users').where({
+  first_name: 'Test',
+  last_name:  'User'
+}).select('id')
+

Key, Value:

knex('users').where('id', 1)
+

Functions:

knex('users')
+  .where((builder) =>
+    builder
+      .whereIn('id', [1, 11, 15])
+      .whereNotIn('id', [17, 19])
+  )
+  .andWhere(function() {
+    this.where('id', '>', 10)
+  })
+

Grouped Chain:

knex('users').where(function() {
+  this.where('id', 1).orWhere('id', '>', 10)
+}).orWhere({name: 'Tester'})
+

Operator:

knex('users').where('columnName', 'like', '%rowlikeme%')
+

The above query demonstrates the common use case of returning all users for which a specific pattern appears within a designated column.

knex('users').where('votes', '>', 100)
+
+const subquery = knex('users')
+  .where('votes', '>', 100)
+  .andWhere('status', 'active')
+  .orWhere('name', 'John')
+  .select('id');
+
+knex('accounts').where('id', 'in', subquery)
+

.orWhere with an object automatically wraps the statement and creates an or (and - and - and) clause

knex('users')
+  .where('id', 1)
+  .orWhere({votes: 100, user: 'knex'})
+

whereNot

.whereNot(~mixed~).orWhereNot

Object Syntax:

knex('users').whereNot({
+  first_name: 'Test',
+  last_name:  'User'
+}).select('id')
+

Key, Value:

knex('users').whereNot('id', 1)
+

Grouped Chain:

knex('users').whereNot(function() {
+  this.where('id', 1).orWhereNot('id', '>', 10)
+}).orWhereNot({name: 'Tester'})
+

Operator:

knex('users').whereNot('votes', '>', 100)
+

WARNING

WhereNot is not suitable for "in" and "between" type subqueries. You should use "not in" and "not between" instead.

const subquery = knex('users')
+  .whereNot('votes', '>', 100)
+  .andWhere('status', 'active')
+  .orWhere('name', 'John')
+  .select('id');
+
+knex('accounts').where('id', 'not in', subquery)
+

whereIn

.whereIn(column|columns, array|callback|builder)
.orWhereIn

Shorthand for .where('id', 'in', obj), the .whereIn and .orWhereIn methods add a "where in" clause to the query. Note that passing empty array as the value results in a query that never returns any rows (WHERE 1 = 0)

knex.select('name').from('users')
+  .whereIn('id', [1, 2, 3])
+  .orWhereIn('id', [4, 5, 6])
+
+knex.select('name').from('users')
+  .whereIn('account_id', function() {
+    this.select('id').from('accounts');
+  })
+
+const subquery = knex.select('id').from('accounts');
+
+knex.select('name').from('users')
+  .whereIn('account_id', subquery)
+
+knex.select('name').from('users')
+  .whereIn(
+    ['account_id', 'email'], 
+    [
+      [3, 'test3@example.com'], 
+      [4, 'test4@example.com']
+    ]
+  )
+
+knex.select('name').from('users')
+  .whereIn(
+    ['account_id', 'email'], 
+    knex.select('id', 'email')
+      .from('accounts')
+  )
+

whereNotIn

.whereNotIn(column, array|callback|builder).orWhereNotIn

knex('users').whereNotIn('id', [1, 2, 3])
+
+knex('users')
+  .where('name', 'like', '%Test%')
+  .orWhereNotIn('id', [1, 2, 3])
+

whereNull

.whereNull(column)
.orWhereNull

knex('users').whereNull('updated_at')
+

whereNotNull

.whereNotNull(column)
.orWhereNotNull

knex('users').whereNotNull('created_at')
+

whereExists

.whereExists(builder | callback)
.orWhereExists

knex('users').whereExists(function() {
+  this.select('*')
+    .from('accounts')
+    .whereRaw('users.account_id = accounts.id');
+})
+
+knex('users')
+  .whereExists(
+    knex.select('*')
+      .from('accounts')
+      .whereRaw('users.account_id = accounts.id')
+  )
+

whereNotExists

.whereNotExists(builder | callback)
.orWhereNotExists

knex('users').whereNotExists(function() {
+  this.select('*')
+    .from('accounts')
+    .whereRaw('users.account_id = accounts.id');
+})
+
+knex('users')
+  .whereNotExists(
+    knex.select('*')
+      .from('accounts')
+      .whereRaw('users.account_id = accounts.id')
+  )
+

whereBetween

.whereBetween(column, range)
.orWhereBetween

knex('users').whereBetween('votes', [1, 100])
+

whereNotBetween

.whereNotBetween(column, range)
.orWhereNotBetween

knex('users').whereNotBetween('votes', [1, 100])
+

whereRaw

.whereRaw(query, [bindings])

Convenience helper for .where(knex.raw(query)).

knex('users').whereRaw('id = ?', [1])
+

whereLike

.whereLike(column, string|builder|raw)
.orWhereLike

Adds a where clause with case-sensitive substring comparison on a given column with a given value.

knex('users').whereLike('email', '%mail%')
+
+knex('users')
+  .whereLike('email', '%mail%')
+  .andWhereLike('email', '%.com')
+  .orWhereLike('email', '%name%')
+

whereILike

.whereILike(column, string|builder|raw)
.orWhereILike

Adds a where clause with case-insensitive substring comparison on a given column with a given value.

knex('users').whereILike('email', '%mail%')
+
+knex('users')
+  .whereILike('email', '%MAIL%')
+  .andWhereILike('email', '%.COM')
+  .orWhereILike('email', '%NAME%')
+

whereJsonObject

.whereJsonObject(column, string|json|builder|raw)

Adds a where clause with json object comparison on given json column.

knex('users')
+  .whereJsonObject('json_col', { "name" : "user_name"})
+

whereJsonPath

.whereJsonPath(column, jsonPath, operator, value)

Adds a where clause with comparison of a value returned by a JsonPath given an operator and a value.

knex('users')
+  .whereJsonPath('json_col', '$.age', '>', 18)
+
+knex('users')
+  .whereJsonPath('json_col', '$.name', '=', 'username')
+

whereJsonSupersetOf

.whereJsonSupersetOf(column, string|json|builder|raw)

Adds a where clause where the comparison is true if a json given by the column include a given value. Only on MySQL, PostgreSQL and CockroachDB.

knex('users')
+  .whereJsonSupersetOf('hobbies', { "sport" : "foot" })
+

whereJsonSubsetOf

.whereJsonSubsetOf(column, string|json|builder|raw)

Adds a where clause where the comparison is true if a json given by the column is included in a given value. Only on MySQL, PostgreSQL and CockroachDB.

// given a hobby column with { "sport" : "tennis" }, 
+// the where clause is true
+knex('users')
+  .whereJsonSubsetOf(
+    'hobby', 
+    { "sport" : "tennis", "book" : "fantasy" }
+  )
+

Join Methods

Several methods are provided which assist in building joins.

join

.join(table, first, [operator], second)

The join builder can be used to specify joins between tables, with the first argument being the joining table, the next three arguments being the first join column, the join operator and the second join column, respectively.

knex('users')
+  .join('contacts', 'users.id', '=', 'contacts.user_id')
+  .select('users.id', 'contacts.phone')
+
+knex('users')
+  .join('contacts', 'users.id', 'contacts.user_id')
+  .select('users.id', 'contacts.phone')
+

For grouped joins, specify a function as the second argument for the join query, and use on with orOn or andOn to create joins that are grouped with parentheses.

knex.select('*').from('users').join('accounts', function() {
+  this
+    .on('accounts.id', '=', 'users.account_id')
+    .orOn('accounts.owner_id', '=', 'users.id')
+})
+

For nested join statements, specify a function as first argument of on, orOn or andOn

knex.select('*').from('users').join('accounts', function() {
+  this.on(function() {
+    this.on('accounts.id', '=', 'users.account_id')
+    this.orOn('accounts.owner_id', '=', 'users.id')
+  })
+})
+

It is also possible to use an object to represent the join syntax.

knex.select('*')
+  .from('users')
+  .join('accounts', {'accounts.id': 'users.account_id'})
+

If you need to use a literal value (string, number, or boolean) in a join instead of a column, use knex.raw.

knex.select('*')
+  .from('users')
+  .join(
+    'accounts', 
+    'accounts.type',
+    knex.raw('?', ['admin'])
+  )
+

innerJoin

.innerJoin(table, ~mixed~)

knex
+  .from('users')
+  .innerJoin('accounts', 'users.id', 'accounts.user_id')
+
+knex
+  .table('users')
+  .innerJoin(
+    'accounts', 
+    'users.id', 
+    '=', 
+    'accounts.user_id'
+  )
+
+knex('users')
+  .innerJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

leftJoin

.leftJoin(table, ~mixed~)

knex.select('*')
+  .from('users')
+  .leftJoin('accounts', 'users.id', 'accounts.user_id')
+
+knex.select('*')
+  .from('users')
+  .leftJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

leftOuterJoin

.leftOuterJoin(table, ~mixed~)

knex.select('*')
+  .from('users')
+  .leftOuterJoin('accounts', 'users.id', 'accounts.user_id')
+
+knex.select('*')
+  .from('users')
+  .leftOuterJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

rightJoin

.rightJoin(table, ~mixed~)

knex.select('*')
+  .from('users')
+  .rightJoin('accounts', 'users.id', 'accounts.user_id')
+
+knex.select('*')
+  .from('users')
+  .rightJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

rightOuterJoin

.rightOuterJoin(table, ~mixed~)

knex.select('*')
+  .from('users')
+  .rightOuterJoin(
+    'accounts', 
+    'users.id', 
+    'accounts.user_id'
+  )
+
+knex.select('*')
+  .from('users')
+  .rightOuterJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

fullOuterJoin

.fullOuterJoin(table, ~mixed~)

knex.select('*')
+  .from('users')
+  .fullOuterJoin('accounts', 'users.id', 'accounts.user_id')
+
+knex.select('*')
+  .from('users')
+  .fullOuterJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

crossJoin

.crossJoin(table, ~mixed~)

Cross join conditions are only supported in MySQL and SQLite3. For join conditions rather use innerJoin.

knex.select('*')
+  .from('users')
+  .crossJoin('accounts')
+
+knex.select('*')
+  .from('users')
+  .crossJoin('accounts', 'users.id', 'accounts.user_id')
+
+knex.select('*')
+  .from('users')
+  .crossJoin('accounts', function() {
+    this
+      .on('accounts.id', '=', 'users.account_id')
+      .orOn('accounts.owner_id', '=', 'users.id')
+  })
+

joinRaw

.joinRaw(sql, [bindings])

knex.select('*')
+  .from('accounts')
+  .joinRaw('natural full join table1').where('id', 1)
+
+knex.select('*')
+  .from('accounts')
+  .join(knex.raw('natural full join table1')).where('id', 1)
+

OnClauses

onIn

.onIn(column, values)

Adds a onIn clause to the query.

knex.select('*')
+  .from('users')
+  .join('contacts', function() {
+    this
+      .on('users.id', '=', 'contacts.id')
+      .onIn('contacts.id', [7, 15, 23, 41])
+  })
+

onNotIn

.onNotIn(column, values)

Adds a onNotIn clause to the query.

knex.select('*')
+  .from('users')
+  .join('contacts', function() {
+    this
+      .on('users.id', '=', 'contacts.id')
+      .onNotIn('contacts.id', [7, 15, 23, 41])
+  })
+

onNull

.onNull(column)

Adds a onNull clause to the query.

knex.select('*').from('users').join('contacts', function() {
+  this
+    .on('users.id', '=', 'contacts.id')
+    .onNull('contacts.email')
+})
+

onNotNull

.onNotNull(column)

Adds a onNotNull clause to the query.

knex.select('*').from('users').join('contacts', function() {
+  this
+    .on('users.id', '=', 'contacts.id')
+    .onNotNull('contacts.email')
+})
+

onExists

.onExists(builder | callback)

Adds a onExists clause to the query.

knex.select('*').from('users').join('contacts', function() {
+  this
+    .on('users.id', '=', 'contacts.id')
+    .onExists(function() {
+      this.select('*')
+        .from('accounts')
+        .whereRaw('users.account_id = accounts.id');
+    })
+})
+

onNotExists

.onNotExists(builder | callback)

Adds a onNotExists clause to the query.

knex.select('*').from('users').join('contacts', function() {
+  this
+    .on('users.id', '=', 'contacts.id')
+    .onNotExists(function() {
+      this.select('*')
+        .from('accounts')
+        .whereRaw('users.account_id = accounts.id');
+    })
+})
+

onBetween

.onBetween(column, range)

Adds a onBetween clause to the query.

knex.select('*').from('users').join('contacts', function() {
+  this
+    .on('users.id', '=', 'contacts.id')
+    .onBetween('contacts.id', [5, 30])
+})
+

onNotBetween

.onNotBetween(column, range)

Adds a onNotBetween clause to the query.

knex.select('*').from('users').join('contacts', function() {
+  this
+    .on('users.id', '=', 'contacts.id')
+    .onNotBetween('contacts.id', [5, 30])
+})
+

onJsonPathEquals

.onJsonPathEquals(column, range)

Adds a onJsonPathEquals clause to the query. The clause performs a join on value returned by two json paths on two json columns.

knex('cities')
+  .select(
+    'cities.name as cityName', 
+    'country.name as countryName'
+  )
+  .join('country', function () {
+    this.onJsonPathEquals(
+      // json column in cities
+      'country_name', 
+      // json path to country name in 'country_name' column
+      '$.country.name', 
+      // json column in country
+      'description', 
+       // json field in 'description' column
+      '$.name'
+    );
+  })
+

ClearClauses

clear

.clear(statement)

Clears the specified operator from the query. Available operators: 'select' alias 'columns', 'with', 'select', 'columns', 'where', 'union', 'join', 'group', 'order', 'having', 'limit', 'offset', 'counter', 'counters'. Counter(s) alias for method .clearCounter()

knex.select('email', 'name')
+  .from('users')
+  .where('id', '<', 10)
+  .clear('select')
+  .clear('where')
+

clearSelect

.clearSelect()

Deprecated, use clear('select'). Clears all select clauses from the query, excluding subqueries.

    knex.select('email', 'name').from('users').clearSelect()
+

clearWhere

.clearWhere()

Deprecated, use clear('where'). Clears all where clauses from the query, excluding subqueries.

knex.select('email', 'name')
+  .from('users')
+  .where('id', 1)
+  .clearWhere()
+

clearGroup

.clearGroup()

Deprecated, use clear('group'). Clears all group clauses from the query, excluding subqueries.

knex.select().from('users').groupBy('id').clearGroup()
+

clearOrder

.clearOrder()

Deprecated, use clear('order'). Clears all order clauses from the query, excluding subqueries.

knex.select()
+  .from('users')
+  .orderBy('name', 'desc')
+  .clearOrder()
+

clearHaving

.clearHaving()

Deprecated, use clear('having'). Clears all having clauses from the query, excluding subqueries.

knex.select()
+  .from('users')
+  .having('id', '>', 5)
+  .clearHaving()
+

clearCounters

.clearCounters()

Clears all increments/decrements clauses from the query.

  knex('accounts')
+    .where('id', '=', 1)
+    .update({ email: 'foo@bar.com' })
+    .decrement({
+      balance: 50,
+    })
+    .clearCounters()
+

distinct

.distinct([*columns])

Sets a distinct clause on the query. If the parameter is falsy or empty array, method falls back to '*'.

// select distinct 'first_name' from customers
+knex('customers')
+  .distinct('first_name', 'last_name')
+
+// select which eliminates duplicate rows
+knex('customers')
+  .distinct()
+

distinctOn

.distinctOn([*columns])

PostgreSQL only. Adds a distinctOn clause to the query.

knex('users').distinctOn('age')
+

groupBy

*.groupBy(names)

Adds a group by clause to the query.

knex('users').groupBy('count')
+

groupByRaw

.groupByRaw(sql)

Adds a raw group by clause to the query.

knex.select('year', knex.raw('SUM(profit)'))
+  .from('sales')
+  .groupByRaw('year WITH ROLLUP')
+

orderBy

.orderBy(column|columns, [direction], [nulls])

Adds an order by clause to the query. column can be string, or list mixed with string and object. nulls specify where the nulls values are put (can be 'first' or 'last').

Single Column:

knex('users').orderBy('email')
+
+knex('users').orderBy('name', 'desc')
+
+knex('users').orderBy('name', 'desc', 'first')
+

Multiple Columns:

knex('users').orderBy([
+  'email', { column: 'age', order: 'desc' }
+])
+
+knex('users').orderBy([
+  { column: 'email' }, 
+  { column: 'age', order: 'desc' }
+])
+
+knex('users').orderBy([
+  { column: 'email' }, 
+  { column: 'age', order: 'desc', nulls: 'last' }
+])
+

orderByRaw

.orderByRaw(sql)

Adds an order by raw clause to the query.

knex.select('*')
+  .from('table')
+  .orderByRaw('col DESC NULLS LAST')
+

Having Clauses

having

.having(column, operator, value)

Adds a having clause to the query.

knex('users')
+  .groupBy('count')
+  .orderBy('name', 'desc')
+  .having('count', '>', 100)
+

havingIn

.havingIn(column, values)

Adds a havingIn clause to the query.

knex.select('*')
+  .from('users')
+  .havingIn('id', [5, 3, 10, 17])
+

havingNotIn

.havingNotIn(column, values)

Adds a havingNotIn clause to the query.

knex.select('*')
+  .from('users')
+  .havingNotIn('id', [5, 3, 10, 17])
+

havingNull

.havingNull(column)

Adds a havingNull clause to the query.

knex.select('*').from('users').havingNull('email')
+

havingNotNull

.havingNotNull(column)

Adds a havingNotNull clause to the query.

knex.select('*').from('users').havingNotNull('email')
+

havingExists

.havingExists(builder | callback)

Adds a havingExists clause to the query.

knex.select('*').from('users').havingExists(function() {
+  this.select('*')
+    .from('accounts')
+    .whereRaw('users.account_id = accounts.id');
+})
+

havingNotExists

.havingNotExists(builder | callback)

Adds a havingNotExists clause to the query.

knex.select('*').from('users').havingNotExists(function() {
+  this.select('*')
+    .from('accounts')
+    .whereRaw('users.account_id = accounts.id');
+})
+

havingBetween

.havingBetween(column, range)

Adds a havingBetween clause to the query.

knex.select('*')
+  .from('users')
+  .havingBetween('id', [5, 10])
+

havingNotBetween

.havingNotBetween(column, range)

Adds a havingNotBetween clause to the query.

knex.select('*')
+  .from('users')
+  .havingNotBetween('id', [5, 10])
+

havingRaw

.havingRaw(sql, [bindings])

Adds a havingRaw clause to the query.

knex('users')
+  .groupBy('count')
+  .orderBy('name', 'desc')
+  .havingRaw('count > ?', [100])
+
+ + + + + \ No newline at end of file diff --git a/guide/raw.html b/guide/raw.html new file mode 100644 index 00000000..5cad8e7e --- /dev/null +++ b/guide/raw.html @@ -0,0 +1,86 @@ + + + + + + Raw | Knex.js + + + + + + + + + + + + +

Raw

Sometimes you may need to use a raw expression in a query. Raw query object may be injected pretty much anywhere you want, and using proper bindings can ensure your values are escaped properly, preventing SQL-injection attacks.

Raw Parameter Binding

One can parameterize sql given to knex.raw(sql, bindings). Parameters can be positional named. One can also choose if parameter should be treated as value or as sql identifier e.g. in case of 'TableName.ColumnName' reference.

knex('users')
+  .select(knex.raw('count(*) as user_count, status'))
+  .where(knex.raw(1))
+  .orWhere(knex.raw('status <> ?', [1]))
+  .groupBy('status')
+

Positional bindings ? are interpreted as values and ?? are interpreted as identifiers.

knex('users').where(knex.raw('?? = ?', ['user.name', 1]))
+

Named bindings such as :name are interpreted as values and :name: interpreted as identifiers. Named bindings are processed so long as the value is anything other than undefined.

const raw = ':name: = :thisGuy or :name: = :otherGuy or :name: = :undefinedBinding'
+
+knex('users')
+  .where(
+    knex.raw(raw, {
+      name: 'users.name',
+      thisGuy: 'Bob',
+      otherGuy: 'Jay',
+      undefinedBinding: undefined
+  }))
+

For simpler queries where one only has a single binding, .raw can accept said binding as its second parameter.

knex('users')
+  .where(
+    knex.raw('LOWER("login") = ?', 'knex')
+  )
+  .orWhere(
+    knex.raw('accesslevel = ?', 1)
+  )
+  .orWhere(
+    knex.raw('updtime = ?', '01-01-2016')
+  )
+

Since there is no unified syntax for array bindings, instead you need to treat them as multiple values by adding ? directly in your query.

const myArray = [1,2,3]
+knex.raw('select * from users where id in (' + myArray.map(_ => '?').join(',') + ')', [...myArray]);
+
+

query will become:

select * from users where id in (?, ?, ?) /* with bindings [1,2,3] */
+

To prevent replacement of ? one can use the escape sequence \\?.

knex.select('*')
+  .from('users')
+  .where('id', '=', 1)
+  .whereRaw('?? \\? ?', ['jsonColumn', 'jsonKey'])
+

To prevent replacement of named bindings one can use the escape sequence \\:.

knex.select('*')
+  .from('users')
+  .whereRaw(":property: = '\\:value' OR \\:property: = :value", {
+    property: 'name',
+    value: 'Bob'
+  })
+

Raw Expressions

Raw expressions are created by using knex.raw(sql, [bindings]) and passing this as a value for any value in the query chain.

knex('users').select(knex.raw('count(*) as user_count, status'))
+  .where(knex.raw(1))
+  .orWhere(knex.raw('status <> ?', [1]))
+  .groupBy('status')
+

Raw Queries

The knex.raw may also be used to build a full query and execute it, as a standard query builder query would be executed. The benefit of this is that it uses the connection pool and provides a standard interface for the different client libraries.

knex.raw('select * from users where id = ?', [1])
+  .then(function(resp) { /*...*/ });
+

Note that the response will be whatever the underlying sql library would typically return on a normal query, so you may need to look at the documentation for the base library the queries are executing against to determine how to handle the response.

Wrapped Queries

The raw query builder also comes with a wrap method, which allows wrapping the query in a value:

const subcolumn = knex.raw(
+    'select avg(salary) from employee where dept_no = e.dept_no'
+  )
+  .wrap('(', ') avg_sal_dept');
+
+knex.select('e.lastname', 'e.salary', subcolumn)
+  .from('employee as e')
+  .whereRaw('dept_no = e.dept_no')
+

Note that the example above be achieved more easily using the as method.

const subcolumn = knex.avg('salary')
+  .from('employee')
+  .whereRaw('dept_no = e.dept_no')
+  .as('avg_sal_dept');
+
+knex.select('e.lastname', 'e.salary', subcolumn)
+  .from('employee as e')
+  .whereRaw('dept_no = e.dept_no')
+
+ + + + + \ No newline at end of file diff --git a/guide/ref.html b/guide/ref.html new file mode 100644 index 00000000..8d27239f --- /dev/null +++ b/guide/ref.html @@ -0,0 +1,51 @@ + + + + + + Ref | Knex.js + + + + + + + + + + + + +

Ref

Can be used to create references in a query, such as column- or tablenames. This is a good and shorter alternative to using knex.raw('??', 'tableName.columName') which essentially does the same thing.

Usage

knex.ref can be used essentially anywhere in a build-chain. Here is an example:

knex(knex.ref('Users').withSchema('TenantId'))
+  .where(knex.ref('Id'), 1)
+  .orWhere(knex.ref('Name'), 'Admin')
+  .select(['Id', knex.ref('Name').as('Username')])
+
select `Id`, `Name` as `Username` from `TenantId`.`Users` where `Id` = 1 or `Name` = 'Admin'
+
select "Id", "Name" as "Username" from "TenantId"."Users" where "Id" = 1 or "Name" = 'Admin'
+
select [Id], [Name] as [Username] from [TenantId].[Users] where [Id] = 1 or [Name] = 'Admin'
+
select `Id`, `Name` as `Username` from `TenantId`.`Users` where `Id` = 1 or `Name` = 'Admin'
+
select `Id`, `Name` as `Username` from `TenantId`.`Users` where `Id` = 1 or `Name` = 'Admin'
+
select "Id", "Name" as "Username" from "TenantId"."Users" where "Id" = 1 or "Name" = 'Admin'
+
select "Id", "Name" as "Username" from "TenantId"."Users" where "Id" = 1 or "Name" = 'Admin'
+
select "Id", "Name" as "Username" from "TenantId"."Users" where "Id" = 1 or "Name" = 'Admin'
+
select "Id", "Name" as "Username" from "TenantId"."Users" where "Id" = 1 or "Name" = 'Admin'
+
select `Id`, `Name` as `Username` from `TenantId`.`Users` where `Id` = 1 or `Name` = 'Admin'
+

withSchema

The Ref function supports schema using .withSchema(string):

knex(knex.ref('users').withSchema('TenantId')).select()
+

alias

Alias is supported using .alias(string)

knex('users')
+  .select(knex.ref('Id').as('UserId'))
+
select `Id` as `UserId` from `users`
+
select "Id" as "UserId" from "users"
+
select [Id] as [UserId] from [users]
+
select `Id` as `UserId` from `users`
+
select `Id` as `UserId` from `users`
+
select "Id" as "UserId" from "users"
+
select "Id" as "UserId" from "users"
+
select "Id" as "UserId" from "users"
+
select "Id" as "UserId" from "users"
+
select `Id` as `UserId` from `users`
+
+ + + + + \ No newline at end of file diff --git a/guide/schema-builder.html b/guide/schema-builder.html new file mode 100644 index 00000000..9f2c9ac1 --- /dev/null +++ b/guide/schema-builder.html @@ -0,0 +1,286 @@ + + + + + + Schema Builder | Knex.js + + + + + + + + + + + + +

Schema Builder

The knex.schema is a getter function, which returns a stateful object containing the query. Therefore be sure to obtain a new instance of the knex.schema for every query. These methods return promises.

Essentials

withSchema

knex.schema.withSchema([schemaName])

Specifies the schema to be used when using the schema-building commands.

knex.schema.withSchema('public').createTable('users', function (table) {
+  table.increments();
+})
+

createTable

knex.schema.createTable(tableName, callback)

Creates a new table on the database, with a callback function to modify the table's structure, using the schema-building commands.

knex.schema.createTable('users', function (table) {
+  table.increments();
+  table.string('name');
+  table.timestamps();
+})
+

createTableLike

knex.schema.createTableLike(tableName, tableNameToCopy, [callback])

Creates a new table on the database based on another table. Copy only the structure : columns, keys and indexes (expected on SQL Server which only copy columns) and not the data. Callback function can be specified to add columns in the duplicated table.

knex.schema.createTableLike('new_users', 'users')
+
+// "new_users" table contains columns 
+// of users and two new columns 'age' and 'last_name'.
+knex.schema.createTableLike('new_users', 'users', (table) => {
+  table.integer('age');
+  table.string('last_name');
+})
+

dropTable

knex.schema.dropTable(tableName)

Drops a table, specified by tableName.

knex.schema.dropTable('users')
+

dropTableIfExists

knex.schema.dropTableIfExists(tableName)

Drops a table conditionally if the table exists, specified by tableName.

knex.schema.dropTableIfExists('users')
+

renameTable

knex.schema.renameTable(from, to)

Renames a table from a current tableName to another.

knex.schema.renameTable('old_users', 'users')
+

hasTable

knex.schema.hasTable(tableName)

Checks for a table's existence by tableName, resolving with a boolean to signal if the table exists.

knex.schema.hasTable('users').then(function(exists) {
+  if (!exists) {
+    return knex.schema.createTable('users', function(t) {
+      t.increments('id').primary();
+      t.string('first_name', 100);
+      t.string('last_name', 100);
+      t.text('bio');
+    });
+  }
+});
+

hasColumn

knex.schema.hasColumn(tableName, columnName)

Checks if a column exists in the current table, resolves the promise with a boolean, true if the column exists, false otherwise.

table

knex.schema.table(tableName, callback)

Chooses a database table, and then modifies the table, using the Schema Building functions inside of the callback.

knex.schema.table('users', function (table) {
+  table.dropColumn('name');
+  table.string('first_name');
+  table.string('last_name');
+})
+

alterTable

knex.schema.alterTable(tableName, callback)

Chooses a database table, and then modifies the table, using the Schema Building functions inside of the callback.

knex.schema.alterTable('users', function (table) {
+  table.dropColumn('name');
+  table.string('first_name');
+  table.string('last_name');
+})
+

createView

knex.schema.createView(tableName, callback)

Creates a new view on the database, with a callback function to modify the view's structure, using the schema-building commands.

knex.schema.createView('users_view', function (view) {
+  view.columns(['first_name']);
+  view.as(knex('users').select('first_name').where('age','>', '18'));
+})
+

createViewOrReplace

knex.schema.createViewOrReplace(tableName, callback)

Creates a new view or replace it on the database, with a callback function to modify the view's structure, using the schema-building commands. You need to specify at least the same columns in same order (you can add extra columns). In SQLite, this function generate drop/create view queries (view columns can be different).

knex.schema.createViewOrReplace('users_view', function (view) {
+  view.columns(['first_name']);
+  view.as(knex('users').select('first_name').where('age','>', '18'));
+})
+

createMaterializedView

knex.schema.createMaterializedView(viewName, callback)

Creates a new materialized view on the database, with a callback function to modify the view's structure, using the schema-building commands. Only on PostgreSQL, CockroachDb, Redshift and Oracle.

knex.schema.createMaterializedView('users_view', function (view) {
+  view.columns(['first_name']);
+  view.as(knex('users').select('first_name').where('age','>', '18'));
+})
+

refreshMaterializedView

knex.schema.refreshMaterializedView(viewName)

Refresh materialized view on the database. Only on PostgreSQL, CockroachDb, Redshift and Oracle.

knex.schema.refreshMaterializedView('users_view')
+

dropView

knex.schema.dropView(viewName)

Drop view on the database.

knex.schema.dropView('users_view')
+

dropViewIfExists

knex.schema.dropViewIfExists(viewName)

Drop view on the database if exists.

knex.schema.dropViewIfExists('users_view')
+

dropMaterializedView

knex.schema.dropMaterializedView(viewName)

Drop materialized view on the database. Only on PostgreSQL, CockroachDb, Redshift and Oracle.

knex.schema.dropMaterializedView('users_view')
+

dropMaterializedViewIfExists

knex.schema.dropMaterializedViewIfExists(viewName)

Drop materialized view on the database if exists. Only on PostgreSQL, CockroachDb, Redshift and Oracle.

knex.schema.dropMaterializedViewIfExists('users_view')
+

renameView

knex.schema.renameView(viewName)

Rename a existing view in the database. Not supported by Oracle and SQLite.

knex.schema.renameView('users_view')
+

alterView

knex.schema.alterView(viewName)

Alter view to rename columns or change default values. Only available on PostgreSQL, MSSQL and Redshift.

knex.schema.alterView('view_test', function (view) {
+  view.column('first_name').rename('name_user');
+  view.column('bio').defaultTo('empty');
+})
+

generateDdlCommands

knex.schema.generateDdlCommands()

Generates complete SQL commands for applying described schema changes, without executing anything. Useful when knex is being used purely as a query builder. Generally produces same result as .toSQL(), with a notable exception with SQLite, which relies on asynchronous calls to the database for building part of its schema modification statements

const ddlCommands = knex.schema.alterTable(
+  'users',
+  (table) => {
+    table
+      .foreign('companyId')
+      .references('company.companyId')
+      .withKeyName('fk_fkey_company');
+  }
+).generateDdlCommands();
+

raw

knex.schema.raw(statement)

Run an arbitrary sql query in the schema builder chain.

knex.schema.raw("SET sql_mode='TRADITIONAL'")
+  .table('users', function (table) {
+    table.dropColumn('name');
+    table.string('first_name');
+    table.string('last_name');
+  })
+

queryContext

knex.schema.queryContext(context)

Allows configuring a context to be passed to the wrapIdentifier hook. The context can be any kind of value and will be passed to wrapIdentifier without modification.

knex.schema.queryContext({ foo: 'bar' })
+  .table('users', function (table) {
+    table.string('first_name');
+    table.string('last_name');
+  })
+

The context configured will be passed to wrapIdentifier for each identifier that needs to be formatted, including the table and column names. However, a different context can be set for the column names via table.queryContext.

Calling queryContext with no arguments will return any context configured for the schema builder instance.

dropSchema

knex.schema.dropSchema(schemaName, [cascade])

Drop a schema, specified by the schema's name, with optional cascade option (default to false). Only supported by PostgreSQL.

//drop schema 'public'
+knex.schema.dropSchema('public')
+//drop schema 'public' cascade
+knex.schema.dropSchema('public', true)
+

dropSchemaIfExists

knex.schema.dropSchemaIfExists(schemaName, [cascade])

Drop a schema conditionally if the schema exists, specified by the schema's name, with optional cascade option (default to false). Only supported by PostgreSQL.

//drop schema if exists 'public'
+knex.schema.dropSchemaIfExists('public')
+//drop schema if exists 'public' cascade
+knex.schema.dropSchemaIfExists('public', true)
+

Schema Building

dropColumn

table.dropColumn(name)

Drops a column, specified by the column's name

dropColumns

table.dropColumns(columns)

Drops multiple columns, taking a variable number of column names.

renameColumn

table.renameColumn(from, to)

Renames a column from one name to another.

increments

table.increments(name, options={[primaryKey: boolean = true])

Adds an auto incrementing column. In PostgreSQL this is a serial; in Amazon Redshift an integer identity(1,1). This will be used as the primary key for the table if the column isn't in another primary key. Also available is a bigIncrements if you wish to add a bigint incrementing number (in PostgreSQL bigserial). Note that a primary key is created by default if the column isn't in primary key (with primary function), but you can override this behaviour by passing the primaryKey option. If you use this function with primary function, the column is added to the composite primary key. With SQLite, autoincrement column need to be a primary key, so if primary function is used, primary keys are transformed in unique index. MySQL don't support autoincrement column without primary key, so multiple queries are generated to create int column, add increments column to composite primary key then modify the column to autoincrement column.

// create table 'users' 
+// with a primary key using 'increments()'
+knex.schema.createTable('users', function (table) {
+  table.increments('userId');
+  table.string('name');
+});
+
+// create table 'users' 
+// with a composite primary key ('userId', 'name'). 
+// increments doesn't generate primary key.
+knex.schema.createTable('users', function (table) {
+  table.primary(['userId', 'name']);
+  table.increments('userId');
+  table.string('name');
+});
+
+// reference the 'users' primary key in new table 'posts'
+knex.schema.createTable('posts', function (table) {
+  table.integer('author').unsigned().notNullable();
+  table.string('title', 30);
+  table.string('content');
+
+  table.foreign('author').references('userId').inTable('users');
+});
+

A primaryKey option may be passed, to disable to automatic primary key creation:

// create table 'users' 
+// with a primary key using 'increments()'
+// but also increments field 'other_id' 
+// that does not need primary key
+knex.schema.createTable('users', function (table) {
+  table.increments('id');
+  table.increments('other_id', { primaryKey: false });
+});
+

integer

table.integer(name, length)

Adds an integer column. On PostgreSQL you cannot adjust the length, you need to use other option such as bigInteger, etc

bigInteger

table.bigInteger(name)

In MySQL or PostgreSQL, adds a bigint column, otherwise adds a normal integer. Note that bigint data is returned as a string in queries because JavaScript may be unable to parse them without loss of precision.

tinyint

table.tinyint(name, length)

Adds a tinyint column

smallint

table.smallint(name)

Adds a smallint column

mediumint

table.mediumint(name)

Adds a mediumint column

bigint

table.bigint(name)

Adds a bigint column

text

table.text(name, [textType])

Adds a text column, with optional textType for MySql text datatype preference. textType may be mediumtext or longtext, otherwise defaults to text.

string

table.string(name, [length])

Adds a string column, with optional length defaulting to 255.

float

table.float(column, [precision], [scale])

Adds a float column, with optional precision (defaults to 8) and scale (defaults to 2).

double

table.double(column, [precision], [scale])

Adds a double column, with optional precision (defaults to 8) and scale (defaults to 2). In SQLite/MSSQL this is a float with no precision/scale; In PostgreSQL this is a double precision; In Oracle this is a number with matching precision/scale.

decimal

table.decimal(column, [precision], [scale])

Adds a decimal column, with optional precision (defaults to 8) and scale (defaults to 2). Specifying NULL as precision creates a decimal column that can store numbers of any precision and scale. (Only supported for Oracle, SQLite, Postgres)

boolean

table.boolean(name)

Adds a boolean column.

date

table.date(name)

Adds a date column.

datetime

table.datetime(name, options={[useTz: boolean], [precision: number]})

Adds a datetime column. By default PostgreSQL creates column with timezone (timestamptz type). This behaviour can be overriden by passing the useTz option (which is by default true for PostgreSQL). MySQL and MSSQL do not have useTz option.

A precision option may be passed:

table.datetime('some_time', { precision: 6 }).defaultTo(knex.fn.now(6))
+

time

table.time(name, [precision])

Adds a time column, with optional precision for MySQL. Not supported on Amazon Redshift.

In MySQL a precision option may be passed:

table.time('some_time', { precision: 6 })
+

timestamp

table.timestamp(name, options={[useTz: boolean], [precision: number]})

Adds a timestamp column. By default PostgreSQL creates column with timezone (timestamptz type) and MSSQL does not (datetime2). This behaviour can be overriden by passing the useTz option (which is by default false for MSSQL and true for PostgreSQL). MySQL does not have useTz option.

table.timestamp('created_at').defaultTo(knex.fn.now());
+

In PostgreSQL and MySQL a precision option may be passed:

table.timestamp('created_at', { precision: 6 }).defaultTo(knex.fn.now(6));
+

In PostgreSQL and MSSQL a timezone option may be passed:

table.timestamp('created_at', { useTz: true });
+

timestamps

table.timestamps([useTimestamps], [defaultToNow], [useCamelCase])

Adds created_at and updated_at columns on the database, setting each to datetime types. When true is passed as the first argument a timestamp type is used instead. Both columns default to being not null and using the current timestamp when true is passed as the second argument. Note that on MySQL the .timestamps() only have seconds precision, to get better precision use the .datetime or .timestamp methods directly with precision. If useCamelCase is true, the name of columns are createdAt and updatedAt.

INFO

PostgreSQL updated_at field will not automatically be updated. Please see this issue for details

dropTimestamps

table.dropTimestamps([useCamelCase])

Drops the columns created_at and updated_at from the table, which can be created via timestamps. If useCamelCase is true, the name of columns are createdAt and updatedAt.

binary

table.binary(name, [length])

Adds a binary column, with optional length argument for MySQL.

enum / enu

table.enu(col, values, [options])

Adds a enum column, (aliased to enu, as enum is a reserved word in JavaScript). Implemented as unchecked varchar(255) on Amazon Redshift. Note that the second argument is an array of values. Example:

table.enu('column', ['value1', 'value2'])
+

For Postgres, an additional options argument can be provided to specify whether or not to use Postgres's native TYPE:

table.enu('column', ['value1', 'value2'], { useNative: true, enumName: 'foo_type' })
+

It will use the values provided to generate the appropriate TYPE. Example:

CREATE TYPE "foo_type" AS ENUM ('value1', 'value2');
+

To use an existing native type across columns, specify 'existingType' in the options (this assumes the type has already been created):

INFO

Since the enum values aren't utilized for a native && existing type, the type being passed in for values is immaterial.

table.enu('column', null, { useNative: true, existingType: true, enumName: 'foo_type' })
+

If you want to use existing enums from a schema, different from the schema of your current table, specify 'schemaName' in the options:

table.enu('column', null, { useNative: true, existingType: true, enumName: 'foo_type', schemaName: 'public' })
+

Knex does not provide any way to alter enumerations after creation. To change an enumeration later on you must use Knex.raw, and the appropriate command for your database.

json

table.json(name)

Adds a json column, using the built-in json type in PostgreSQL, MySQL and SQLite, defaulting to a text column in older versions or in unsupported databases.

For PostgreSQL, due to incompatibility between native array and json types, when setting an array (or a value that could be an array) as the value of a json or jsonb column, you should use JSON.stringify() to convert your value to a string prior to passing it to the query builder, e.g.

knex.table('users')
+  .where({id: 1})
+  .update({json_data: JSON.stringify(mightBeAnArray)});
+

jsonb

table.jsonb(name)

Adds a jsonb column. Works similar to table.json(), but uses native jsonb type if possible.

uuid

table.uuid(name, options=({[useBinaryUuid:boolean],[primaryKey:boolean]})

Adds a uuid column - this uses the built-in uuid type in PostgreSQL, and falling back to a char(36) in other databases by default. If useBinaryUuid is true, binary(16) is used. See uuidToBin function to convert uuid in binary before inserting and binToUuid to convert binary uuid to uuid. If primaryKey is true, then for PostgreSQL the field will be configured as uuid primary key, for CockroackDB an additional default gen_random_uuid() is set on the type.

You may set the default value to the uuid helper function. Not supported by Redshift.

knex.schema.createTable(tblName, (table) => {
+  table.uuid('uuidColumn').defaultTo(knex.fn.uuid());
+});
+

geometry

table.geometry(name)

Adds a geometry column. Supported by SQLite, MSSQL and PostgreSQL.

knex.schema.createTable(tblName, (table) => {
+  table.geometry('geometryColumn');
+});
+

geography

table.geography(name)

Adds a geography column. Supported by SQLite, MSSQL and PostgreSQL (in PostGIS extension).

knex.schema.createTable(tblName, (table) => {
+  table.geography('geographyColumn');
+});
+

point

table.point(name)

Add a point column. Not supported by CockroachDB and MSSQL.

knex.schema.createTable(tblName, (table) => {
+  table.point('pointColumn');
+});
+

comment

table.comment(value)

Sets the comment for a table.

engine

table.engine(val)

Sets the engine for the database table, only available within a createTable call, and only applicable to MySQL.

charset

table.charset(val)

Sets the charset for the database table, only available within a createTable call, and only applicable to MySQL.

collate

table.collate(val)

Sets the collation for the database table, only available within a createTable call, and only applicable to MySQL.

inherits

table.inherits(val)

Sets the tables that this table inherits, only available within a createTable call, and only applicable to PostgreSQL.

specificType

table.specificType(name, type)

Sets a specific type for the column creation, if you'd like to add a column type that isn't supported here.

index

table.index(columns, [indexName], options=({[indexType: string], [storageEngineIndexType: 'btree'|'hash'], [predicate: QueryBuilder]}))

Adds an index to a table over the given columns. A default index name using the columns is used unless indexName is specified. In MySQL, the storage engine index type may be 'btree' or 'hash' index types, more info in Index Options section : https://dev.mysql.com/doc/refman/8.0/en/create-index.html. The indexType can be optionally specified for PostgreSQL and MySQL. Amazon Redshift does not allow creating an index. In PostgreSQL, SQLite and MSSQL a partial index can be specified by setting a 'where' predicate.

knex.table('users', function (table) {
+  table.index(['name', 'last_name'], 'idx_name_last_name', {
+    indexType: 'FULLTEXT',
+    storageEngineIndexType: 'hash',
+    predicate: knex.whereNotNull('email'),
+  });
+});
+

dropIndex

table.dropIndex(columns, [indexName])

Drops an index from a table. A default index name using the columns is used unless indexName is specified (in which case columns is ignored). Amazon Redshift does not allow creating an index.

setNullable

table.setNullable(column)

Makes table column nullable.

dropNullable

table.dropNullable(column)

Makes table column not nullable. Note that this operation will fail if there are already null values in this column.

primary

table.primary(columns, options=({[constraintName:string],[deferrable:'not deferrable'|'deferred'|'immediate']})

Create a primary key constraint on table using input columns. If you need to create a composite primary key, pass an array of columns to columns. Constraint name defaults to tablename_pkey unless constraintName is specified. On Amazon Redshift, all columns included in a primary key must be not nullable. Deferrable primary constraint are supported on Postgres and Oracle and can be set by passing deferrable option to options object.

knex.schema.alterTable('users', function(t) {
+  t.unique('email')
+})
+knex.schema.alterTable('job', function(t) {
+  t.primary('email',{constraintName:'users_primary_key',deferrable:'deferred'})
+})
+

INFO

If you want to chain primary() while creating new column you can use primary

unique

table.unique(columns, options={[indexName: string], [deferrable:'not deferrable'|'immediate'|'deferred'], [storageEngineIndexType:'btree'|'hash'], [useConstraint:true|false], [predicate: QueryBuilder]})

Adds an unique index to a table over the given columns. In MySQL, the storage engine index type may be 'btree' or 'hash' index types, more info in Index Options section : https://dev.mysql.com/doc/refman/8.0/en/create-index.html. A default index name using the columns is used unless indexName is specified. If you need to create a composite index, pass an array of column to columns. Deferrable unique constraint are supported on Postgres and Oracle and can be set by passing deferrable option to options object. In MSSQL and Postgres, you can set the useConstraint option to true to create a unique constraint instead of a unique index (defaults to false for MSSQL, true for Postgres without predicate, false for Postgres with predicate). In PostgreSQL, SQLite and MSSQL a partial unique index can be specified by setting a 'where' predicate.

knex.schema.alterTable('users', function(t) {
+  t.unique('email')
+})
+knex.schema.alterTable('job', function(t) {
+  t.unique(['account_id', 'program_id'], {indexName: 'job_composite_index', deferrable: 'deferred', storageEngineIndexType: 'hash'})
+})
+knex.schema.alterTable('job', function(t) {
+  t.unique(['account_id', 'program_id'], {indexName: 'job_composite_index', useConstraint: true})
+})
+knex.schema.alterTable('job', function(t) {
+  t.unique(['account_id', 'program_id'], {indexName: 'job_composite_index', predicate: knex.whereNotNull('account_id')})
+})
+

INFO

If you want to chain unique() while creating new column you can use unique

foreign

table.foreign(columns, [foreignKeyName])[.onDelete(statement).onUpdate(statement).withKeyName(foreignKeyName).deferrable(type)]

Adds a foreign key constraint to a table for an existing column using table.foreign(column).references(column) or multiple columns using table.foreign(columns).references(columns).inTable(table).

A default key name using the columns is used unless foreignKeyName is specified.

You can also chain onDelete() and/or onUpdate() to set the reference option (RESTRICT, CASCADE, SET NULL, NO ACTION) for the operation. You can also chain withKeyName() to override default key name that is generated from table and column names (result is identical to specifying second parameter to function foreign()).

Deferrable foreign constraint is supported on Postgres and Oracle and can be set by chaining .deferrable(type)

Note that using foreign() is the same as column.references(column) but it works for existing columns.

knex.schema.table('users', function (table) {
+  table.integer('user_id').unsigned()
+  table.foreign('user_id').references('Items.user_id_in_items').deferrable('deferred')
+})
+

dropForeign

table.dropForeign(columns, [foreignKeyName])

Drops a foreign key constraint from a table. A default foreign key name using the columns is used unless foreignKeyName is specified (in which case columns is ignored).

dropUnique

table.dropUnique(columns, [indexName])

Drops a unique key constraint from a table. A default unique key name using the columns is used unless indexName is specified (in which case columns is ignored).

dropPrimary

table.dropPrimary([constraintName])

Drops the primary key constraint on a table. Defaults to tablename_pkey unless constraintName is specified.

queryContext

table.queryContext(context)

Allows configuring a context to be passed to the wrapIdentifier hook for formatting table builder identifiers. The context can be any kind of value and will be passed to wrapIdentifier without modification.

knex.schema.table('users', function (table) {
+  table.queryContext({ foo: 'bar' });
+  table.string('first_name');
+  table.string('last_name');
+})
+

This method also enables overwriting the context configured for a schema builder instance via schema.queryContext:

knex.schema.queryContext('schema context')
+  .table('users', function (table) {
+    table.queryContext('table context');
+    table.string('first_name');
+    table.string('last_name');
+})
+

Note that it's also possible to overwrite the table builder context for any column in the table definition:

knex.schema.queryContext('schema context')
+  .table('users', function (table) {
+    table.queryContext('table context');
+    table.string('first_name').queryContext('first_name context');
+    table.string('last_name').queryContext('last_name context');
+})
+

Calling queryContext with no arguments will return any context configured for the table builder instance.

Chainable Methods

The following three methods may be chained on the schema building methods, as modifiers to the column.

alter

column.alter(options={[alterNullable: boolean = true, alterType: boolean = true])

Marks the column as an alter / modify, instead of the default add.

WARNING

This only works in .alterTable() and is not supported by SQlite or Amazon Redshift. Alter is not done incrementally over older column type so if you like to add notNullable and keep the old default value, the alter statement must contain both .notNullable().defaultTo(1).alter(). If one just tries to add .notNullable().alter() the old default value will be dropped. Nullable alterations are done only if alterNullable is true. Type alterations are done only if alterType is true.

knex.schema.alterTable('user', function(t) {
+  t.increments().primary(); // add
+  // drops previous default value from column,
+  // change type to string and add not nullable constraint
+  t.string('username', 35).notNullable().alter();
+  // drops both not null constraint and the default value
+  t.integer('age').alter();
+  // if alterNullable is false, drops only the default value
+  t.integer('age').alter({alterNullable : false});
+  // if alterType is false, type of column is not altered.
+  t.integer('age').alter({alterType : false});
+});
+

index

column.index([indexName], options=({[indexType: string], [storageEngineIndexType: 'btree'|'hash'], [predicate: QueryBuilder]}))

Specifies a field as an index. If an indexName is specified, it is used in place of the standard index naming convention of tableName_columnName. In MySQL, the storage engine index type may be 'btree' or 'hash' index types, more info in Index Options section : https://dev.mysql.com/doc/refman/8.0/en/create-index.html. The indexType can be optionally specified for PostgreSQL and MySQL. No-op if this is chained off of a field that cannot be indexed. In PostgreSQL, SQLite and MSSQL a partial index can be specified by setting a 'where' predicate.

primary

column.primary(options=({[constraintName:string],[deferrable:'not deferrable'|'deferred'|'immediate']}));

Sets a primary key constraint on column. Constraint name defaults to tablename_pkey unless constraintName is specified. On Amazon Redshift, all columns included in a primary key must be not nullable. Deferrable primary constraint are supported on Postgres and Oracle and can be set by passing deferrable option to options object.

knex.schema.table('users', function (table) {
+  table.integer('user_id').primary('email',{constraintName:'users_primary_key',deferrable:'deferred'})
+})
+

INFO

If you want to create primary constraint on existing column use primary

unique

column.unique(options={[indexName:string],[deferrable:'not deferrable'|'immediate'|'deferred']})

Sets the column as unique. On Amazon Redshift, this constraint is not enforced, but it is used by the query planner. Deferrable unique constraint are supported on Postgres and Oracle and can be set by passing deferrable option to options object.

knex.schema.table('users', function (table) {
+  table.integer('user_id').unique({indexName:'user_unique_id', deferrable:'immediate'})
+})
+

INFO

If you want to create unique constraint on existing column use unique

references

column.references(column)

Sets the "column" that the current column references as a foreign key. "column" can either be "." syntax, or just the column name followed up with a call to inTable to specify the table.

inTable

column.inTable(table)

Sets the "table" where the foreign key column is located after calling column.references.

onDelete

column.onDelete(command)

Sets the SQL command to be run "onDelete".

onUpdate

column.onUpdate(command)

Sets the SQL command to be run "onUpdate".

defaultTo

column.defaultTo(value, options={[constraintName: string = undefined]))

Sets the default value for the column on an insert.

In MSSQL a constraintName option may be passed to ensure a specific constraint name:

column.defaultTo('value', { constraintName: 'df_table_value' });
+

unsigned

column.unsigned()

Specifies a number as unsigned. Only for numeric values.

notNullable

column.notNullable()

Adds a not null on the current column being created.

nullable

column.nullable()

Default on column creation, this explicitly sets a field to be nullable.

first

column.first()

Sets the column to be inserted on the first position, only used in MySQL alter tables.

after

column.after(field)

Sets the column to be inserted after another, only used in MySQL alter tables.

comment

column.comment(value)

Sets the comment for a column.

knex.schema.createTable('accounts', function(t) {
+  t.increments().primary();
+  t.string('email').unique().comment('This is the email field');
+});
+

collate

column.collate(collation)

Sets the collation for a column (only works in MySQL). Here is a list of all available collations: https://dev.mysql.com/doc/refman/5.5/en/charset-charsets.html

knex.schema.createTable('users', function(t) {
+  t.increments();
+  t.string('email').unique().collate('utf8_unicode_ci');
+});
+

View

columns

view.columns([columnNames])

Specify the columns of the view.

knex.schema.createView('users_view', function (view) {
+  view.columns(['first_name', 'last_name']);
+  view.as(knex('users').select('first_name').where('age','>', '18'));
+});
+

as

view.as(selectQuery)

Specify the select query of the view.

checkOption

view.checkOption()

Add check option on the view definition. On OracleDb, MySQL, PostgreSQL and Redshift.

localCheckOption

view.localCheckOption()

Add local check option on the view definition. On MySQL, PostgreSQL and Redshift.

cascadedCheckOption

view.cascadedCheckOption()

Add cascaded check option on the view definition. On MySQL, PostgreSQL and Redshift.

Checks

check

table.check(checkPredicate, [bindings], [constraintName]))

Specify a check on table or column with raw predicate.

knex.schema.createTable('product', function (table) {
+  table.integer('price_min');
+  table.integer('price');
+  table.check('?? >= ??', ['price', 'price_min']);
+})
+

checkPositive

column.checkPositive([constraintName])

Specify a check on column that test if the value of column is positive.

knex.schema.createTable('product', function (table) {
+  table.integer('price').checkPositive();
+})
+

checkNegative

column.checkNegative([constraintName])

Specify a check on column that test if the value of column is negative.

knex.schema.createTable('product', function (table) {
+  table.integer('price_decrease').checkNegative();
+})
+

checkIn

column.checkIn(values, [constraintName])

Specify a check on column that test if the value of column is contained in a set of specified values.

knex.schema.createTable('product', function (table) {
+  table.string('type').checkIn(['table', 'chair', 'sofa']);
+})
+

checkNotIn

column.checkNotIn(values, [constraintName])

Specify a check on column that test if the value of column is not contains in a set of specified values.

knex.schema.createTable('product', function (table) {
+  table.string('type').checkNotIn(['boot', 'shoe']);
+})
+

checkBetween

column.checkBetween(values, [constraintName])

Specify a check on column that test if the value of column is within a range of values.

knex.schema.createTable('product', function (table) {
+  table.integer('price').checkBetween([0, 100]);
+})
+// You can add checks on multiple intervals
+knex.schema.createTable('product', function (table) {
+  table.integer('price').checkBetween([ [0, 20], [30,40] ]);
+})
+

checkLength

column.checkLength(operator, length, [constraintName])

Specify a check on column that test if the length of a string match the predicate.

knex.schema.createTable('product', function (table) {
+  // operator can be =, !=, <=, >=, <, >
+  t.varchar('phone').checkLength('=', 8);
+})
+

checkRegex

column.checkRegex(regex, [constraintName])

Specify a check on column that test if the value match the specified regular expression. In MSSQL only simple pattern matching in supported but not regex syntax.

knex.schema.createTable('product', function (table) {
+  table.string('phone').checkRegex('[0-9]{8}');
+  // In MSSQL, {8} syntax don't work,
+  // you need to duplicate [0-9].
+  table.string('phone').checkRegex('[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]');
+})
+

dropChecks

table.dropChecks([checkConstraintNames])

Drop checks constraint given an array of constraint names.

knex.schema.createTable('product', function (table) {
+  table.integer('price').checkPositive('price_check')
+  table.integer('price_proportion').checkBetween([0, 100],'price_proportion_check')
+  table.dropChecks(['price_check', 'price_proportion_check']);
+})
+
+ + + + + \ No newline at end of file diff --git a/guide/transactions.html b/guide/transactions.html new file mode 100644 index 00000000..f718f3a7 --- /dev/null +++ b/guide/transactions.html @@ -0,0 +1,199 @@ + + + + + + Transactions | Knex.js + + + + + + + + + + + + +

Transactions

Transactions are an important feature of relational databases, as they allow correct recovery from failures and keep a database consistent even in cases of system failure. All queries within a transaction are executed on the same database connection, and run the entire set of queries as a single unit of work. Any failure will mean the database will rollback any queries executed on that connection to the pre-transaction state.

Transactions are handled by passing a handler function into knex.transaction. The handler function accepts a single argument, an object which may be used in two ways:

  1. As the "promise aware" knex connection
  2. As an object passed into a query with transacting and eventually call commit or rollback.

Consider these two examples:

// Using trx as a query builder:
+knex.transaction(function(trx) {
+
+  const books = [
+    {title: 'Canterbury Tales'},
+    {title: 'Moby Dick'},
+    {title: 'Hamlet'}
+  ];
+
+  return trx
+    .insert({name: 'Old Books'}, 'id')
+    .into('catalogues')
+    .then(function(ids) {
+      books.forEach((book) => book.catalogue_id = ids[0]);
+      return trx('books').insert(books);
+    });
+})
+.then(function(inserts) {
+  console.log(inserts.length + ' new books saved.');
+})
+.catch(function(error) {
+  // If we get here, that means that 
+  // neither the 'Old Books' catalogues insert,
+  // nor any of the books inserts will have taken place.
+  console.error(error);
+});
+

And then this example:

// Using trx as a transaction object:
+knex.transaction(function(trx) {
+
+  const books = [
+    {title: 'Canterbury Tales'},
+    {title: 'Moby Dick'},
+    {title: 'Hamlet'}
+  ];
+
+  knex.insert({name: 'Old Books'}, 'id')
+    .into('catalogues')
+    .transacting(trx)
+    .then(function(ids) {
+      books.forEach((book) => book.catalogue_id = ids[0]);
+      return knex('books').insert(books).transacting(trx);
+    })
+    .then(trx.commit)
+    .catch(trx.rollback);
+})
+.then(function(inserts) {
+  console.log(inserts.length + ' new books saved.');
+})
+.catch(function(error) {
+  // If we get here, that means that 
+  // neither the 'Old Books' catalogues insert,
+  // nor any of the books inserts will have taken place.
+  console.error(error);
+});
+

Same example as above using await/async:

try {
+  await knex.transaction(async trx => {
+
+    const books = [
+      {title: 'Canterbury Tales'},
+      {title: 'Moby Dick'},
+      {title: 'Hamlet'}
+    ];
+    
+    const ids = await trx('catalogues')
+      .insert({
+        name: 'Old Books'
+      }, 'id')
+
+    books.forEach((book) => book.catalogue_id = ids[0])
+    const inserts = await trx('books').insert(books)
+    
+    console.log(inserts.length + ' new books saved.')
+  })
+} catch (error) {
+  // If we get here, that means that neither the 'Old Books' catalogues insert,
+  // nor any of the books inserts will have taken place.
+  console.error(error);
+}
+

Same example as above using another await/async approach:

try {
+  await knex.transaction(async trx => {
+
+    const books = [
+      {title: 'Canterbury Tales'},
+      {title: 'Moby Dick'},
+      {title: 'Hamlet'}
+    ];
+
+    const ids = await knex('catalogues')
+      .insert({
+        name: 'Old Books'
+      }, 'id')
+      .transacting(trx)
+
+    books.forEach(book => book.catalogue_id = ids[0])
+    await knex('books')
+      .insert(books)
+      .transacting(trx)
+
+    console.log(inserts.length + ' new books saved.')
+  })
+} catch (error) {
+  console.error(error);
+}
+

Throwing an error directly from the transaction handler function automatically rolls back the transaction, same as returning a rejected promise.

Notice that if a promise is not returned within the handler, it is up to you to ensure trx.commit, or trx.rollback are called, otherwise the transaction connection will hang.

Calling trx.rollback will return a rejected Promise. If you don't pass any argument to trx.rollback, a generic Error object will be created and passed in to ensure the Promise always rejects with something.

Note that Amazon Redshift does not support savepoints in transactions.

In some cases you may prefer to create transaction but only execute statements in it later. In such case call method transaction without a handler function:

// Using trx as a transaction object:
+const trx = await knex.transaction();
+
+const books = [
+  {title: 'Canterbury Tales'},
+  {title: 'Moby Dick'},
+  {title: 'Hamlet'}
+];
+
+trx('catalogues')
+  .insert({name: 'Old Books'}, 'id')
+  .then(function(ids) {
+    books.forEach((book) => book.catalogue_id = ids[0]);
+    return trx('books').insert(books);
+  })
+  .then(trx.commit)
+  .catch(trx.rollback);
+

If you want to create a reusable transaction instance, but do not want to actually start it until it is used, you can create a transaction provider instance. It will start transaction after being called for the first time, and return same transaction on subsequent calls:

// Does not start a transaction yet
+const trxProvider = knex.transactionProvider();
+
+const books = [
+  {title: 'Canterbury Tales'},
+  {title: 'Moby Dick'},
+  {title: 'Hamlet'}
+];
+
+// Starts a transaction
+const trx = await trxProvider();
+const ids = await trx('catalogues')
+  .insert({name: 'Old Books'}, 'id')
+books.forEach((book) => book.catalogue_id = ids[0]);
+await trx('books').insert(books);
+
+// Reuses same transaction
+const sameTrx = await trxProvider();
+const ids2 = await sameTrx('catalogues')
+  .insert({name: 'New Books'}, 'id')
+books.forEach((book) => book.catalogue_id = ids2[0]);
+await sameTrx('books').insert(books);
+

You can access the promise that gets resolved after transaction is rolled back explicitly by user or committed, or rejected if it gets rolled back by DB itself, when using either way of creating transaction, from field executionPromise:

const trxProvider = knex.transactionProvider();
+const trx = await trxProvider();
+const trxPromise = trx.executionPromise;
+
+const trx2 = await knex.transaction();
+const trx2Promise = trx2.executionPromise;
+
+const trxInitPromise = new Promise(async (resolve, reject) => {
+  knex.transaction((transaction) => {
+    resolve(transaction);
+  });
+});
+const trx3 = await trxInitPromise;
+const trx3Promise = trx3.executionPromise;
+

You can check if a transaction has been committed or rolled back with the method isCompleted:

const trx = await knex.transaction();
+trx.isCompleted(); // false
+await trx.commit();
+trx.isCompleted(); // true
+
+const trx2 = knex.transactionProvider();
+await trx2.rollback();
+trx2.isCompleted(); // true
+

You can check the property knex.isTransaction to see if the current knex instance you are working with is a transaction.

Transaction Modes

In case you need to specify an isolation level for your transaction, you can use a config parameter isolationLevel. Not supported by oracle and sqlite, options are read uncommitted, read committed, repeatable read, snapshot (mssql only), serializable.

// Simple read skew example
+const isolationLevel = 'read committed';
+const trx = await knex.transaction({isolationLevel});
+const result1 = await trx(tableName).select();
+await knex(tableName).insert({ id: 1, value: 1 });
+const result2 = await trx(tableName).select();
+await trx.commit();
+// result1 may or may not deep equal result2 depending on isolation level
+

You may also set the transaction mode as read only using the readOnly config parameter. It is currently only supported on mysql, postgres, and redshift.

const trx = await knex.transaction({ readOnly: true });
+// 💥 Cannot `INSERT` while inside a `READ ONLY` transaction
+const result = await trx(tableName).insert({ id: 1, foo: 'bar' });
+
+ + + + + \ No newline at end of file diff --git a/guide/utility.html b/guide/utility.html new file mode 100644 index 00000000..79acff5e --- /dev/null +++ b/guide/utility.html @@ -0,0 +1,47 @@ + + + + + + Utility | Knex.js + + + + + + + + + + + + +

Utility

A collection of utilities that the knex library provides for convenience.

batchInsert

knex.batchInsert(tableName)

The batchInsert utility will insert a batch of rows wrapped inside a transaction (which is automatically created unless explicitly given a transaction using transacting), at a given chunkSize.

It's primarily designed to be used when you have thousands of rows to insert into a table.

By default, the chunkSize is set to 1000.

BatchInsert also allows for returning values and supplying transactions using transacting.

const rows = [{/*...*/}, {/*...*/}];
+const chunkSize = 30;
+knex.batchInsert('TableName', rows, chunkSize)
+  .returning('id')
+  .then(function(ids) { /*...*/ })
+  .catch(function(error) { /*...*/ });
+
+knex.transaction(function(tr) {
+  return knex.batchInsert('TableName', rows, chunkSize)
+    .transacting(tr)
+  })
+  .then(function() { /*...*/ })
+  .catch(function(error) { /*...*/ });
+

now

knex.fn.now(precision)

Return the current timestamp with a precision (optional)

table.datetime('some_time', { precision: 6 }).defaultTo(knex.fn.now(6))
+

uuid

knex.fn.uuid()

Return a uuid generation function. Not supported by Redshift

table.uuid('uuid').defaultTo(knex.fn.uuid())
+

uuidToBin

knex.fn.uuidToBin(uuid)

Convert a string uuid (char(36)) to a binary uuid (binary(16))

knex.schema.createTable('uuid_table', (t) => {
+  t.uuid('uuid_col_binary', { useBinaryUuid: true });
+});
+knex('uuid_table').insert({
+  uuid_col_binary:  knex.fn.uuidToBin('3f06af63-a93c-11e4-9797-00505690773f'),
+});
+

binToUuid

knex.fn.binToUuid(binaryUuid)

Convert a binary uuid (binary(16)) to a string uuid (char(36))

const res = await knex('uuid_table').select('uuid_col_binary');
+knex.fn.binToUuid(res[0].uuid_col_binary)
+
+ + + + + \ No newline at end of file diff --git a/hashmap.json b/hashmap.json new file mode 100644 index 00000000..dddaee3a --- /dev/null +++ b/hashmap.json @@ -0,0 +1 @@ +{"changelog.md":"997a0d36","faq_index.md":"98b8e957","faq_recipes.md":"71463d9b","faq_support.md":"3a43ae3b","guide_extending.md":"c10412b7","guide_index.md":"895872e8","guide_interfaces.md":"da877da3","guide_migrations.md":"8f3715e4","guide_query-builder.md":"c2c99abe","guide_raw.md":"bcfb9bab","guide_ref.md":"0b35ab7a","guide_schema-builder.md":"1a118d1d","guide_transactions.md":"1e8e7cd5","guide_utility.md":"bc42b63a","index.md":"0854ee54"} diff --git a/index.html b/index.html new file mode 100644 index 00000000..3b17b382 --- /dev/null +++ b/index.html @@ -0,0 +1,24 @@ + + + + + + SQL Query Builder for Javascript | Knex.js + + + + + + + + + + + + +
Logo knex

Knex.js

SQL query builder

Knex.js (pronounced /kəˈnɛks/) is a "batteries included" SQL query builder for PostgreSQL, CockroachDB, MSSQL, MySQL, MariaDB, SQLite3, Better-SQLite3, Oracle, and Amazon Redshift designed to be flexible, portable, and fun to use.

It features both traditional node style callbacks as well as a promise interface for cleaner async flow control, a stream interface, full-featured query and schema builders, transaction support (with savepoints), connection pooling and standardized responses between different query clients and dialects.

+ + + + + \ No newline at end of file diff --git a/knex-logo.png b/knex-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..eb2e902072626c2d387bab12bf5d169b22ef06b5 GIT binary patch literal 9640 zcmcI~cT`i`)^7-eYUoXh6r~eF=-to+q!*=^5JKoJ^ddbXmvON1>fP6uGxs-f?p}J1BCSO6rR0da0tEoHc`eP{zS}Cdgn{q^tw? zZ6!{HKsh{sJIWWq8R+ijfsqSTsPs3XuI~R#>hAsz zHO5!nAJ644dH-8rj7g9e3TlMHc>4JuQR@CE58s=AGjl-xGuO+{$L(L5Iv}AaHu?q zcm`ku{z&xK1e6-W7p2IJ_nH_)R08iZ6H!Sy2?;rIn2@NLoT%u(Np421T^z?B@;N!&E9f3wcy*$v|oc~)IRZllhA6-ue zyv@b`>wPU%RRbSSM`t(u0LDl|g;PsiRSG66B?S=^f&B@4ycl?OHJved9|!&QAQ_{) z|GIK>=KNQ5${~<{9tuTn9c3 z#y&vv`4hK!jggxi9Mq%$r`lV@)B#s*V{Y1(jYKx5c9Rj9mpxB;BJ=@9zl}h%83K_K z=1+kq-&(>oC}!m&9(FlSW>@p< zP16y%MDl8n7|bW?ov(D+*S(gUs3F>snANZZWGELcpMGd0_(dGIPokGV2iU4-Aq>5K zf}x4v+xtxrO%%ye^k$m1x4h@6W-0_0@#B#;cVe9)C!D&JOVjD`O=tq3qS&1a8~jzt zIhE&lKAnh?mdGVp{va84M^s%?qcEc@cz#KGF^rIy6etN$9F(SmK6d^o*aODCj-)CK zacAR*d4svPg=>u#CR5hpVpUH^5|fz|H|pJnW;Yt_!`tdcCDU)Qk%Xtm zR^r=VelC*haFPxy;CYZP*gFwqdA+x?OYG3ZPeumUJ!*vxu>CW*aptCQp%wt+bUL%#xuRCI_CNdr@t9qo7Hd46F3JS`UqUh;fc?vX}Z-Xf94i+JXG} z7q${Jw?>@>S|JGX8brc;FA)JPU@R{&D*S%KI#9Z+nBosn`<96Qc5IzI`uUUFcdJ4PdfVln)Rr$BHvN@G{o-fZK zo-4&m3^uynJ(ddkt*x=q-y3L*8<4GKml_R^ABcadV9eRnSZ1CV4VUH%e~COJ|aepfF=sw z5c@=~R$6`ry-H@)qRQ8*co9@C5Jkg1y&wSK#ySPlaa@ za_wY^g@ju(A{nBcx0J~1?QFK^Q?$vZ`v3;4y85#Z51)zX5rU#ba?jiJ=JMdNB{8mz zTzi^sy+uIeFc7&Cv98#bGYz~!o@V#UB;JhcOF<);^L`_EOdZFf)-vXXp+R0-D26-V zH511^lAw=XuB~+k&`6r&Pai3#a0x1%q^yAg22tzkdE>&*KOB$MUJtvhb%$#}xuT+9 zq2KcDn&a@nQ>?mqkQ-!9Ei_4dU>5ym_ukpm=&)tvP_k7ITlceum7zug`Hz%stxe%K zJ=^$ZYv5$ADB)rp_{neyn525^hckpt{9(^H*6gEepR0`QeFRmE?z)n3@a0H4CHCD@pVSZX+W7?y7162nA{#3 z8M-i?eb?GL-L{#T0H=tARn+^vOcKdZvv^-ALy)B;_AG|0F)J1xTi#H2=BZcAH#E|H z5n@s;tk*l5l=Z>sIA1rth<6~qVf5D8W;cV}_b?)hmViZohr2@20ZE4D^&AyS?RU~U zd%07}yx&(;KGls-$e%}z_P4Q=SD*#LZo^-|S2LPFO;WWI?HHRnDyQ;XsVvWV6i#3F^qUMaZw4L?KwQ-M`A7Jk*v-8)T> zb8N3!)ZL{8-DX`GPjh9;*$kH@95h;`E6F3sNa#O$in+4>2@EP(bd|Z|=ez0zCkR#k6M(6fseT4cKyGTK8>x#?zCRwOqR!gi3!7 zGrD}A{}m&!qrW0A7AKiRG27K5T$oNAuI#DcMm}5CLkoBJO)~($yxXrqpVD1J^6*GE zx08|!bo9FLpk^y^y4-_q#2r+>r>vRQmA3^H zVkP6LfSYxl>-0?>bJnv3UOKq8`0awZ`$4H(6+40>Y#mS%!f*SR;ZC`oEM3x({%QtP zXLvxCHU1(FQbTy&`#ar`$;ejJ@ligZYwN|-c*{00O#%~-Q$pt%j~Nd?jXo6|RDz{3 zZ=V^%e!ppSFy`x%ms#!s#T6u6nx0`{4{M{yk3+D< z;0#{vblr3X6-e%ltYiHHjl%|EE7}e^QUY&589T4bLm8RQ((RkhtIeHlbI~0{jvsYt-rca^lO=qVVMfEmzgf;WwD6Yn z+1(&)PMeJzohnG>yxLmUP#iK$xrbk5F9a2v2Het_U5sp$2yL~T|A zVnQAA&7tSYjrFD)n6PDxcTf3dDak42jRFlCo?(t9lI8s&m=@&~niVvB8sa?fI02eK z6QiRbQKDZ{7}u8ee)>K(d;#2CQU4LD95_q3WuzP@_68Nf5P4KMyYzr84&EUpYtk3s zJWhFrrvg2x+O(jRF?K0M(&o?@ZhPIqv9~3La76bKUK*+(^MpkP$m*$d!&|I+;P7}x z7wMy%Ro#Lb8OUI=jnd3SCC^A{p_K3?!46-^W|S$wqtM2mqIG#ZpH(vAv4}46@mwri zUV;&XNn)&G0QX*6w)Z@(%DfMow^n{-6Zh;LPK`sKc~rVS?bC3qDG+qW^x8oZ8{gAH z9?)C#27T4D8}nqxUhFT_XKwAlI1U*1X`R3tv;_jmZ-HL&->nZVCy5igTBe01etv=E za4C~X{(OsH_VnC02|MiTAZ0O2AjiERd9#wGThnA|CytWfHv+?lL`(Dw!>Y$9Y!IRC@A*OxkCoQSxI?44?#7 z?+d=pXm67R3LES_uM9BsC@iTnt$Yn@9u%GhZt?vJSyv>u9pHVYU}ttmCt=iH57x)V zWnwU20iOCoelXN57I@%SeN3LWS`yNJ&$(Vh-i9LW#*_CyD}vo=bg37v8LjfOVNfgn z>%7`W`E=jw3|MMEFi-1w^ZAfq=G*t1j+&prNDq=tjo{_>gXgk~jN#a~qn19Y ztUp6H)gZnDo7g5fGuF2K!C}VT7p|@SPn|U;YOt1_CnYZ!Uu)+^!{2qj#ff)tCj`1`6sFG6Re*%{EED%w`2K)(sM zO0*kR;)YZFVqHQ)!#J)n%@1yRntgObqQa!X13FaAkg*b7|dKphL-2iYwWN zIM{lz)AW%PG9%J~vH8g^_oS1a`yr`UMFm^q=@e7$?wfze9Fc7z=7IW>99AH=s0}6e7P({HRNP@2u{Y;827Ryj&kc!>@29J>#N0EOu3u~4aRlGx80(KX zyJNtyfl1}j_r1SNE;P5>UJyLknSB6eAbynE6nl=+$aSdPwJ@{Pib?#0u>> zmB4eDTksG4&V?jF-mvBT`c6%6+9AJUBa0c97d=m#dYN zv4OH|+Wg?Q>U8sZJyT7Eg6<_pCdGh_GOBZwvx=OjmBB_m=n<*QZm3R?HUaC|d2Vna zA=!M%De%rrYL}zs5Qc_gyiV?@|K<((;_zeqniqIj!65s%vL;Pnh>CJq`^Jl|ISEKl z^tOM^IAtYc6HRMK9*~Ib=lU zsZ|?Qc79mo<&Cf+G$!(Pz0$m*XM1C3 z^-74OA!bic7ENuDihq|F@Dg&gaTIVM0jT*ZHDfm>Mu=pGuhmR5CA46t>)4$If6tgW zwQ7x@vOIo8NxX&`tQPq0-5->K-!mg_K#V?szUvA`y6Lp|2 zV^F3XD{WcAq|j1zI`ej4jZx!5;>ZKuQkYogA~pBD3&tFG^_Cyzs)xUXw>v$+nR7i>N zVN4>*zKa;hV#}uZb)hlZ!M?bLi6)p-GB$4ycR#&U8dOm~*O@-K9_=6LKbWPjEdQ=j zl7gI{Ws+D%2kgg&s^_i$HbPY|Wj)_o4klVwzV>5DK{Ckr=8c1?PLQG_ViNGloX||# z+(_lMqV`YeVL=00$I%%cuk7Ky3*?vM<5->uo*#y^vfBzVmB@4+m6&fPov?)PVY@rm z*i}q$zC~h1B8!+#*TR+wrl$r75HKd}ruZPRYc~8~r zGuwA$i1CT3suL;0@US!o=9^t)eowc)?rG(VL6f%!)*7DdPJ#?_&EFf)vBNJ7kRP!_ zJ@8n3!NR2KfNfr4LEzr1lbi*`!Sk~!cy2^0!3qu`jauLZ3d@Q z9>Y`~TW4#r`qNT~#oX>t+$cko6o9xS*ks(3N5QQ}!9_E612Cl*lDIQbxlht>FjF&$ zkl*qH9idE*XltL%rqfk@Px;hb^6sGw%X*hjwx0SEh84*TKF(_h@V$joYule0#O)?O zH?Kd4UGNK2b$t)J%J!kz9MYQjR^w5-Xz24+`}rt$=#C?;HWw?P^_G)WCshh??f44+ zs)x_+`1f$N@F(^Kv$p?eloJ;$8mTy4ZQ z@y}0h8NvIlwh0t22~Hb#dB>6(1!6zh>b7nX{2o!HiTD)u9cTPRv;b%Gv8>e@>Sb%7 zO?LB9?b+@8czQrOM~wDnj-4le?#?!>*(>8cGKz7H>zwsq*6`*OPuOj2b|n*oj3-tRN2LEU`8T7LPpu5XxF zt3k7)BhM%$g#+FV?ian@X{$%5;y}a7M$Qyy{#pYu;G32)n=N|%C-6YNBHOZi4*JD#(#hHG+6)2^wO z>V=K9trrotLth4C#YxTAOTVrAD}lg2h)9YzzG03?4|6YBSo585Mw%CqBNp$tj_ux1 z55%#?W2JtjMvX`Y5{;IL*3hx`=jO+3W=&bU3ugNiRw)h63Y>iZKugzJe9KqwgkNE! zbht{K9<32e#{FZ*s9{L%#fwp56e!=cAHN+u)Ry}4k>piuc3rsikEC^amXhY^ww3D% zbuT(eQ(VgD?TWd(MA}q{ja%2gTz<;&72kua*0Ktg=G}cEsqzq4MmE?pT*W!SBzTR! z4BLRFkX(=r@=Q%UDEDORYzZ~6I8mB!TeFl|h8!0PNbj$3g;M{L7tEzDD`|N7P3^em ziK(?}oaFSWKgGSExx%ZiXtK%rmXJ|vQj)eME{Q|t@thd8k3mn!HPp(>KE|iq3Lskn z9bUg%4Ki6YZac$F8 z-iDQOyA^o`dF`sXK$_@wA>VllYw5Od9Ybk1?oiXKg4`v^A0)lU;983L_?Uj4A2QT1 zXcp$eG@$4Qn-mb44ZXCDy;K z@++C~=a@wH7ZK!Ng_AFyeBjR8oTx({0YSO0t_BE)K0);)U*eTA_W_+A zw5x{h^H{2rZ-dDFy01@4%>!;{3!l=hy?1eEy$Rmr&xzL(@yGCdC8z@3HI%MFiR*b2 z-XlE=7D4;_PWe#PS&z87>@a}OPctur!tSp%z~}VMzc34KmUl#cw6a~{+0(#!hKcXK zmH&La1;f6{QOghfYJES@eT>Dj5k)`jd~v-Ob0@w3`iDIOj1|YuQ1nrWS$i#Z?JP_6 zwZ7}C5jLD@)(~H zx#2I+gtnEM5U<6@OJ9a(|zUtloBr|OVWze)x_~}VpkR;Y=oRR#4h2u&muU%Dqa(P0c`fJx zewCS4yYsZt?o_r4_G0S_zXN$YYFgZgdh$X6erVhenJkPZ6)b-L^5gp zRziPc5p}I+wdurnhP(WT>4BQQFE`QwH=MiqlSLv+vb+6Q$hqWA3)2nO_q9!_E+p%Q zV5LoP_Rls|g(arY;+(R5yAUlG6L$wg8INu7bFdgYhjf7D>Yn~n2FRtM1=`hfa zAJ)4IVzQVd#4zny=vI4i{S)hyP@@;n<0R8(WB*|QYTJUWscsASrQt5qIO*-I#a$GImJ<3D8 z+V&IostOg>+_%MTEbEhEp+NT<@mR(87B3*&23o1WyA|4{B+g`K-)C9qLLON-f*Xng z3(pdBgqveude6uaS&${)v!6)o&05{0UMV*AztWAe2A632nrF zNNk}uNb95X^VojB?0CHs7_4%Ef98@_!ymCEvMCbw2PdHyT}dCeH2k5;M4cGrT=E_=7dx;R*P?*t1>U(IrL8Qe-NL?!vaZ@y1$S*Fv@HO2S)~ z!#B2WjrfXD8}W`Ue~VyunhZJc1brGfq2dr24_%{##T{c`0X^S;f&*bEECBn@m3vz9iLIr z1wzZBQf+7r#AF1!i#5qM5@&rF5Bu1w=B$$!2r<}+{`{K#^~TN{cS3)Dch$Mq7Mt8A&S~_Gwn?IG4(7v7+00dp>uBgQH zi3oj;74kuo)C~9s>yYmxTea>#S0eZAZ@pCRAa-t|(i;a^krufSBnKN#_(AbK9BwT0 zM~%qzL|1pm*Bh~WjK<5NEp@<3CO=uVWbuB#C4N^?CGxcWo#rOlTZUxZGp81m4;+TF zgW4FKj@xp*oWXB^#>DuZjk92eep9_K7ohcV<-S!mkC*Kok*d)W`aAD@+>a!Pwu#6A zCY&dTMHFU=&E+xuX|P=)g-ovgxT}I>3|qOQdvOoj>p<%tk4Llu492f^2i{m3sB!|r zTm0*R0DO%&nn^#jUOy2&`N*ec+(xW&R1eB4KYe;1OhRbXJ>LKiyMya7PM~wAjBx?E zzJjsJBxM*+W#JUZY&o$EULg>|QVR5%sGGRpWbQ-uBExC-w2kq2EE|lrVF;>rI1M2t#4e`z0>;;bQ z27Ad+Q0G-Ttv?Z#hRB7qSFViDM#TC5bRHsObuiZ7UfQPYvI<&Avpo#J>Tx_DmreaQz!+MI(&KJSG;vN zJ;?ccol{SqDAFQIDoMm!hM|I>D=Gj0 literal 0 HcmV?d00001