@lunora/sql-store 1.0.0-alpha.196 → 1.0.0-alpha.197
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/createSqlCtxDb-Be29nU7z.mjs +7 -0
- package/dist/packem_shared/runSqlAggregateMigrations-BqfzHcG8.mjs +1 -0
- package/dist/packem_shared/runSqlGlobalTableMigrations-qVa5Q8BM.mjs +1 -0
- package/package.json +3 -3
- package/dist/packem_shared/createSqlCtxDb-XrP6PcFB.mjs +0 -7
- package/dist/packem_shared/runSqlAggregateMigrations-CUAkKj8T.mjs +0 -1
- package/dist/packem_shared/runSqlGlobalTableMigrations-6ORBtPAz.mjs +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createSqlCtxDb as a,readSqlCdcChangedTables as o,readSqlCdcChanges as t,readSqlCdcFloor as l,runSqlCdcMigration as s,sweepSqlCdcRetention as n}from"./packem_shared/createSqlCtxDb-
|
|
1
|
+
import{createSqlCtxDb as a,readSqlCdcChangedTables as o,readSqlCdcChanges as t,readSqlCdcFloor as l,runSqlCdcMigration as s,sweepSqlCdcRetention as n}from"./packem_shared/createSqlCtxDb-Be29nU7z.mjs";import{runSqlGlobalTableMigrations as i}from"./packem_shared/runSqlGlobalTableMigrations-qVa5Q8BM.mjs";import{runSqlAggregateMigrations as c,runSqlRankMigrations as q}from"./packem_shared/runSqlAggregateMigrations-BqfzHcG8.mjs";import{d as f,r as m}from"./packem_shared/ctx-db-search-DB_ejfgj.mjs";import{S as x,m as p}from"./packem_shared/ctx-db-search-state-DRiojLLm.mjs";import{d as u,a as h,c as M,s as T,t as A}from"./packem_shared/value-codec-CiS1h_97.mjs";import{z as R}from"./packem_shared/sql-exec-UH-aJj2C.mjs";export{x as SEARCH_STATE_TABLE,f as backfillSqlSearchIndexes,a as createSqlCtxDb,u as decodeBigint,R as decodeGlobalRow,h as effectiveColumnKind,p as migrateSearchState,o as readSqlCdcChangedTables,t as readSqlCdcChanges,l as readSqlCdcFloor,c as runSqlAggregateMigrations,s as runSqlCdcMigration,i as runSqlGlobalTableMigrations,q as runSqlRankMigrations,m as runSqlSearchMigrations,M as sqliteDecode,T as sqliteEncode,n as sweepSqlCdcRetention,A as tryJsonParse};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import{LunoraError as T}from"@lunora/errors";import{m as Zt,D as en}from"./ctx-db-search-state-DRiojLLm.mjs";import{c as tn,r as nn,A as on,C as rn,R as sn,u as an,a as cn,T as dn,x as ln,b as un}from"./ctx-db-search-DB_ejfgj.mjs";import{throwingScheduler as fn,describeRelationWrites as wn,CDC_LOG_TABLE as K,cdcForkedError as $n,cursorBelowRetainedFloor as hn,cdcTrimmedError as _n,serverInList as En,assertNoExplicitUndefined as Be,hasTrigger as pn,runRowValidators as We,assertFlatPredicate as at,mergeWhere as W,rankTableName as he,sortColumnName as pe,resolveRankPartition as ct,encodePartitionKey as Ce,resolveRankSeekTuple as yn,RANK_TIEBREAK as ye,CountRlsUnsupportedError as dt,compileWhereSql as Q,ConflictError as oe,patchDependsOnRow as Rn,normalizeIdStructurally as Sn,applyInsertDefaults as Tn,assertValidClientId as gn,aggregateSqlFunction as _e,softDeleteScope as Le,resolveWith as lt,relationHooks as ut,applySelect as ft,encodeCursor as vn,NotFoundError as mn,applyOnDelete as An,normalizeCountArgument as In,selectIndexForCount as Cn,encodeAggregateKey as z,aggregateTableName as te,selectIndexForAggregate as Ln,readAggregateValue as Fe,runTriggers as bn,matchesRankStaticWhere as wt,nullsOrderSuffix as $t,nullsFirstOf as ht,rankPivotConditionSql as Mt,CURSOR_PREFIX as Nn,resolvePatch as Mn,stripReservedPatchFields as qn,assertPatchAllowed as kn,resolveRelationPredicates as Dn,selectIndexForGroupBy as On,composeReadPredicate as Bn,fanOutScalarCounts as Wn,matchesStaticWhere as xe,foldAggregateTally as _t,coerceAggregateNumber as Ue,whereOfFilter as Fn,isPushableWhere as xn,tiebreakDirectionFor as Un,CDC_LOG_TABLE_SEQ_INDEX as Pn,NotUniqueError as Hn,WORKERD_SQLITE_LIMITS as Kn}from"@lunora/shard-engine";import{decodeWire as qt,needsWireEncoding as kt,encodeWire as Dt}from"@lunora/wire";import{sql as e,SQL as jn}from"drizzle-orm";import{p as Et,m as pt,q as A,a as q,c as Gn,o as Vn,f as Y,s as ne,O as Ee,b as U,h as yt,t as Yn,j as Pe,u as Rt,l as St,B as He,v as be,n as Qe,e as Tt,w as gt,x as vt,y as Xn}from"./sql-exec-UH-aJj2C.mjs";import{z as Yi}from"./sql-exec-UH-aJj2C.mjs";import{runSqlGlobalTableMigrations as Qn}from"./runSqlGlobalTableMigrations-qVa5Q8BM.mjs";import{runSqlAggregateMigrations as zn,runSqlRankMigrations as Jn}from"./runSqlAggregateMigrations-BqfzHcG8.mjs";import{c as Ot,a as Bt}from"./value-codec-CiS1h_97.mjs";const Zn=(a,c)=>{if(a.size<c)return;const $=a.keys().next().value;$!==void 0&&a.delete($)},mt=50,ei=(a,c={})=>{const $=c.maxBatch??mt,r=Number.isInteger($)&&$>0?$:mt,R=new Map;let w=!1;const d=async(g,m)=>{const I=[...m.keys()];for(let C=0;C<I.length;C+=r){const b=I.slice(C,C+r);try{const B=await a(g,b);for(const V of b){const X=B.get(V);for(const J of m.get(V)??[])J.resolve(X)}}catch(B){for(const V of b)for(const X of m.get(V)??[])X.reject(B)}}},M=()=>{w=!1;const g=[...R.entries()];R.clear();for(const[m,I]of g)d(m,I).catch(()=>{})};return{async load(g,m){return new Promise((I,C)=>{let b=R.get(g);b||(b=new Map,R.set(g,b));const B=b.get(m);B?B.push({reject:C,resolve:I}):b.set(m,[{reject:C,resolve:I}]),w||(w=!0,queueMicrotask(M))})}}},ti=new Set(["_id","id"]),ni=a=>{throw new T("INTERNAL",`no ORDER BY NULL placement for dialect "${String(a)}"`)},Wt=(a,c)=>{const $=c.direction==="desc"?"DESC":"ASC",r=c.nullable===!0,R=w=>e`${be(a,c.field)} ${e.raw(`${$}${w}`)}`;switch(a.name){case"mysql":{const w=R("");return!r||c.nulls===void 0?[w]:[e`(${be(a,c.field)} IS NULL) ${e.raw(c.nulls==="first"?"DESC":"ASC")}`,w]}case"postgres":return[R(r?$t(ht(c)):"")];case"sqlite":return[R(r&&c.nulls!==void 0?$t(ht(c)):"")];default:return ni(a.name)}},ii=(a,c)=>{const $=a.flatMap(r=>Wt(c,r));if(!a.some(r=>ti.has(r.field))){const r=Un(a);$.push(e`${U("id")} ${e.raw(r==="desc"?"DESC":"ASC")}`)}return e.join($,e`, `)},At=50,oi=a=>{const c=a.detail??a["QUERY PLAN"];return typeof c=="string"?c:Object.values(a).map(String).join(" | ")},Ke=(a,c,$,r)=>{for(const[R,w]of Yn(a)){if(w.serverDefault){R in c&&($[R]=w.serverDefault({auth:r}));continue}w.onUpdateFn&&!(R in c)&&($[R]=w.onUpdateFn())}},ri=128,si=()=>{const a=new Map;return{delete:c=>{a.delete(c)},get:c=>{const $=a.get(c);if($!==void 0)return a.delete(c),a.set(c,$),$},set:(c,$)=>{a.has(c)?a.delete(c):Zn(a,ri),a.set(c,$)}}},ai=async(a,c,$,r,R)=>{const w=R.get(r);if(w!==void 0)return w;const d=Object.entries($.tables).filter(([,g])=>g.shardMode===void 0||g.shardMode.kind==="global").map(([g])=>g),M=await Promise.all(d.map(async g=>({found:(await A(a,c,e`SELECT 1 FROM ${e.identifier(g)} WHERE ${e.identifier("id")} = ${r} LIMIT 1`)).length>0,tableName:g})));for(const g of M)if(g.found)return R.set(r,g.tableName),g.tableName},re=a=>a==null?null:Number(a),ci=(a,c,$)=>$.map(r=>{const R={};for(const w of c){const d=a.shape[w];R[w]=Ot(r[w]??null,d&&Bt(d))}return{key:R,value:re(r.value)}}),di=(a,c,$)=>[...a.partitionBy??[],...a.sortBy.map(R=>R.field),...a.where?Object.keys(a.where):[]].every(R=>gt(c,R)===gt($,R)),Ft=a=>a.length===1?a[0]:e`(${e.join(a,e` AND `)})`,ie=a=>e.join(a.map(c=>e`${e.identifier(c)}`),e`, `),se=a=>e.join(a.map(c=>e`${c}`),e`, `),je=(a,c)=>{if(a==="postgres"){for(const[$,r]of Object.entries(c))if(typeof r=="string"&&r.includes("\0"))throw new T("BAD_REQUEST",`"${$}" holds a NUL character (U+0000), which a Postgres text column cannot store`)}},Ne=a=>a instanceof ArrayBuffer?new Uint8Array(a):Array.isArray(a)?Uint8Array.from(a):a,li=(a,c,$,r,R)=>{const w=[];for(let d=0;d<$.length+1;d+=1){const M=$[d],g=c.sortBy[d],m=d<$.length&&M!==void 0&&g!==void 0,I=g?.direction==="desc"?"desc":"asc",C=m?Mt(M,Ne(r[M]),I,!1):e`${e.identifier(ye)} < ${R}`;if(C===void 0)continue;const b=$.slice(0,d).map(B=>Qe(a,e`${e.identifier(B)}`,Ne(r[B])));b.push(C),w.push(Ft(b))}return w.length>0?e.join(w,e` OR `):e`1 = 0`},ui=(a,c,$)=>{if($.length!==c.length)return;const r=[];for(const[R,w]of c.entries()){const d=Mt(w.column,Ne($[R]),w.direction,!0);if(d===void 0)continue;const M=[];for(let g=0;g<R;g+=1){const m=c[g];m!==void 0&&M.push(Qe(a,e`${e.identifier(m.column)}`,Ne($[g])))}M.push(d),r.push(Ft(M))}return r.length===0?e`(1 = 0)`:e`(${e.join(r,e` OR `)})`},fi=(a,c)=>{if(a.sortBy.length===0)throw new T("INTERNAL",`rankIndex "${a.name}" requires at least one "sortBy" column for stable pagination`);const $=[{column:"__partition__",direction:"asc",nullable:!1}];for(const[r,R]of a.sortBy.entries())$.push({column:c[r]??pe(r),direction:R.direction,nullable:!0});return $.push({column:ye,direction:"asc",nullable:!1}),$},wi=async(a,c,$,r,R)=>{const d=[];for(let I=0;I<R.length;I+=50)d.push(R.slice(I,I+50));const M=await Promise.all(d.map(async I=>A(a,c,e`SELECT * FROM ${e.identifier(r)} WHERE ${e.identifier("id")} IN (${se(I)})`))),g=new Map;for(const I of M)for(const C of I)g.set(C.id,C);const m=[];for(const I of R){const C=Y($,g.get(I));C&&m.push(C)}return m},$i=a=>{const c=a.map(w=>Ot(w,"bytes")),$=JSON.stringify(kt(c)?Dt(c):c),r=new TextEncoder().encode($);let R="";for(const w of r)R+=String.fromCodePoint(w);return Nn+btoa(R)},xt=a=>{if(a===void 0)return!1;const c=Bt(a);return c==="any"||c==="bigint"||c==="from"||c==="union"},Ye=(a,c,$)=>{if(xt(a.shape[c]))throw new T("BAD_REQUEST",`${$}: "${c}" may hold an order-preserving key rather than a value SQL can reduce or group — declare an aggregateIndex covering this (by, field, op) so the maintained companion answers it instead (its running total is a REAL, so it stays exact only while the total is inside 2^53)`)},hi=(a,c,$,r)=>{for(const R of $)Ye(a,R,`groupBy(${c}, { by: [..."${R}"] })`);r.field!==void 0&&Ye(a,r.field,`groupBy(${c}, { agg: { op: "${r.op}", field: "${r.field}" } })`)},Xe="__cdc_sweep",Ut="global",_i=6e4,Ei=1e4,pi=3e4;let It=0;const yi=async(a,c)=>{const{autoincrementPrimaryKey:$,key:r,real:R,text:w}=c.companionTypes;await q(a,c,e`CREATE TABLE IF NOT EXISTS ${e.identifier(K)} (${e.identifier("seq")} ${e.raw($)}, ${e.identifier("ts")} ${e.raw(R)} NOT NULL, ${e.identifier("table")} ${e.raw(r)} NOT NULL, ${e.identifier("id")} ${e.raw(r)} NOT NULL, ${e.identifier("op")} ${e.raw(r)} NOT NULL, ${e.identifier("doc")} ${e.raw(w)})`);const d=c.indexKeyPrefix?.("string");await Gn(a,c,{columns:e`${d===void 0?e`${e.identifier("table")}`:e`${e.identifier("table")}(${e.raw(String(d))})`}, ${e.identifier("seq")}`,name:Pn,table:K,unique:!1}),await q(a,c,e`CREATE TABLE IF NOT EXISTS ${e.identifier(Xe)} (${e.identifier("id")} ${e.raw(r)} NOT NULL PRIMARY KEY, ${e.identifier("lease_until")} ${e.raw(R)} NOT NULL)`),await q(a,c,e`INSERT INTO ${e.identifier(Xe)} (${e.identifier("id")}, ${e.identifier("lease_until")}) VALUES (${Ut}, ${0}) ${c.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${e.identifier("id")} = ${e.identifier("id")}`:e`ON CONFLICT(${e.identifier("id")}) DO NOTHING`}`)},Ri=async(a,c,$)=>{const r=e`UPDATE ${e.identifier(Xe)} SET ${e.identifier("lease_until")} = ${$+_i} WHERE ${e.identifier("id")} = ${Ut} AND ${e.identifier("lease_until")} <= ${$}`;if(c.supportsReturning)return(await A(a,c,e`${r} RETURNING ${e.identifier("id")}`)).length>0;const R=await q(a,c,r);return(c.affectedRows?c.affectedRows(R??{rowsAffected:0}):0)>0},Si=async(a,c,$,r)=>{if(!await Ri(a,c,r))return;const R=r-$;await q(a,c,e`
|
|
2
|
+
DELETE FROM ${e.identifier(K)} WHERE ${e.identifier("seq")} IN (
|
|
3
|
+
SELECT ${e.identifier("seq")} FROM (
|
|
4
|
+
SELECT ${e.identifier("seq")} FROM ${e.identifier(K)} WHERE ${e.identifier("ts")} <= ${R} ORDER BY ${e.identifier("seq")} ASC LIMIT ${e.raw(String(Ei))}
|
|
5
|
+
) AS ${e.identifier("expired")}
|
|
6
|
+
)
|
|
7
|
+
`)},Pt=async(a,c)=>{const $=await A(a,c,e`SELECT MIN(${e.identifier("seq")}) AS ${e.identifier("seq")} FROM ${e.identifier(K)}`),r=Number($[0]?.seq??Number.NaN);return Number.isFinite(r)?r:void 0},Ti=async(a,c)=>{if(c.name!=="sqlite")return;const $=await A(a,c,e`SELECT ${e.identifier("seq")} AS ${e.identifier("seq")} FROM ${e.identifier("sqlite_sequence")} WHERE ${e.identifier("name")} = ${K}`),r=Number($[0]?.seq??Number.NaN);return Number.isFinite(r)?r:0},gi=a=>JSON.stringify(kt(a)?Dt(a):a),vi=async(a,c,$,r,R,w,d)=>{await q(a,d,e`INSERT INTO ${e.identifier(K)} (${e.identifier("ts")}, ${e.identifier("table")}, ${e.identifier("id")}, ${e.identifier("op")}, ${e.identifier("doc")}) VALUES (${c}, ${$}, ${r}, ${R}, ${w===void 0?null:gi(w)})`)},Ki=async(a,c,$)=>{const r=c.sinceSeq??0,R=Math.max(1,Math.min(c.limit??1e3,1e4)),w=await Pt(a,$),d=await Ti(a,$);if(d!==void 0&&r>d)throw $n(d,r,"global");if(w!==void 0&&hn(w,r))throw _n(w,r,"global");const g=(await A(a,$,e`SELECT seq, ts, ${e.identifier("table")}, id, op, doc FROM ${e.identifier(K)} WHERE seq > ${r} ORDER BY seq ASC LIMIT ${e.raw(String(R))}`)).map(m=>{const{doc:I}=m,C={id:String(m.id),op:String(m.op),seq:Number(m.seq),table:String(m.table),ts:Number(m.ts)};return typeof I=="string"?{...C,doc:qt(JSON.parse(I))}:C});return{changes:g,cursor:g.at(-1)?.seq??r}},mi=1e4,Ai=1e3,Ct=(a,c)=>{const $=e.identifier(K),r=e`(SELECT MIN(v.seq) FROM ${$} v WHERE v.seq > ${a} AND v.seq - 1 > ${a} AND v.ts > ${c} AND NOT EXISTS (SELECT 1 FROM ${$} p WHERE p.seq = v.seq - 1))`;return e`${r} AS ${e.identifier("gap")}, (SELECT MAX(q.seq) FROM ${$} q WHERE q.seq > ${a} AND q.seq < ${r}) AS ${e.identifier("below")}`},Lt=(a,c,$)=>{const r=Number(c?.gap??Number.NaN);if(!Number.isFinite(r))return a;const R=Number(c?.below??Number.NaN);return Math.min(a,Number.isFinite(R)?R:$)},Ii=async(a,c,$,r={})=>{const R=e.identifier(K),w=$.name!=="sqlite",d=(r.now??Date.now())-mi;if(r.cursorOnly){const C=e`GREATEST((SELECT MAX(seq) FROM ${R}) - ${e.raw(String(Ai))}, ${c})`,b=await A(a,$,e`SELECT MAX(seq) AS seq${w?e`, ${Ct(C,d)}`:e``} FROM ${R}`),B=Number(b[0]?.seq??c);return{cursor:Lt(Number.isFinite(B)?B:c,b[0],c),tables:[]}}const M=await A(a,$,e`SELECT ${e.identifier("table")} AS ${e.identifier("table")}, MAX(seq) AS seq${w?e`, ${Ct(e`${c}`,d)}`:e``} FROM ${R} WHERE seq > ${c} GROUP BY ${e.identifier("table")}`);let g=c;const m=[];for(const C of M){const b=Number(C.seq);Number.isFinite(b)&&b>g&&(g=b),m.push(String(C.table))}g=Lt(g,M[0],c);const I=r.retained===!0?await Pt(a,$):void 0;return{cursor:g,tables:m,...I===void 0?{}:{floor:I}}},Ci=/string or blob too big/iu,Li=/row is too big/iu,bi=(a,c)=>{const{code:$,errno:r}=c,R=c instanceof Error?c.message:"";switch(a.name){case"mysql":return r===1118||$==="ER_TOO_BIG_ROWSIZE"?"InnoDB's per-row ceiling — roughly 8 KB, half a 16 KB page, for the part of the row stored inline":void 0;case"postgres":return Li.test(R)?"the 8 KB heap page a tuple must fit once its wide columns have been TOASTed out":void 0;default:return Ci.test(R)?"the storage engine's per-row ceiling (2 MB on D1)":void 0}},Ni=(a,c,$)=>{const r=bi(a,c);if(r!==void 0)throw new T("PAYLOAD_TOO_LARGE",`document is too large to store in "${$}": a single row cannot exceed ${r}. The limit is on the STORED bytes, which are UTF-8. Keep the payload in R2 (ctx.storage) and store a reference on the row.`)},Ge=16,bt="__lunora_patch_if_match__",Nt=(a,c,$)=>{throw a.isUniqueViolation(c)?new oe(`unique constraint violation on "${$}"`,"unique"):(Ni(a,c,$),c)},Ve=new WeakMap,Mi=a=>{const c=En(a);return($,r,R,w)=>r.some(d=>d instanceof jn)?Vn(a,$,r,R):c($,r,R,w)},ji=a=>{const{crossShardCounter:c,crossShardReader:$,exec:r,maxRelationKeys:R,schema:w}=a,{dialect:d}=a,M={fieldRef:t=>be(d,t),rangeTypeGuard:(t,n)=>Xn(d,t,n),...d.name==="mysql"?{containsExpr:(t,n)=>{const i=Et(t);return i===void 0?e`LOCATE(${n}, ${t}) > 0`:e`LOCATE(${pt("mysql",n)}, ${i}) > 0`}}:{},...d.name==="postgres"?{containsExpr:(t,n)=>{const i=Et(t);return i===void 0?e`strpos(${t}, ${n}) > 0`:e`strpos(${i}, ${pt("postgres",n)}) > 0`}}:{},...d.name==="sqlite"?{}:{inList:Mi(d.name)}},g=new WeakMap,m=t=>{const n=g.get(t);if(n!==void 0)return n;const i={...M,serialize:(s,o)=>vt(d,o,s,l=>ne(t,o,l))};return g.set(t,i),i},I=(t,n)=>Qe(d.name,t,n),C=t=>{const n=ie(t.columns),i=se(t.values),s=h=>d.name==="mysql"?e`VALUES(${e.identifier(h)})`:e`excluded.${e.identifier(h)}`,o=h=>d.name==="postgres"?e`${e.identifier(t.table)}.${e.identifier(h)}`:e`${e.identifier(h)}`,l=e.join(Object.entries(t.set(s,o)).map(([h,p])=>e`${e.identifier(h)} = ${p}`),e`, `),E=d.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${l}`:e`ON CONFLICT(${e.identifier(t.conflictKey)}) DO UPDATE SET ${l}`;return e`INSERT INTO ${e.identifier(t.table)} (${n}) VALUES (${i}) ${E}`},b=a.clock??(()=>Date.now()),B=a.idGenerator??(()=>crypto.randomUUID()),V=a.cdc??!1,X=typeof a.cdcRetentionMs=="number"&&Number.isFinite(a.cdcRetentionMs)&&a.cdcRetentionMs>0?a.cdcRetentionMs:void 0,J=a.auth??{identity:null,userId:null},ae=async(t,n,i,s)=>{if(!V||(await vi(r,b(),t,n,i,s,d),X===void 0))return;const o=b();if(!(o-It<pi)){It=o;try{await Si(r,d,X,o)}catch{}}},Ht=a.scheduler??fn,Me=si();let Re=0;const qe=a.provisionScope??{},F=async()=>{const t=Ve.get(qe);if(t)return t;const n=(async()=>{await Qn(r,w,d),await zn(r,w,d),await Jn(r,w,d),await Zt(r,d),await nn(r,w,d),V&&await yi(r,d)})().catch(i=>{throw Ve.delete(qe),i});return Ve.set(qe,n),n},ce=async(t,n)=>{await F();const i=await ai(r,d,w,t,Me);if(!(n!==void 0&&i!==n))return i},de=new Map,ze=async(t,n)=>{const i=te(t,n);return(await A(r,d,d.tableExists(i))).length>0},Se=async(t,n)=>{const i=`${t}::${n.name}`,s=de.get(i);if(s!==void 0)return s;if(!await ze(t,n.name))return de.set(i,!1),!1;const l=w.tables[t];if(!l)return de.set(i,!1),!1;const E=n.by??[],h=new Map;await St(r,d,l,t,_=>{if(n.where&&!xe(_,n.where))return;const f=z(E,_);_t(h,f,n,_)});const p=te(t,n.name);await q(r,d,e`DELETE FROM ${e.identifier(p)}`);const u=[];for(const[_,f]of h)u.push(e`INSERT INTO ${e.identifier(p)} (${e.identifier("__key__")}, ${e.identifier("__value__")}, ${e.identifier("__count__")}) VALUES (${_}, ${f.value}, ${f.count})`);return await Tt(r,d,u),de.set(i,!0),!0},Kt=async(t,n,i,s)=>{const o=new Map;let l,E=!0;for(;E;){const h=l===void 0?s:[...s,e`${e.identifier("id")} > ${l}`],p=h.length>0?e` WHERE ${e.join(h,e` AND `)}`:e``,u=await A(r,d,e`SELECT ${e.identifier("id")}, ${U(i.field??"")} FROM ${e.identifier(n)}${p} ORDER BY ${e.identifier("id")} ASC LIMIT ${e.raw(String(He))}`);for(const _ of yt(t,u))_t(o,"",i,_);l=u.at(-1)?.id,E=u.length===He}return o.get("")?.value??null},jt=async(t,n,i)=>{const s=n.field??"",o=[],l=w.tables[t],E=(_,f)=>vt(d,_,f,S=>l?ne(l,_,S):Pe(S));for(const _ of n.by??[]){const f=E(_,i[_]??null);o.push(f===null?e`${U(_)} IS NULL`:e`${U(_)} = ${f}`)}for(const[_,f]of Object.entries(n.where??{})){const S=f!==null&&typeof f=="object"&&!Array.isArray(f)?f.eq:f,y=E(_,S),v=be(d,_);o.push(y===null?e`${v} IS NULL`:e`${v} = ${y}`)}const h=o.length>0?e` WHERE ${e.join(o,e` AND `)}`:e``;if(l&&xt(l.shape[s]))return Kt(l,t,n,o);const p=_e(n.op),u=await A(r,d,e`SELECT ${e.raw(p)}(${U(s)}) AS ${e.identifier("value")} FROM ${e.identifier(t)}${h}`);return re(u[0]?.value)},Gt=async(t,n)=>{await q(r,d,e`DELETE FROM ${e.identifier(t)} WHERE ${e.identifier("__key__")} = ${n} AND ${e.identifier("__count__")} <= 0`)},Vt=async(t,n,i,s)=>{const o=te(t,n.name),{op:l}=n,E=n.field??"",h=i&&(!n.where||xe(i,n.where))?i:void 0,p=s&&(!n.where||xe(s,n.where))?s:void 0;if(!(!h&&!p)){if(l==="count"||l==="sum"||l==="avg"){const u=new Set;for(const[_,f]of[[h,-1],[p,1]]){if(!_)continue;const S=l==="count"?1:Ue(_[E]);if(S===void 0)continue;const y=z(n.by??[],_);u.add(y),await q(r,d,C({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(v,L)=>({__count__:e`${L("__count__")} + ${v("__count__")}`,__value__:l==="count"?e`${L("__value__")} + ${v("__value__")}`:e`COALESCE(${L("__value__")}, 0) + ${v("__value__")}`}),table:o,values:[y,f*S,f]}))}for(const _ of u)await Gt(o,_);return}if(h){const u=z(n.by??[],h),_=Ue(h[E]),S=(await A(r,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(o)} WHERE ${e.identifier("__key__")} = ${u}`))[0],y=re(S?.value),v=(S?.count??0)-1;if(v<=0)await q(r,d,e`DELETE FROM ${e.identifier(o)} WHERE ${e.identifier("__key__")} = ${u}`);else if(S&&_!==void 0&&y!==null&&_===y){const L=await jt(t,n,h);await q(r,d,e`UPDATE ${e.identifier(o)} SET ${e.identifier("__value__")} = ${L}, ${e.identifier("__count__")} = ${v} WHERE ${e.identifier("__key__")} = ${u}`)}else await q(r,d,e`UPDATE ${e.identifier(o)} SET ${e.identifier("__count__")} = ${e.identifier("__count__")} - 1 WHERE ${e.identifier("__key__")} = ${u}`)}if(p){const u=z(n.by??[],p),_=Ue(p[E]);if(_===void 0)await q(r,d,C({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(f,S)=>({__count__:e`${S("__count__")} + 1`}),table:o,values:[u,null,1]}));else{const f=l==="min"?"MIN":"MAX";await q(r,d,C({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(S,y)=>({__count__:e`${y("__count__")} + 1`,__value__:e`${e.raw(f)}(COALESCE(${y("__value__")}, ${S("__value__")}), ${S("__value__")})`}),table:o,values:[u,_,1]}))}}}},Te=async t=>{const n=w.tables[t]?.aggregateIndexes;if(!(!n||n.length===0))for(const i of n)await Se(t,i)},le=async(t,n,i)=>{const s=w.tables[t]?.aggregateIndexes;if(!(!s||s.length===0))for(const o of s){const l=`${t}::${o.name}`;(de.get(l)??await ze(t,o.name))&&await Vt(t,o,n,i)}},ue=new Map,Je=async(t,n)=>{const i=he(t,n);return(await A(r,d,d.tableExists(i))).length>0},ke=async(t,n)=>{const i=`${t}::rank::${n.name}`,s=ue.get(i);if(s!==void 0)return s;if(!await Je(t,n.name))return ue.set(i,!1),!1;const l=w.tables[t];if(!l)return ue.set(i,!1),!1;const E=he(t,n.name);await q(r,d,e`DELETE FROM ${e.identifier(E)}`);const h=n.sortBy.map((f,S)=>pe(S)),p=ie(["__id__","__partition__",...h]);let u=[];const _=async()=>{if(u.length===0)return;const f=u;u=[],await Tt(r,d,f)};return await St(r,d,l,t,async f=>{if(n.where&&!wt(f,n.where))return;const S=Ce(n.partitionBy??[],f),y=n.sortBy.map(v=>Pe(f[v.field]??null));u.push(e`INSERT INTO ${e.identifier(E)} (${p}) VALUES (${se([f._id,S,...y])})`),u.length>=He&&await _()}),await _(),ue.set(i,!0),!0},ge=async t=>{const n=w.tables[t]?.rankIndexes;if(!(!n||n.length===0))for(const i of n)await ke(t,i)},Z=async(t,n,i,s)=>{const o=w.tables[t],l=o?.rankIndexes;if(!l||l.length===0)return;const E=o?.softDeleteMode?.field,h=E===void 0||s===void 0?void 0:s[E],p=h!=null;for(const u of l){if(i!==void 0&&s!==void 0&&di(u,i,s))continue;const _=`${t}::rank::${u.name}`;if(!(ue.get(_)??await Je(t,u.name)))continue;const y=he(t,u.name);if(i&&await q(r,d,e`DELETE FROM ${e.identifier(y)} WHERE ${e.identifier("__id__")} = ${n}`),s&&!p){if(u.where&&!wt(s,u.where))continue;const v=u.sortBy.map((D,x)=>pe(x)),L=ie(["__id__","__partition__",...v]),N=Ce(u.partitionBy??[],s),k=u.sortBy.map(D=>Pe(s[D.field]??null));await q(r,d,e`INSERT INTO ${e.identifier(y)} (${L}) VALUES (${se([n,N,...k])})`)}}},fe=tn({dialect:d,exec:r,schema:w}),Ze=new Set;for(const[t,n]of Object.entries(w.tables))for(const i of Object.values(n.triggerMap??{}))Ze.add(`${t} ${i.timing} ${i.op}`);const j=(t,n,i)=>Ze.has(`${t} ${n} ${i}`);let et;const G=async(t,n,i)=>{if(Re+=1,Re>At)throw Re-=1,new oe(`trigger recursion exceeded ${String(At)} levels on "${i.table}" — check for a self-triggering write`,"trigger");try{await bn({ctx:et,event:i,op:n,schema:w,tableName:i.table,timing:t})}finally{Re-=1}},Yt=async(t,n)=>{try{await q(r,d,n)}catch(i){Nt(d,i,t)}},ve=async(t,n)=>(await A(r,d,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} = ${n}`))[0],tt=async(t,n,i,s,o)=>{const l=e`${e.identifier(Ee)}`,E=e.join([I(e`${e.identifier("id")}`,s.id),I(l,s[Ee]??null),...o===void 0?[]:[e`(${o})`]],e` AND `),h=n==="UPDATE"?e`UPDATE ${e.identifier(t)} SET ${i}, ${l} = ${Rt.sql(l)} WHERE ${E}`:e`DELETE FROM ${e.identifier(t)} WHERE ${E}`;try{if(d.supportsReturning)return(await A(r,d,e`${h} RETURNING ${e.identifier("id")}`)).length>0;const p=await q(r,d,h);return(d.affectedRows?d.affectedRows(p??{rowsAffected:0}):0)>0}catch(p){return Nt(d,p,t)}},De=async(t,n,i,s)=>{if(s!==void 0&&!await tt(t,n,i,s))throw new oe(`optimistic concurrency conflict on "${t}" — the row changed during this mutation; refetch and retry`,"occ")},Oe=(t,n=t?._creationTime)=>({_creationTime:n,[Ee]:Rt.value(t?.[Ee])}),nt=async(t,n,i,s,o,l,E)=>{const h=Y(n,o);if(!h||o===void 0)throw new T("INTERNAL",`document not found: ${i}`);const p=Mn(n,s,h),u={...h,...qn(p),_id:i};Ke(n,p,u,J);const _=We(n,u,{only:new Set(Object.keys(p)),previous:()=>h,tolerateStoredNull:!0});je(d.name,Object.fromEntries(Object.keys(p).map(y=>[y,_[y]]))),kn(E,h,_),j(t,"before","update")&&await G("before","update",{doc:{..._},id:i,op:"update",previous:h,table:t}),await Te(t),await ge(t);const f=Object.keys(n.shape),S=e.join(f.map(y=>e`${e.identifier(y)} = ${ne(n,y,_[y]??null)}`),e`, `);if(await tt(t,"UPDATE",S,o,l))return await le(t,h,_),await Z(t,i,h,_),await fe(t,i,{document:_,previous:h,written:Oe(o)}),await ae(t,i,"update",_),j(t,"after","update")&&await G("after","update",{doc:_,id:i,op:"update",previous:h,table:t}),_},Xt=(t,n,i,s)=>{const o=Object.keys(t.shape);return{columns:["id","_creationTime",...o],values:[n,i,...o.map(l=>ne(t,l,s[l]??null))]}},Qt=async(t,n,i,s)=>{const o=On(n,i.op,i.field,s.by,s.where);if(!o||!await Se(t,o.index))return;const E=te(t,o.index.name),h=Object.keys(o.partial);if(h.length===(o.index.by??[]).length&&h.length>0){const u=z(o.index.by??[],o.partial),_=await A(r,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(E)} WHERE ${e.identifier("__key__")} = ${u}`);if(_.length===0)return[];const f=_[0];return[{key:{...o.partial},value:Fe(i.op,{count:f.count,value:re(f.value)})}]}return h.length>0?void 0:(await A(r,d,e`SELECT ${e.identifier("__key__")} AS ${e.identifier("key")}, ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(E)}`)).map(u=>{const _=u;return{key:qt(JSON.parse(_.key)),value:Fe(i.op,{count:_.count,value:re(_.value)})}})},it=t=>{const n=w.tables[t]?.shardMode?.kind;return n==="shardBy"||n==="root"},ot=t=>{throw new T("INTERNAL",`cross-backend relation: a global table cannot load the shard-local relation '${t}' (it spans every shard) — wire a cross-shard reader to support it`)},zt=(t,n)=>{if(n?.relationMask!==void 0&&n.with!==void 0&&Object.keys(n.with).length>0)throw new T("MASK_UNSUPPORTED",`masking cannot follow a nested \`with\` across the cross-shard relation hop to "${t}" — the nested rows are hydrated on the serving shard, where the mask policy does not exist. Read the nested relation at its own (masked) call site instead.`);const i={};if(n?.relationBaseWhere)for(const s of Object.keys(w.tables)){const o=n.relationBaseWhere(s);o!==void 0&&(i[s]=o)}return{orderBy:n?.orderBy,relationPolicies:i,where:W(n?.baseWhere,n?.where),with:n?.with}},me=(t,n)=>it(t)?$?$(t,zt(t,n)):ot(t):P.findMany(t,n),Ae=(t,n,i)=>Dn(t,{fetcher:me,maxRelationKeys:R,relationBaseWhere:i,schema:w,tableName:n}),Jt=ei(async(t,n)=>{const i=await A(r,d,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} IN (${se(n)})`),s=new Map;for(const o of i){const{id:l}=o;typeof l=="string"&&s.set(l,o)}return s}),rt=async(t,n,i)=>{const{fetchLimit:s,limit:o,orderKeys:l,predicate:E}=await Bn(n,i,{fetcher:me,maxRelationKeys:R,schema:w,tableName:t}),h=Q(E,m(n)),p=ii(l,d);let u=e`SELECT * FROM ${e.identifier(t)}`;return h&&(u=e`${u} WHERE ${h}`),u=e`${u} ORDER BY ${p}`,s!==void 0&&(u=e`${u} LIMIT ${e.raw(String(s))}`),{limit:o,orderKeys:l,query:u}},P={relationWrites:wn(w),async cdcChangedTables(t,n){if(V)try{return await F(),await Ii(r,t,d,{...n,now:b(),retained:X!==void 0})}catch{return}},async aggregate(t,n){const i=w.tables[t];if(!i)throw new T("INTERNAL",`unknown table: ${t}`);if(await F(),_e(n.op),n.op==="count")return P.count(t,{baseWhere:n.baseWhere,relationBaseWhere:n.relationBaseWhere,restrictsCounts:n.restrictsCounts,where:n.where});if(!n.field)throw new T("INTERNAL",`aggregate(${t}, { op: "${n.op}" }): "field" is required for non-count reducers`);const s=Le(i.softDeleteMode,void 0),o=W(W(n.baseWhere,n.where),s),l=await Ae(o,t,n.relationBaseWhere),E=l!==o;if(i.aggregateIndexes&&!n.baseWhere&&!E&&!s){const S=Ln(i.aggregateIndexes,n.op,n.field,n.where);if(S&&await Se(t,S.index)){const v=z(S.index.by??[],S.key),L=te(t,S.index.name),k=(await A(r,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(L)} WHERE ${e.identifier("__key__")} = ${v}`))[0];return Fe(n.op,k===void 0?void 0:{count:k.count,value:re(k.value)})}}Ye(i,n.field,`aggregate(${t}, { op: "${n.op}", field: "${n.field}" })`);const h=Q(l,m(i)),p=e.raw(_e(n.op)),u=e`SELECT ${p}(${U(n.field)}) AS ${e.identifier("value")} FROM ${e.identifier(t)}`,f=(await A(r,d,h?e`${u} WHERE ${h}`:u))[0]?.value;return f==null?null:Number(f)},async count(t,n){const i=w.tables[t];if(!i)throw new T("INTERNAL",`unknown table: ${t}`);await F();const s=In(n);if(s.restrictsCounts)throw new dt(t);const o=Le(i.softDeleteMode,void 0),l=W(W(s.baseWhere,s.where),o),E=await Ae(l,t,s.relationBaseWhere),h=E!==l;if(i.aggregateIndexes&&!s.baseWhere&&!h&&!o){const f=Cn(i.aggregateIndexes,s.where);if(f&&await Se(t,f.index)){const y=z(f.index.by??[],f.key),v=te(t,f.index.name),L=await A(r,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")} FROM ${e.identifier(v)} WHERE ${e.identifier("__key__")} = ${y}`);return Number(L[0]?.value??0)}}const p=Q(E,m(i)),u=e`SELECT COUNT(*) AS ${e.identifier("count")} FROM ${e.identifier(t)}`,_=await A(r,d,p?e`${u} WHERE ${p}`:u);return Number(_[0]?.count??0)},async delete(t,n,i){const s=await ce(t,n);if(!s)return;const o=w.tables[s];if(!o)return;const l=await ve(s,t),E=Y(o,l),h=i?.hard===!0,p=!h&&o.softDeleteMode?o.softDeleteMode.field:void 0;if(!(p&&(!E||E[p]!==null&&E[p]!==void 0))){if(j(s,"before","delete")&&await G("before","delete",{id:t,op:"delete",previous:E??void 0,table:s}),await An({deletedId:t,deletedReference:u=>E?.[u],findHolders:async(u,_,f)=>{if(w.tables[u]?.shardMode?.kind==="shardBy")throw new T("INTERNAL",`cross-backend cascade from global '${s}' into shardBy '${u}' is not supported — would require Query Coordinator fan-out across shards`);return(await P.findMany(u,{includeDeleted:h,where:{[_]:f}})).page},onCascade:(u,_)=>P.delete(_,void 0,i),onRestrict:u=>{throw new oe(u,"restrict")},onSetNull:(u,_,f)=>P.patch(_,{[f]:null}),schema:w,tableName:s}),await Te(s),await ge(s),p&&E){const u={[p]:b()},_={...E,...u};Ke(o,u,_,J);const f=e.join(Object.keys(o.shape).map(S=>e`${e.identifier(S)} = ${ne(o,S,_[S]??null)}`),e`, `);await De(s,"UPDATE",f,l),await le(s,E,_),await Z(s,t,E,void 0),await fe(s,t,{document:_,previous:E,written:Oe(l)}),await ae(s,t,"update",_),j(s,"after","delete")&&await G("after","delete",{id:t,op:"delete",previous:E,table:s});return}await De(s,"DELETE",void 0,l),Me.delete(t),await le(s,E??void 0,void 0),await Z(s,t,E??void 0,void 0),await fe(s,t,void 0),await ae(s,t,"delete"),j(s,"after","delete")&&await G("after","delete",{id:t,op:"delete",previous:E??void 0,table:s})}},async findFirst(t,n={}){return(await P.findMany(t,{...n,limit:1})).page[0]??null},async findFirstOrThrow(t,n={}){const i=await P.findFirst(t,n);if(i===null)throw new mn(`findFirstOrThrow: no "${t}" document matched`);return i},async findMany(t,n={}){const i=w.tables[t];if(!i)throw new T("INTERNAL",`unknown table: ${t}`);await F();const s=async(S,y,v,L)=>{if(it(S))return c?Wn(c,S,y,v,L):ot(S);const N=w.tables[S];if(!N)throw new T("INTERNAL",`unknown table: ${S}`);const k=Le(N.softDeleteMode,void 0),D={[y]:{in:v}},x=W(W(D,L),k),we=await Ae(x,S,void 0),Ie=Q(we,m(N)),$e=U(y);let H=e`SELECT ${$e} AS __fk__, COUNT(*) AS ${e.identifier("count")} FROM ${e.identifier(S)}`;Ie&&(H=e`${H} WHERE ${Ie}`),H=e`${H} GROUP BY ${$e}`;const O=await A(r,d,H),ee=new Map;for(const st of O)ee.set(st.__fk__,Number(st.count??0));return ee},{limit:o,orderKeys:l,query:E}=await rt(t,i,n),h=await A(r,d,E),p=yt(i,h);if(o===void 0)return n.with&&await lt({fetcher:me,groupedCounter:s,parents:p,...ut(n),schema:w,tableName:t,with:n.with}),{continueCursor:null,isDone:!0,page:ft(p,n.select,n.with)};const u=p.length>o,_=u?p.slice(0,o):p,f=_.at(-1);return n.with&&await lt({fetcher:me,groupedCounter:s,parents:_,...ut(n),schema:w,tableName:t,with:n.with}),{continueCursor:u&&f?vn(f,l):null,isDone:!u,page:ft(_,n.select,n.with)}},async explain(t,n={}){const i=w.tables[t];if(!i)throw new T("INTERNAL",`unknown table: ${t}`);await F();const{query:s}=await rt(t,i,n),o=d.name==="sqlite"?"EXPLAIN QUERY PLAN":"EXPLAIN",l=await A(r,d,e`${e.raw(o)} ${s}`),{params:E,sql:h}=d.render(s);return{params:E,plan:l.map(p=>oi(p)),sql:h}},async get(t,n){const i=await ce(t,n);if(!i)return null;const s=w.tables[i];return s?Y(s,await Jt.load(i,t)):null},async groupBy(t,n){const i=w.tables[t];if(!i)throw new T("INTERNAL",`unknown table: ${t}`);await F();const s=n.agg??{op:"count"};if(_e(s.op),s.op!=="count"&&!s.field)throw new T("INTERNAL",`groupBy(${t}, { agg: { op: "${s.op}" } }): "field" is required for non-count reducers`);const o=Le(i.softDeleteMode,void 0),l=W(W(n.baseWhere,n.where),o),E=await Ae(l,t,n.relationBaseWhere),h=E!==l;if(i.aggregateIndexes&&!n.baseWhere&&!h&&!o){const y=await Qt(t,i.aggregateIndexes,s,n);if(y!==void 0)return y}hi(i,t,n.by,s);const p=Q(E,m(i)),u=n.by.map(y=>e`${U(y)} AS ${e.identifier(y)}`);if(s.op==="count")u.push(e`COUNT(*) AS ${e.identifier("value")}`);else{if(!s.field)throw new T("INTERNAL",`groupBy(${t}, { agg: { op: "${s.op}" } }): "field" is required for non-count reducers`);u.push(e`${e.raw(_e(s.op))}(${U(s.field)}) AS ${e.identifier("value")}`)}const _=e.join(n.by.map(y=>U(y)),e`, `);let f=e`SELECT ${e.join(u,e`, `)} FROM ${e.identifier(t)}`;p&&(f=e`${f} WHERE ${p}`),f=e`${f} GROUP BY ${_}`;const S=await A(r,d,f);return ci(i,n.by,S)},async insert(t,n,i){const s=w.tables[t];if(!s)throw new T("INTERNAL",`unknown table: ${t}`);await F();const o=We(s,Tn(s,n,J),{supplied:n,trusted:i?.allowExplicitId===!0});je(d.name,o);let l,E=!0;i?.clientId!==void 0?(gn(i.clientId),l=i.clientId):i?.allowExplicitId&&typeof o._id=="string"?l=o._id:(l=B(),E=!1);const h=i?.allowExplicitId&&typeof o._creationTime=="number"?o._creationTime:b(),p={...o,_creationTime:h,_id:l};j(t,"before","insert")&&await G("before","insert",{doc:{...p},id:l,op:"insert",table:t}),await Te(t),await ge(t);const{columns:u,values:_}=Xt(s,l,h,o);return await Yt(t,e`INSERT INTO ${e.identifier(t)} (${ie(u)}) VALUES (${se(_)})`),E&&Me.set(l,t),await le(t,void 0,p),await Z(t,l,void 0,p),await fe(t,l,{document:p,written:{_creationTime:h,[Ee]:null}}),await ae(t,l,"insert",p),j(t,"after","insert")&&await G("after","insert",{doc:p,id:l,op:"insert",table:t}),l},normalizeId(t,n){return Sn(w,t,n)},async patch(t,n,i,s){Be("patch",n);const o=await ce(t,i);if(!o)throw new T("INTERNAL",`document not found: ${t}`);const l=w.tables[o];if(!l)throw new T("INTERNAL",`document not found: ${t}`);const E=Rn(l,n)?Ge:1;for(let h=0;h<E;h+=1){const p=await ve(o,t);if(await nt(o,l,t,n,p,void 0,s))return}throw new oe(`optimistic concurrency conflict on "${o}" — the row changed during this mutation; refetch and retry`,"occ")},async patchIf(t,n,i){Be("patchIf",i.patch);const s=w.tables[t];if(!s)throw new T("INTERNAL",`unknown table: ${t}`);const o=Q(i.where,m(s))??e`1 = 1`;for(let l=0;l<Ge;l+=1){const[E]=await A(r,d,e`SELECT *, CASE WHEN (${o}) THEN 1 ELSE 0 END AS ${e.identifier(bt)} FROM ${e.identifier(t)} WHERE ${e.identifier("id")} = ${n}`);if(E===void 0)return{applied:!1,doc:null};const{[bt]:h,...p}=E;if(Number(h)!==1)return{applied:!1,doc:Y(s,p)??null};const u=await nt(t,s,n,i.patch,p,o,{verify:i.verify});if(u)return{applied:!0,doc:u}}throw new oe(`patchIf on "${t}" lost the row to concurrent writes ${String(Ge)} times in a row; retry the operation`,"occ")},async restore(t,n){const i=await ce(t,n);if(!i)throw new T("INTERNAL",`document not found: ${t}`);const s=w.tables[i],o=s?.softDeleteMode?.field;if(!s||!o)throw new T("INTERNAL",`ctx.db.restore: table "${i}" is not a .softDelete() table`);const l=await ve(i,t),E=l?.[o]!==null&&l?.[o]!==void 0;if(await P.patch(t,{[o]:null},n),E){const h=Y(s,l);h!==null&&await Z(i,t,void 0,{...h,[o]:null})}},query(t){const n=w.tables[t];if(!n)throw new T("INTERNAL",`unknown table: ${t}`);const i="the legacy query()/withIndex() reader is not available on the D1 (global) backend; use findMany",s=[];let o=0,l;const E=d.name!=="mysql",h={...m(n),fieldRef:f=>e`m.${U(f)}`,rangeTypeGuard:()=>{throw new T("INTERNAL","a path range is never pushed into a search")}},p=f=>s.every(S=>S(f)),u=async(f,S)=>{await F();const y=2*an+f.filters.length,v=l===void 0?void 0:Q(l,h,void 0,y),L=v!==void 0&&d.name==="sqlite"&&d.render(v).params.length+y>Kn.boundParams,N=L?void 0:v,k=o>0||L,D=await cn(r,d,n,t,N===void 0?f:{...f,scope:N},dn(k?void 0:S));!k&&S===void 0&&ln(D);const x=D.filter(we=>p(we));return S===void 0?x:x.slice(0,S)},_=f=>{const S={async*[Symbol.asyncIterator](){if(!f)throw new T("INTERNAL",i);yield*await u(f,void 0)},async collect(){if(!f)throw new T("INTERNAL",i);return u(f,void 0)},async collectWithScores(){throw new T("GLOBAL_SEARCH_SCORES_UNSUPPORTED",`collectWithScores() is not supported on \`.global()\` tables (table "${t}") — relevance scores are not yet surfaced on this backend; use .collect() instead`)},filter(y){s.push(y);const v=Fn(y);return v!==void 0&&xn(v,n.shape,E)?l=W(l,v):o+=1,S},async first(){if(!f)throw new T("INTERNAL",i);return(await u(f,1))[0]??null},order(){return S},async paginate(y){if(!f)throw new T("INTERNAL",i);const v=rn(y);return sn(await u(f,un(v)),v)},async take(y){if(!f)throw new T("INTERNAL",i);return u(f,y)},async unique(){if(!f)throw new T("INTERNAL",i);const y=await u(f,2);if(y.length>1)throw new Hn(`unique() on table "${t}" matched ${String(y.length)} documents; expected at most one`);return y[0]??null},withGeoIndex(){throw new T("INTERNAL",`geo indexes are not supported on \`.global()\` tables (table "${t}") — geospatial queries run only on sharded tables`)},withIndex(){throw new T("INTERNAL",i)},withSearchIndex(y,v){const L=(n.searchIndexes??[]).find(k=>k.name===y);if(!L)throw new T("INTERNAL",`unknown search index "${y}" on table "${t}"`);const N={definition:L,field:L.field,filters:[],hasQuery:!1,indexName:y,query:""};if(v(on(N,t,en(L.language))),!N.hasQuery)throw new T("INTERNAL",`search index "${y}" on table "${t}" requires a .search(field, query) call`);return _(N)}};return S};return _(void 0)},async rank(t,n,i){const s=w.tables[t];if(!s)throw new T("INTERNAL",`unknown table: ${t}`);const o=s.rankIndexes?.find(D=>D.name===n);if(!o)throw new T("INTERNAL",`unknown rankIndex "${n}" on table "${t}"`);if(i.restrictsCounts)throw new dt(t);if(await F(),!await ke(t,o))return null;const E=typeof i.row=="string"?i.row:i.row._id;if(!E)return null;const h=he(t,o.name),p=o.sortBy.map((D,x)=>pe(x)),u=ie(["__partition__",...p]),f=(await A(r,d,e`SELECT ${u} FROM ${e.identifier(h)} WHERE ${e.identifier("__id__")} = ${E}`))[0];if(!f)return null;const S=f.__partition__,y=W(i.baseWhere,i.where);at(y,w,t,"rank");const v=ct(o,y);if(v&&Ce(o.partitionBy??[],v)!==S)return null;const L=li(d.name,o,p,f,E),N=await A(r,d,e`SELECT COUNT(*) AS c FROM ${e.identifier(h)} WHERE ${e.identifier("__partition__")} = ${S} AND (${L})`),k=await A(r,d,e`SELECT COUNT(*) AS c FROM ${e.identifier(h)} WHERE ${e.identifier("__partition__")} = ${S}`);return{position:Number(N[0]?.c??0)+1,total:Number(k[0]?.c??0)}},async rankPage(t,n,i={}){at(W(i.baseWhere,i.where),w,t,"rankPage");const s=w.tables[t];if(!s)throw new T("INTERNAL",`unknown table: ${t}`);const o=s.rankIndexes?.find(O=>O.name===n);if(!o)throw new T("INTERNAL",`unknown rankIndex "${n}" on table "${t}"`);if(await F(),!await ke(t,o))return{continueCursor:null,isDone:!0,page:[]};const E=he(t,o.name),h=o.sortBy.map((O,ee)=>pe(ee)),p=Math.max(1,Math.min(1e3,Math.floor(i.take??100))),u=W(i.baseWhere,i.where),_=ct(o,u),f=fi(o,h),S=e.join(f.flatMap(O=>Wt(d,{direction:O.direction,field:O.column,nullable:O.nullable})),e`, `),y=[];typeof i.partitionKey=="string"?y.push(e`${e.identifier("__partition__")} = ${i.partitionKey}`):_&&y.push(e`${e.identifier("__partition__")} = ${Ce(o.partitionBy??[],_)}`);const v=yn(i);if(v!==void 0){const O=ui(d.name,f,v);O!==void 0&&y.push(O)}const L=ie([ye,"__partition__",...h]);let N=e`SELECT ${L} FROM ${e.identifier(E)}`;y.length>0&&(N=e`${N} WHERE ${e.join(y,e` AND `)}`),N=e`${N} ORDER BY ${S} LIMIT ${e.raw(String(p+1))}`;const k=await A(r,d,N),D=k.length>p,x=D?k.slice(0,p):k,we=x.map(O=>O[ye]),Ie=await wi(r,d,s,t,we);let $e=null;const H=x.at(-1);if(D&&H!==void 0){const O=[H.__partition__,...h.map(ee=>H[ee]),H[ye]];$e=$i(O)}return{continueCursor:$e,isDone:!D,page:Ie}},async replace(t,n,i,s){Be("replace",n);const o=await ce(t,i);if(!o)throw new T("INTERNAL",`document not found: ${t}`);const l=w.tables[o];if(!l)throw new T("INTERNAL",`document not found: ${t}`);const E=await ve(o,t);if(E===void 0)throw new T("INTERNAL",`document not found: ${t}`);const p=pn(w,o,"update")||(l.aggregateIndexes??[]).length>0||(l.rankIndexes??[]).length>0?Y(l,E)??void 0:void 0,u=s?.allowExplicitId&&typeof n._creationTime=="number"?n._creationTime:void 0,_=typeof E._creationTime=="number"?E._creationTime:void 0,f=u??_??b(),S={...n,_creationTime:f,_id:t};Ke(l,n,S,J);const y=We(l,S,{previous:()=>p??Y(l,E)??void 0,supplied:n,trusted:s?.allowExplicitId===!0});je(d.name,y),j(o,"before","update")&&await G("before","update",{doc:{...y},id:t,op:"update",previous:p,table:o}),await Te(o),await ge(o);const v=Object.keys(l.shape),L=e.join([e`${e.identifier("_creationTime")} = ${f}`,...v.map(N=>e`${e.identifier(N)} = ${ne(l,N,y[N]??null)}`)],e`, `);await De(o,"UPDATE",L,E),await le(o,p,y),await Z(o,t,p,y),await fe(o,t,{document:y,previous:p,written:Oe(E,f)}),await ae(o,t,"update",y),j(o,"after","update")&&await G("after","update",{doc:y,id:t,op:"update",previous:p,table:o})}};return et={db:P,scheduler:Ht},P};export{ji as createSqlCtxDb,Yi as decodeGlobalRow,Ii as readSqlCdcChangedTables,Ki as readSqlCdcChanges,Pt as readSqlCdcFloor,yi as runSqlCdcMigration,Si as sweepSqlCdcRetention};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as v}from"@lunora/errors";import{aggregateTableName as Y,rankTableName as X,sortColumnName as O,bigintSqlKey as V}from"@lunora/shard-engine";import{sql as t}from"drizzle-orm";import{e as Q,a as g,B as b,r as z,i as Z,U as J,b as nn}from"./value-codec-CiS1h_97.mjs";import{O as A,t as en,a as d,q as $,c as _,b as m,d as tn,s as on,e as rn}from"./sql-exec-UH-aJj2C.mjs";const sn=new Set(["any","null"]),D=(e,n)=>{const i=n.kindOf(e);return i!==void 0&&sn.has(i)||n.nullableOf(e)?!0:i==="union"&&(n.membersOf(e)??[]).some(o=>D(o,n))},S=e=>{const n=e._meta?.inner;return e.kind==="optional"&&n?S(n):e},R=e=>D(e,{kindOf:Q,membersOf:n=>S(n)._meta?.members,nullableOf:n=>S(n)._meta?.column?.notNull===!1}),U=(e,n,i=!1)=>n.columnType(g(e),{unique:i||e._meta?.column?.unique===!0}),k=e=>{const n=new Set;for(const i of e.indexes)i.unique===!0&&i.fields.length===1&&n.add(i.fields[0]);return n},an=(e,n,i)=>{if(i.indexKeyPrefix!==void 0)for(const o of n.indexes){if(o.unique!==!0||o.fields.length<=1)continue;const r=o.fields.filter(s=>{const a=n.shape[s];return a!==void 0&&i.indexKeyPrefix?.(g(a))!==void 0});if(r.length>0)throw new Error(`${e}.${o.name}: a composite unique index cannot cover ${r.join(", ")} in full on this engine (its key limit forces a prefix, which would constrain the prefix rather than the value). Split it into single-column unique indexes, or store those fields as a bounded type.`)}},ln=e=>e._meta?.column?.notNull===!0&&e.kind!=="optional"&&!R(e),un=(e,n,i)=>{const o=[],r=k(n);for(const[u,c]of Object.entries(n.shape)){if(!c._meta?.column)continue;const f=ln(c)?" NOT NULL":"";o.push(t`${t.identifier(u)} ${t.raw(`${U(c,i,r.has(u))}${f}`)}`)}const s=[...i.frameworkColumns().map(u=>t`${t.identifier(u.name)} ${t.raw(u.type)}`),t`${t.identifier(A)} ${t.raw(i.companionTypes.integer)}`],a=s.length+o.length,l=i.maxTableColumns;if(l!==void 0&&a>l){const u=a-1===l,c="Move one or more fields into a single object field, or split the table — either way the existing rows need a data migration (`defineMigration` + `lunora migrate up`); there is no in-place path.";throw new v("VALIDATION_ERROR",`@lunora/sql-store: global table "${e}" needs ${String(a)} columns, over this engine's ${String(l)}-column limit. ${u?`One of them is "${A}", the row version every guarded write reads, which caps declared fields at ${String(l-s.length)}. A table provisioned at ${String(l)} columns before that column existed cannot be widened to hold it. `:""}${c}`)}return t.join([...s,...o],t`, `)},x=e=>{if(e.indexKeyPrefix?.("string")===void 0)return t`${m("_creationTime")}, ${m("id")}`},M=async(e,n,i,o)=>{const r=t.join(i.refs.map(a=>t`${a} IS NOT NULL`),t` AND `);if((await $(e,n,t`SELECT ${i.columns} FROM ${t.identifier(i.table)} WHERE ${r} GROUP BY ${i.columns} HAVING COUNT(*) > 1 LIMIT 1`)).length>0)throw new v("INTERNAL",`unique index "${i.name}" on "${i.table}" ${o}: existing rows are duplicates under it. De-duplicate the table with a data migration first.`)},cn=async(e,n,i)=>{if(!i.unique){await _(e,n,i);return}try{await _(e,n,i)}catch(o){throw n.isUniqueViolation(o)&&await M(e,n,i,"cannot be created"),o}},F=e=>{const n=e.indexOf("(");return n===-1?void 0:e.slice(n,e.lastIndexOf(")")+1)},fn=(e,n)=>{const i=n.unique?t`UNIQUE `:t``;return F(e.render(t`CREATE ${i}INDEX ${t.identifier(n.name)} ON ${t.identifier(n.table)} (${n.columns})`).sql)},dn=(e,n,i)=>{const o=fn(e,i);return o!==void 0&&o!==F(n)},B=async(e,n,i)=>{if(n.name!=="sqlite")return;const r=(await $(e,n,t`SELECT sql FROM sqlite_master WHERE type = 'index' AND name = ${i.name} COLLATE NOCASE AND tbl_name = ${i.table} COLLATE NOCASE`))[0]?.sql;typeof r=="string"&&dn(n,r,i)&&(i.unique&&await M(e,n,i,"cannot be re-created with its new column list; the previous index is left in place"),await d(e,n,t`DROP INDEX IF EXISTS ${t.identifier(i.name)}`))},mn=(e,n,i)=>{an(e,n,i);const o=k(n),r=l=>{const u=m(l),c=n.shape[l],f=c&&i.indexKeyPrefix&&c._meta?.column?.unique!==!0&&!o.has(l)?i.indexKeyPrefix(g(c)):void 0;return f===void 0?u:t`${u}(${t.raw(String(f))})`},s=x(i),a=[];for(const l of n.indexes){const u=l.fields.map(E=>r(E)),c=t.join(u,t`, `),f=l.unique??!1;a.push({columns:f||s===void 0?c:t`${c}, ${s}`,kind:"declared",name:`${e}_${l.name}`,refs:u,table:e,unique:f})}s!==void 0&&a.push({columns:s,kind:"default-order",name:`${e}__by_creation`,refs:[],table:e,unique:!1});for(const[l,u]of en(n)){if(!u.unique)continue;const c=r(l);a.push({columns:c,kind:"unique-column",name:`${e}_unique_${l}`,refs:[c],table:e,unique:!0})}return a},$n=/^\s*create\s+unique\s/iu,T="__lunora_framework_indexes",En=()=>t`CREATE TABLE IF NOT EXISTS ${t.identifier(T)} (name TEXT PRIMARY KEY COLLATE NOCASE, table_name TEXT NOT NULL)`,P=(e,n)=>t`INSERT OR IGNORE INTO ${t.identifier(T)} (name, table_name) VALUES (${e}, ${n})`,w=e=>e.replaceAll(/[A-Z]/gu,n=>n.toLowerCase()),gn=async(e,n,i)=>{if(n.name!=="sqlite"||(await $(e,n,t`SELECT 1 AS present FROM sqlite_master WHERE type = 'table' AND name = ${T} COLLATE NOCASE`)).length>0)return;await d(e,n,En());const r=x(n),s=r===void 0?void 0:`, ${n.render(r).sql})`;for(const a of i){const l=`${w(a)}_`,u=await $(e,n,t`SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ${a} COLLATE NOCASE AND sql IS NOT NULL`);for(const c of u){const f=String(c.name),E=String(c.sql);w(f).startsWith(l)&&w(f)!==w(`${a}__by_creation`)&&($n.test(E)||s!==void 0&&E.trimEnd().endsWith(s))&&await d(e,n,P(f,a))}}},pn=(e,n,i)=>{if(e.name!=="sqlite")return[];const o=new Set(n.map(r=>w(r.name)));return i.filter(r=>!o.has(w(r)))},wn=async(e,n,i,o)=>{if(o.name!=="sqlite")return;const r=await $(e,o,t`SELECT "l"."name" FROM ${t.identifier(T)} AS "l" WHERE "l"."table_name" = ${n} COLLATE NOCASE`);for(const s of pn(o,i,r.map(a=>String(a.name))))await d(e,o,t`DROP INDEX IF EXISTS ${t.identifier(s)}`),await d(e,o,t`DELETE FROM ${t.identifier(T)} WHERE ${t.identifier(T)}."name" = ${s}`)},Tn=async(e,n,i,o)=>{const r=mn(n,i,o);for(const s of r){if(s.kind==="default-order"){await _(e,o,s);continue}s.kind==="declared"&&await B(e,o,s),await cn(e,o,s),o.name==="sqlite"&&await d(e,o,P(s.name,s.table))}await wn(e,n,r,o)},N="__lunora_migration_state",hn=async(e,n)=>{await d(e,n,t`CREATE TABLE IF NOT EXISTS ${t.identifier(N)} (${t.identifier("name")} ${t.raw(n.companionTypes.key)} PRIMARY KEY)`)},_n=async(e,n,i)=>(await $(e,n,t`SELECT ${t.identifier("name")} FROM ${t.identifier(N)} WHERE ${t.identifier("name")} = ${i}`)).length>0,Ln=async(e,n,i,o,r,s)=>{r>0&&s!==void 0&&console.warn(`[@lunora/sql-store] storage migration "${i}" on "${o}": ${String(r)} row(s) ${s} and were left as stored — range filters and ORDER BY over them stay wrong until they are corrected by hand.`);try{await d(e,n,t`INSERT INTO ${t.identifier(N)} (${t.identifier("name")}) VALUES (${i})`)}catch(a){if(!n.isUniqueViolation(a))throw a}},yn=100,An=(e,n)=>{const i=[],o=[];for(const r of n.fields){const s=e[r],a=n.convert(s);a!==void 0&&(i.push(t`${m(r)} = ${a}`),o.push(t`${m(r)} = ${s}`))}return{assignments:i,guards:o}},Sn=e=>t.join(e.fields.map(n=>e.probe(n)),t` OR `),j=async(e,n,i,o)=>{if(o.fields.length===0||(await hn(e,i),await _n(e,i,o.marker)))return;const r=Sn(o),s=t.join([m("id"),...o.fields.map(u=>m(u))],t`, `);let a="",l=0;for(;;){const u=await $(e,i,t`SELECT ${s} FROM ${t.identifier(n)} WHERE (${r}) AND ${m("id")} > ${a} ORDER BY ${m("id")} LIMIT ${t.raw(String(yn))}`);if(u.length===0){await Ln(e,i,o.marker,n,l,o.describeUnconvertible);return}const c=[];for(const E of u){const{id:p}=E,{assignments:h,guards:I}=typeof p=="string"?An(E,o):{assignments:[],guards:[]};if(h.length===0){l+=1;continue}c.push(t`UPDATE ${t.identifier(n)} SET ${t.join(h,t`, `)} WHERE ${m("id")} = ${p} AND ${t.join(I,t` AND `)}`)}await rn(e,i,c);const f=u.at(-1)?.id;if(typeof f!="string")return;a=f}},C=e=>`'${e}%'`,On=e=>Object.entries(e.shape).filter(([,n])=>n._meta?.column!==void 0&&g(n)==="bigint").map(([n])=>n),Rn=e=>Object.entries(e.shape).filter(([,n])=>n._meta?.column!==void 0&&Z(g(n))).map(([n])=>n),K=(e,n)=>({convert:i=>{if(!(typeof i!="string"||i.length===b))try{return V(BigInt(i))}catch{return}},describeUnconvertible:"hold a value the order-preserving key cannot represent (non-numeric text, or a magnitude past 39 digits)",fields:On(n),marker:`bigint-key:${e}`,probe:i=>t`(${m(i)} IS NOT NULL AND LENGTH(${m(i)}) <> ${b})`}),Nn=async(e,n,i,o)=>j(e,n,o,K(n,i)),G=(e,n)=>({convert:z,fields:Rn(n),marker:`untyped-number-key:${e}`,probe:i=>t`(${m(i)} LIKE ${t.raw(C(J))} AND ${m(i)} NOT LIKE ${t.raw(C(nn))})`}),In=async(e,n,i,o)=>j(e,n,o,G(n,i)),q=new Set,L=(e,n)=>{q.has(e)||(q.add(e),console.warn(`[@lunora/sql-store] ${n}`))},bn=e=>{const n=e._meta?.column;return n?.defaultFn?n.defaultFn():n&&"defaultValue"in n?n.defaultValue:void 0},Cn=(e,n,i)=>i._meta?.column?.unique===!0||e.indexes.some(o=>o.unique===!0&&o.fields.includes(n)),qn=async(e,n,i,o,r,s)=>{if(s.kind==="optional"||R(s))return;const a=Cn(i,r,s),l=a?void 0:bn(s),u=`UPDATE "${n}" SET "${r}" = … WHERE "${r}" IS NULL`;if(l===void 0){const c=a?"is unique, so one default cannot be written to every row":"has no default";L(`backfill:${n}.${r}`,`"${n}.${r}" was added as a required field that ${c}, so rows written before it read NULL. Backfill it with a migration (${u}).`);return}try{await d(e,o,t`UPDATE ${t.identifier(n)} SET ${t.identifier(r)} = ${on(i,r,l)} WHERE ${t.identifier(r)} IS NULL`)}catch(c){L(`backfill:${n}.${r}`,`could not backfill "${n}.${r}" from its default (${c instanceof Error?c.message:String(c)}); rows written before it read NULL. Run ${u}.`)}},vn=(e,n)=>[[A,n.companionTypes.integer],...Object.entries(e.shape).filter(([,i])=>i._meta?.column!==void 0).map(([i,o])=>[i,U(o,n),o])],Dn=async(e,n,i,o)=>{const r=vn(i,o),s="t",a=async l=>{try{return await $(e,o,t`SELECT ${t.join(l.map(u=>tn(s,u)),t`, `)} FROM ${t.identifier(n)} AS ${t.identifier(s)} LIMIT 0`),!0}catch{return!1}};if(!await a(r.map(([l])=>l)))for(const[l,u,c]of r)await a([l])||(await d(e,o,t`ALTER TABLE ${t.identifier(n)} ADD COLUMN ${t.identifier(l)} ${t.raw(u)}`),c!==void 0&&await qn(e,n,i,o,l,c))},Un=(e,n,i)=>{const o=typeof i.charset=="string"?` CHARACTER SET ${i.charset} COLLATE ${String(i.collation)}`:"";return`ALTER TABLE \`${e}\` MODIFY COLUMN \`${n}\` ${String(i.type)}${o} NULL`},kn=async(e,n,i,o)=>{if(o.name==="sqlite")return;const r=Object.keys(i.shape).filter(s=>{const a=i.shape[s];return a._meta?.column?.notNull===!0&&a.kind!=="optional"&&R(a)});if(r.length!==0)try{const s=o.name==="postgres"?t`column_name AS ${t.identifier("name")}`:t`column_name AS ${t.identifier("name")}, column_type AS ${t.identifier("type")}, character_set_name AS ${t.identifier("charset")}, collation_name AS ${t.identifier("collation")}`,a=o.name==="postgres"?t`table_schema = ANY (current_schemas(false))`:t`table_schema = DATABASE()`,l=await $(e,o,t`SELECT ${s} FROM information_schema.columns WHERE ${a} AND table_name = ${n} AND is_nullable = 'NO' AND column_name IN (${t.join(r.map(u=>t`${u}`),t`, `)})`);for(const u of l){const c=String(u.name);if(o.name==="postgres"){await d(e,o,t`ALTER TABLE ${t.identifier(n)} ALTER COLUMN ${t.identifier(c)} DROP NOT NULL`);continue}L(`${n}.${c}`,`"${n}"."${c}" accepts null in the schema but its column is NOT NULL, so writing null there fails. Relaxing it on MySQL can rebuild the table, so it is not done at startup; run this once, in a maintenance window: ${Un(n,c,u)};`)}}catch(s){L(`${n}:relax-failed`,`could not relax NOT NULL on the null-accepting columns of "${n}" (${r.join(", ")}): ${s instanceof Error?s.message:String(s)}. Writing null to them fails until the constraint is dropped by hand.`)}},xn=async(e,n,i,o)=>{const r=un(n,i,o);await d(e,o,t`CREATE TABLE IF NOT EXISTS ${t.identifier(n)} (${r})`),await Dn(e,n,i,o),await kn(e,n,i,o),await Tn(e,n,i,o),await Nn(e,n,i,o),await In(e,n,i,o)},Yn=(e,n)=>[K(e,n),G(e,n)].filter(i=>i.fields.length>0),Xn=async(e,n,i,o)=>{let r=!1;for(const[s,a]of Object.entries(n.tables))a.shardMode?.kind!=="global"||o!==void 0&&!o.has(s)||(r||(r=!0,await gn(e,i,Object.entries(n.tables).filter(([,l])=>l.shardMode?.kind==="global").map(([l])=>l))),await xn(e,s,a,i))},H=e=>e.shardMode===void 0||e.shardMode.kind==="global",Vn=async(e,n,i)=>{const{integer:o,key:r,real:s}=i.companionTypes;for(const[a,l]of Object.entries(n.tables)){const u=l.aggregateIndexes;if(!(!u||u.length===0||!H(l)))for(const c of u){const f=Y(a,c.name);await d(e,i,t`CREATE TABLE IF NOT EXISTS ${t.identifier(f)} (${t.identifier("__key__")} ${t.raw(r)} PRIMARY KEY, ${t.identifier("__value__")} ${t.raw(s)}, ${t.identifier("__count__")} ${t.raw(o)} NOT NULL DEFAULT 0)`),i.name==="sqlite"&&((await $(e,i,t`PRAGMA table_info(${t.identifier(f)})`)).some(p=>p.name==="__count__")||await d(e,i,t`ALTER TABLE ${t.identifier(f)} ADD COLUMN ${t.identifier("__count__")} ${t.raw(o)} NOT NULL DEFAULT 0`))}}},y=(e,n,i,o)=>{const r=e.name==="mysql"&&o?t`${t.identifier(n)}(191)`:t`${t.identifier(n)}`;return t`${r} ${t.raw(i)}`},Mn=(e,n,i)=>{const o=[y(e,"__partition__","ASC",!0)];for(const[r,s]of n.sortBy.entries()){const a=i.shape[s.field],l=a!==void 0&&e.indexKeyPrefix?.(g(a))!==void 0;o.push(y(e,O(r),s.direction==="desc"?"DESC":"ASC",l))}return o.push(y(e,"__id__","ASC",!0)),o},Fn=(e,n,i)=>n.sortBy.map((o,r)=>{const s=i.shape[o.field],a=e.columnType(s?g(s):void 0);return t`${t.identifier(O(r))} ${t.raw(a)}`}),Bn=/__sort_k\d+__/gu,Pn=async(e,n,i,o)=>{if(n.name!=="sqlite")return;const s=(await $(e,n,t`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ${i} COLLATE NOCASE`))[0]?.sql;if(typeof s!="string")return;const a=s.match(Bn)??[];(a.length!==o.length||a.some((l,u)=>l!==o[u]))&&await d(e,n,t`DROP TABLE ${t.identifier(i)}`)},Qn=async(e,n,i)=>{const{key:o}=i.companionTypes;for(const[r,s]of Object.entries(n.tables)){const a=s.rankIndexes;if(!(!a||a.length===0||!H(s)))for(const l of a){const u=X(r,l.name),c=Fn(i,l,s);await Pn(e,i,u,l.sortBy.map((I,W)=>O(W)));const f=c.length>0?t`, ${t.join(c,t`, `)}`:t``;await d(e,i,t`CREATE TABLE IF NOT EXISTS ${t.identifier(u)} (${t.identifier("__id__")} ${t.raw(o)} PRIMARY KEY, ${t.identifier("__partition__")} ${t.raw(o)} NOT NULL${f})`);const E=Mn(i,l,s),p=`${r}__rank_${l.name}__btree`,h={columns:t.join(E,t`, `),name:p,refs:[],table:u,unique:!1};await B(e,i,h),await _(e,i,h)}}};export{T as INDEX_LEDGER_TABLE,N as MIGRATION_STATE_TABLE,fn as declaredIndexColumnList,w as foldIdentifier,un as globalTableColumnsDdl,mn as globalTableIndexSpecs,F as indexColumnList,En as indexLedgerDdl,dn as indexShapeChanged,Yn as legacyRewrites,Sn as legacyRowPredicate,xn as provisionGlobalTable,Xn as provisionGlobalTablesSequentially,P as recordIndexStatement,Vn as runSqlAggregateMigrations,Qn as runSqlRankMigrations,pn as undeclaredFrameworkIndexes,vn as wantedGlobalColumns};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{sql as a}from"drizzle-orm";import{provisionGlobalTablesSequentially as m,foldIdentifier as c,globalTableColumnsDdl as A,globalTableIndexSpecs as L,legacyRewrites as O,indexLedgerDdl as R,wantedGlobalColumns as k,undeclaredFrameworkIndexes as y,indexShapeChanged as $,INDEX_LEDGER_TABLE as T,MIGRATION_STATE_TABLE as E,recordIndexStatement as M,legacyRowPredicate as C}from"./runSqlAggregateMigrations-BqfzHcG8.mjs";import{r as q,q as g}from"./sql-exec-UH-aJj2C.mjs";const v=50,h=new Map([["'","'"],["[","]"],["`","`"],['"','"']]),N=/^[\w$]+/u,b=(e,r)=>{const n=h.get(e[r]);let t=r+1;for(;t<e.length;)if(e[t]!==n)t+=1;else if(n!=="]"&&e[t+1]===n)t+=2;else return t+1;return t},_=e=>{const r=e.trim(),n=h.get(r[0]??"");if(n===void 0)return N.exec(r)?.[0];const t=b(r,0),s=r.slice(1,r[t-1]===n?t-1:t);return n==="]"?s:s.replaceAll(`${n}${n}`,n)},B=e=>{const r=new Set,n=i=>{const d=_(i);d!==void 0&&r.add(d)};let t=0,s=0,o=0;for(;o<e.length;){const i=e[o];if(h.has(i)){o=b(e,o);continue}if(i==="(")t+=1,s=t===1?o+1:s;else if(i===")"){if(t-=1,t===0)return n(e.slice(s,o)),r}else i===","&&t===1&&(n(e.slice(s,o)),s=o+1);o+=1}return r},F=async(e,r,n)=>{const t=new Set,s=new Map,o=n.has(c(T)),i=[];if(n.has(c(E))&&i.push(a`SELECT 'marker' AS kind, name, NULL AS owner FROM ${a.identifier(E)}`),o&&i.push(a`SELECT 'index' AS kind, "l"."name" AS name, "l"."table_name" AS owner FROM ${a.identifier(T)} AS "l"`),i.length===0)return{hasLedger:o,markers:t,owned:s};for(const d of await g(e,r,a.join(i,a` UNION ALL `)))if(d.kind==="marker")t.add(String(d.name));else{const f=c(String(d.owner));s.set(f,[...s.get(f)??[],String(d.name)])}return{hasLedger:o,markers:t,owned:s}},D=async(e,r)=>{try{const n=await g(e,r,a`SELECT type, name, tbl_name, sql FROM sqlite_master WHERE type IN ('table', 'index')`),t=new Set,s=new Map,o=new Map;for(const l of n){const u=String(l.name);if(u.startsWith("sqlite_"))continue;const S=typeof l.sql=="string"?l.sql:void 0;l.type==="table"?(t.add(c(u)),s.set(c(u),new Set([...S===void 0?[]:B(S)].map(I=>c(I))))):o.set(c(u),S)}const{hasLedger:i,markers:d,owned:f}=await F(e,r,t);return{columns:s,hasLedger:i,indexes:o,markers:d,owned:f,tables:t}}catch{return}},w=e=>e.kind==="default-order"?[]:[M(e.name,e.table)],p=e=>{const r=e.unique?a`UNIQUE `:a``;return a`CREATE ${r}INDEX IF NOT EXISTS ${a.identifier(e.name)} ON ${a.identifier(e.table)} (${e.columns})`},x=(e,r,n)=>n.length===0?[]:[a`CREATE TABLE IF NOT EXISTS ${a.identifier(E)} (${a.identifier("name")} ${a.raw(e.companionTypes.key)} PRIMARY KEY)`,...n.map(t=>a`INSERT OR IGNORE INTO ${a.identifier(E)} (${a.identifier("name")}) SELECT ${t.marker} WHERE NOT EXISTS (SELECT 1 FROM ${a.identifier(r)} WHERE ${C(t)})`)],G=(e,r,n,t)=>{const s=A(e,r,n),o=L(e,r,n),i=O(e,r);if(!t.tables.has(c(e)))return[...t.hasLedger?[]:[R()],a`CREATE TABLE IF NOT EXISTS ${a.identifier(e)} (${s})`,...o.flatMap(l=>[p(l),...w(l)]),...x(n,e,i)];const d=t.columns.get(c(e))??new Set;if(k(r,n).some(([l])=>!d.has(c(l)))||i.some(l=>!t.markers.has(l.marker))||y(n,o,t.owned.get(c(e))??[]).length>0)return;const f=[];for(const l of o){if(!t.indexes.has(c(l.name))){f.push(p(l),...w(l));continue}const u=t.indexes.get(c(l.name));if(l.kind==="declared"&&typeof u=="string"&&$(n,u,l))return}return f},X=e=>{const r=[];let n=[],t=0;for(const s of e)n.length>0&&t+s.statements.length>v&&(r.push(n),n=[],t=0),n.push(s),t+=s.statements.length;return n.length>0&&r.push(n),r},U=(e,r)=>!r.hasLedger&&Object.entries(e.tables).some(([n,t])=>t.shardMode?.kind==="global"&&r.tables.has(c(n))),Q=async(e,r,n)=>{if(!e.batch||n.name!=="sqlite"){await m(e,r,n);return}const t=await D(e,n);if(t===void 0){await m(e,r,n);return}if(U(r,t)){await m(e,r,n);return}const s=[],o=new Set;for(const[i,d]of Object.entries(r.tables)){if(d.shardMode?.kind!=="global")continue;const f=G(i,d,n,t);f===void 0?o.add(i):f.length>0&&s.push({statements:f,table:i})}for(const i of X(s))try{await q(e,n,i.flatMap(d=>d.statements))}catch{for(const d of i)o.add(d.table)}await m(e,r,n,o)};export{B as columnNamesOfCreateTable,Q as runSqlGlobalTableMigrations};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/sql-store",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.197",
|
|
4
4
|
"description": "Internal dialect-parameterized SQL store core for Lunora .global() backends (D1, PlanetScale)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@lunora/errors": ">=1.0.0-alpha.51 <2.0.0-0",
|
|
54
|
-
"@lunora/shard-engine": "1.0.0-alpha.
|
|
55
|
-
"@lunora/wire": "1.0.0-alpha.
|
|
54
|
+
"@lunora/shard-engine": "1.0.0-alpha.148",
|
|
55
|
+
"@lunora/wire": "1.0.0-alpha.2",
|
|
56
56
|
"drizzle-orm": "^0.45.3"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import{LunoraError as S}from"@lunora/errors";import{m as en,D as tn}from"./ctx-db-search-state-DRiojLLm.mjs";import{c as nn,r as on,A as rn,C as sn,R as an,u as cn,a as dn,T as ln,x as un,b as fn}from"./ctx-db-search-DB_ejfgj.mjs";import{throwingScheduler as wn,describeRelationWrites as $n,CDC_LOG_TABLE as K,cdcForkedError as hn,cursorBelowRetainedFloor as _n,cdcTrimmedError as En,serverInList as pn,assertNoExplicitUndefined as Fe,hasTrigger as yn,runRowValidators as We,assertFlatPredicate as at,mergeWhere as F,rankTableName as he,sortColumnName as pe,resolveRankPartition as ct,encodePartitionKey as Ce,resolveRankSeekTuple as Rn,RANK_TIEBREAK as ye,CountRlsUnsupportedError as dt,compileWhereSql as X,ConflictError as oe,patchDependsOnRow as Tn,normalizeIdStructurally as Sn,assertValidClientId as gn,aggregateSqlFunction as _e,softDeleteScope as Le,resolveWith as lt,relationHooks as ut,applySelect as ft,encodeCursor as vn,NotFoundError as mn,applyOnDelete as An,normalizeCountArgument as In,selectIndexForCount as Cn,encodeAggregateKey as Q,aggregateTableName as te,selectIndexForAggregate as Ln,readAggregateValue as Ue,runTriggers as bn,matchesRankStaticWhere as wt,nullsOrderSuffix as $t,nullsFirstOf as ht,rankPivotConditionSql as Nt,CURSOR_PREFIX as Mn,resolvePatch as Nn,stripReservedPatchFields as qn,assertPatchAllowed as kn,resolveRelationPredicates as Dn,selectIndexForGroupBy as On,composeReadPredicate as Bn,fanOutScalarCounts as Fn,matchesStaticWhere as xe,foldAggregateTally as _t,coerceAggregateNumber as Pe,whereOfFilter as Wn,isPushableWhere as Un,tiebreakDirectionFor as xn,CDC_LOG_TABLE_SEQ_INDEX as Pn,NotUniqueError as Hn,WORKERD_SQLITE_LIMITS as Kn}from"@lunora/shard-engine";import{decodeWire as qt,needsWireEncoding as kt,encodeWire as Dt}from"@lunora/wire";import{sql as e,SQL as jn}from"drizzle-orm";import{p as Et,m as pt,q as A,a as q,c as Gn,o as Vn,f as z,s as ne,O as Ee,b as x,h as yt,t as Ot,j as He,u as Rt,l as Tt,B as Ke,v as Me,n as Qe,e as St,w as gt,x as vt,y as Yn}from"./sql-exec-UH-aJj2C.mjs";import{z as Yi}from"./sql-exec-UH-aJj2C.mjs";import{runSqlGlobalTableMigrations as Xn}from"./runSqlGlobalTableMigrations-6ORBtPAz.mjs";import{runSqlAggregateMigrations as Qn,runSqlRankMigrations as zn}from"./runSqlAggregateMigrations-CUAkKj8T.mjs";import{c as Bt,a as Ft}from"./value-codec-CiS1h_97.mjs";const Jn=(s,c)=>{if(s.size<c)return;const $=s.keys().next().value;$!==void 0&&s.delete($)},mt=50,Zn=(s,c={})=>{const $=c.maxBatch??mt,o=Number.isInteger($)&&$>0?$:mt,R=new Map;let f=!1;const d=async(g,m)=>{const C=[...m.keys()];for(let L=0;L<C.length;L+=o){const b=C.slice(L,L+o);try{const B=await s(g,b);for(const V of b){const Y=B.get(V);for(const J of m.get(V)??[])J.resolve(Y)}}catch(B){for(const V of b)for(const Y of m.get(V)??[])Y.reject(B)}}},N=()=>{f=!1;const g=[...R.entries()];R.clear();for(const[m,C]of g)d(m,C).catch(()=>{})};return{async load(g,m){return new Promise((C,L)=>{let b=R.get(g);b||(b=new Map,R.set(g,b));const B=b.get(m);B?B.push({reject:L,resolve:C}):b.set(m,[{reject:L,resolve:C}]),f||(f=!0,queueMicrotask(N))})}}},ei=new Set(["_id","id"]),ti=s=>{throw new S("INTERNAL",`no ORDER BY NULL placement for dialect "${String(s)}"`)},Wt=(s,c)=>{const $=c.direction==="desc"?"DESC":"ASC",o=c.nullable===!0,R=f=>e`${Me(s,c.field)} ${e.raw(`${$}${f}`)}`;switch(s.name){case"mysql":{const f=R("");return!o||c.nulls===void 0?[f]:[e`(${Me(s,c.field)} IS NULL) ${e.raw(c.nulls==="first"?"DESC":"ASC")}`,f]}case"postgres":return[R(o?$t(ht(c)):"")];case"sqlite":return[R(o&&c.nulls!==void 0?$t(ht(c)):"")];default:return ti(s.name)}},ni=(s,c)=>{const $=s.flatMap(o=>Wt(c,o));if(!s.some(o=>ei.has(o.field))){const o=xn(s);$.push(e`${x("id")} ${e.raw(o==="desc"?"DESC":"ASC")}`)}return e.join($,e`, `)},At=50,ii=(s,c,$)=>{const o={...c};for(const[R,f]of Ot(s)){if(f.serverDefault){o[R]=f.serverDefault({auth:$});continue}o[R]===void 0&&(f.defaultFn?o[R]=f.defaultFn():"defaultValue"in f&&(o[R]=f.defaultValue))}return o},oi=s=>{const c=s.detail??s["QUERY PLAN"];return typeof c=="string"?c:Object.values(s).map(String).join(" | ")},je=(s,c,$,o)=>{for(const[R,f]of Ot(s)){if(f.serverDefault){R in c&&($[R]=f.serverDefault({auth:o}));continue}f.onUpdateFn&&!(R in c)&&($[R]=f.onUpdateFn())}},ri=128,si=()=>{const s=new Map;return{delete:c=>{s.delete(c)},get:c=>{const $=s.get(c);if($!==void 0)return s.delete(c),s.set(c,$),$},set:(c,$)=>{s.has(c)?s.delete(c):Jn(s,ri),s.set(c,$)}}},ai=async(s,c,$,o,R)=>{const f=R.get(o);if(f!==void 0)return f;const d=Object.entries($.tables).filter(([,g])=>g.shardMode===void 0||g.shardMode.kind==="global").map(([g])=>g),N=await Promise.all(d.map(async g=>({found:(await A(s,c,e`SELECT 1 FROM ${e.identifier(g)} WHERE ${e.identifier("id")} = ${o} LIMIT 1`)).length>0,tableName:g})));for(const g of N)if(g.found)return R.set(o,g.tableName),g.tableName},re=s=>s==null?null:Number(s),ci=(s,c,$)=>$.map(o=>{const R={};for(const f of c){const d=s.shape[f];R[f]=Bt(o[f]??null,d&&Ft(d))}return{key:R,value:re(o.value)}}),di=(s,c,$)=>[...s.partitionBy??[],...s.sortBy.map(R=>R.field),...s.where?Object.keys(s.where):[]].every(R=>gt(c,R)===gt($,R)),Ut=s=>s.length===1?s[0]:e`(${e.join(s,e` AND `)})`,ie=s=>e.join(s.map(c=>e`${e.identifier(c)}`),e`, `),se=s=>e.join(s.map(c=>e`${c}`),e`, `),be=(s,c)=>{if(s==="postgres"){for(const[$,o]of Object.entries(c))if(typeof o=="string"&&o.includes("\0"))throw new S("BAD_REQUEST",`"${$}" holds a NUL character (U+0000), which a Postgres text column cannot store`)}},Ne=s=>s instanceof ArrayBuffer?new Uint8Array(s):Array.isArray(s)?Uint8Array.from(s):s,li=(s,c,$,o,R)=>{const f=[];for(let d=0;d<$.length+1;d+=1){const N=$[d],g=c.sortBy[d],m=d<$.length&&N!==void 0&&g!==void 0,C=g?.direction==="desc"?"desc":"asc",L=m?Nt(N,Ne(o[N]),C,!1):e`${e.identifier(ye)} < ${R}`;if(L===void 0)continue;const b=$.slice(0,d).map(B=>Qe(s,e`${e.identifier(B)}`,Ne(o[B])));b.push(L),f.push(Ut(b))}return f.length>0?e.join(f,e` OR `):e`1 = 0`},ui=(s,c,$)=>{if($.length!==c.length)return;const o=[];for(const[R,f]of c.entries()){const d=Nt(f.column,Ne($[R]),f.direction,!0);if(d===void 0)continue;const N=[];for(let g=0;g<R;g+=1){const m=c[g];m!==void 0&&N.push(Qe(s,e`${e.identifier(m.column)}`,Ne($[g])))}N.push(d),o.push(Ut(N))}return o.length===0?e`(1 = 0)`:e`(${e.join(o,e` OR `)})`},fi=(s,c)=>{if(s.sortBy.length===0)throw new S("INTERNAL",`rankIndex "${s.name}" requires at least one "sortBy" column for stable pagination`);const $=[{column:"__partition__",direction:"asc",nullable:!1}];for(const[o,R]of s.sortBy.entries())$.push({column:c[o]??pe(o),direction:R.direction,nullable:!0});return $.push({column:ye,direction:"asc",nullable:!1}),$},wi=async(s,c,$,o,R)=>{const d=[];for(let C=0;C<R.length;C+=50)d.push(R.slice(C,C+50));const N=await Promise.all(d.map(async C=>A(s,c,e`SELECT * FROM ${e.identifier(o)} WHERE ${e.identifier("id")} IN (${se(C)})`))),g=new Map;for(const C of N)for(const L of C)g.set(L.id,L);const m=[];for(const C of R){const L=z($,g.get(C));L&&m.push(L)}return m},$i=s=>{const c=s.map(f=>Bt(f,"bytes")),$=JSON.stringify(kt(c)?Dt(c):c),o=new TextEncoder().encode($);let R="";for(const f of o)R+=String.fromCodePoint(f);return Mn+btoa(R)},xt=s=>{if(s===void 0)return!1;const c=Ft(s);return c==="any"||c==="bigint"||c==="from"||c==="union"},Ye=(s,c,$)=>{if(xt(s.shape[c]))throw new S("BAD_REQUEST",`${$}: "${c}" may hold an order-preserving key rather than a value SQL can reduce or group — declare an aggregateIndex covering this (by, field, op) so the maintained companion answers it instead (its running total is a REAL, so it stays exact only while the total is inside 2^53)`)},hi=(s,c,$,o)=>{for(const R of $)Ye(s,R,`groupBy(${c}, { by: [..."${R}"] })`);o.field!==void 0&&Ye(s,o.field,`groupBy(${c}, { agg: { op: "${o.op}", field: "${o.field}" } })`)},Xe="__cdc_sweep",Pt="global",_i=6e4,Ei=1e4,pi=3e4;let It=0;const yi=async(s,c)=>{const{autoincrementPrimaryKey:$,key:o,real:R,text:f}=c.companionTypes;await q(s,c,e`CREATE TABLE IF NOT EXISTS ${e.identifier(K)} (${e.identifier("seq")} ${e.raw($)}, ${e.identifier("ts")} ${e.raw(R)} NOT NULL, ${e.identifier("table")} ${e.raw(o)} NOT NULL, ${e.identifier("id")} ${e.raw(o)} NOT NULL, ${e.identifier("op")} ${e.raw(o)} NOT NULL, ${e.identifier("doc")} ${e.raw(f)})`);const d=c.indexKeyPrefix?.("string");await Gn(s,c,{columns:e`${d===void 0?e`${e.identifier("table")}`:e`${e.identifier("table")}(${e.raw(String(d))})`}, ${e.identifier("seq")}`,name:Pn,table:K,unique:!1}),await q(s,c,e`CREATE TABLE IF NOT EXISTS ${e.identifier(Xe)} (${e.identifier("id")} ${e.raw(o)} NOT NULL PRIMARY KEY, ${e.identifier("lease_until")} ${e.raw(R)} NOT NULL)`),await q(s,c,e`INSERT INTO ${e.identifier(Xe)} (${e.identifier("id")}, ${e.identifier("lease_until")}) VALUES (${Pt}, ${0}) ${c.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${e.identifier("id")} = ${e.identifier("id")}`:e`ON CONFLICT(${e.identifier("id")}) DO NOTHING`}`)},Ri=async(s,c,$)=>{const o=e`UPDATE ${e.identifier(Xe)} SET ${e.identifier("lease_until")} = ${$+_i} WHERE ${e.identifier("id")} = ${Pt} AND ${e.identifier("lease_until")} <= ${$}`;if(c.supportsReturning)return(await A(s,c,e`${o} RETURNING ${e.identifier("id")}`)).length>0;const R=await q(s,c,o);return(c.affectedRows?c.affectedRows(R??{rowsAffected:0}):0)>0},Ti=async(s,c,$,o)=>{if(!await Ri(s,c,o))return;const R=o-$;await q(s,c,e`
|
|
2
|
-
DELETE FROM ${e.identifier(K)} WHERE ${e.identifier("seq")} IN (
|
|
3
|
-
SELECT ${e.identifier("seq")} FROM (
|
|
4
|
-
SELECT ${e.identifier("seq")} FROM ${e.identifier(K)} WHERE ${e.identifier("ts")} <= ${R} ORDER BY ${e.identifier("seq")} ASC LIMIT ${e.raw(String(Ei))}
|
|
5
|
-
) AS ${e.identifier("expired")}
|
|
6
|
-
)
|
|
7
|
-
`)},Ht=async(s,c)=>{const $=await A(s,c,e`SELECT MIN(${e.identifier("seq")}) AS ${e.identifier("seq")} FROM ${e.identifier(K)}`),o=Number($[0]?.seq??Number.NaN);return Number.isFinite(o)?o:void 0},Si=async(s,c)=>{if(c.name!=="sqlite")return;const $=await A(s,c,e`SELECT ${e.identifier("seq")} AS ${e.identifier("seq")} FROM ${e.identifier("sqlite_sequence")} WHERE ${e.identifier("name")} = ${K}`),o=Number($[0]?.seq??Number.NaN);return Number.isFinite(o)?o:0},gi=s=>JSON.stringify(kt(s)?Dt(s):s),vi=async(s,c,$,o,R,f,d)=>{await q(s,d,e`INSERT INTO ${e.identifier(K)} (${e.identifier("ts")}, ${e.identifier("table")}, ${e.identifier("id")}, ${e.identifier("op")}, ${e.identifier("doc")}) VALUES (${c}, ${$}, ${o}, ${R}, ${f===void 0?null:gi(f)})`)},Ki=async(s,c,$)=>{const o=c.sinceSeq??0,R=Math.max(1,Math.min(c.limit??1e3,1e4)),f=await Ht(s,$),d=await Si(s,$);if(d!==void 0&&o>d)throw hn(d,o,"global");if(f!==void 0&&_n(f,o))throw En(f,o,"global");const g=(await A(s,$,e`SELECT seq, ts, ${e.identifier("table")}, id, op, doc FROM ${e.identifier(K)} WHERE seq > ${o} ORDER BY seq ASC LIMIT ${e.raw(String(R))}`)).map(m=>{const{doc:C}=m,L={id:String(m.id),op:String(m.op),seq:Number(m.seq),table:String(m.table),ts:Number(m.ts)};return typeof C=="string"?{...L,doc:qt(JSON.parse(C))}:L});return{changes:g,cursor:g.at(-1)?.seq??o}},mi=1e4,Ai=1e3,Ct=(s,c)=>{const $=e.identifier(K),o=e`(SELECT MIN(v.seq) FROM ${$} v WHERE v.seq > ${s} AND v.seq - 1 > ${s} AND v.ts > ${c} AND NOT EXISTS (SELECT 1 FROM ${$} p WHERE p.seq = v.seq - 1))`;return e`${o} AS ${e.identifier("gap")}, (SELECT MAX(q.seq) FROM ${$} q WHERE q.seq > ${s} AND q.seq < ${o}) AS ${e.identifier("below")}`},Lt=(s,c,$)=>{const o=Number(c?.gap??Number.NaN);if(!Number.isFinite(o))return s;const R=Number(c?.below??Number.NaN);return Math.min(s,Number.isFinite(R)?R:$)},Ii=async(s,c,$,o={})=>{const R=e.identifier(K),f=$.name!=="sqlite",d=(o.now??Date.now())-mi;if(o.cursorOnly){const L=e`GREATEST((SELECT MAX(seq) FROM ${R}) - ${e.raw(String(Ai))}, ${c})`,b=await A(s,$,e`SELECT MAX(seq) AS seq${f?e`, ${Ct(L,d)}`:e``} FROM ${R}`),B=Number(b[0]?.seq??c);return{cursor:Lt(Number.isFinite(B)?B:c,b[0],c),tables:[]}}const N=await A(s,$,e`SELECT ${e.identifier("table")} AS ${e.identifier("table")}, MAX(seq) AS seq${f?e`, ${Ct(e`${c}`,d)}`:e``} FROM ${R} WHERE seq > ${c} GROUP BY ${e.identifier("table")}`);let g=c;const m=[];for(const L of N){const b=Number(L.seq);Number.isFinite(b)&&b>g&&(g=b),m.push(String(L.table))}g=Lt(g,N[0],c);const C=o.retained===!0?await Ht(s,$):void 0;return{cursor:g,tables:m,...C===void 0?{}:{floor:C}}},Ci=/string or blob too big/iu,Li=/row is too big/iu,bi=(s,c)=>{const{code:$,errno:o}=c,R=c instanceof Error?c.message:"";switch(s.name){case"mysql":return o===1118||$==="ER_TOO_BIG_ROWSIZE"?"InnoDB's per-row ceiling — roughly 8 KB, half a 16 KB page, for the part of the row stored inline":void 0;case"postgres":return Li.test(R)?"the 8 KB heap page a tuple must fit once its wide columns have been TOASTed out":void 0;default:return Ci.test(R)?"the storage engine's per-row ceiling (2 MB on D1)":void 0}},Mi=(s,c,$)=>{const o=bi(s,c);if(o!==void 0)throw new S("PAYLOAD_TOO_LARGE",`document is too large to store in "${$}": a single row cannot exceed ${o}. The limit is on the STORED bytes, which are UTF-8. Keep the payload in R2 (ctx.storage) and store a reference on the row.`)},Ge=16,bt="__lunora_patch_if_match__",Mt=(s,c,$)=>{throw s.isUniqueViolation(c)?new oe(`unique constraint violation on "${$}"`,"unique"):(Mi(s,c,$),c)},Ve=new WeakMap,Ni=s=>{const c=pn(s);return($,o,R,f)=>o.some(d=>d instanceof jn)?Vn(s,$,o,R):c($,o,R,f)},ji=s=>{const{crossShardCounter:c,crossShardReader:$,exec:o,maxRelationKeys:R,schema:f}=s,{dialect:d}=s,N={fieldRef:t=>Me(d,t),rangeTypeGuard:(t,n)=>Yn(d,t,n),...d.name==="mysql"?{containsExpr:(t,n)=>{const i=Et(t);return i===void 0?e`LOCATE(${n}, ${t}) > 0`:e`LOCATE(${pt("mysql",n)}, ${i}) > 0`}}:{},...d.name==="postgres"?{containsExpr:(t,n)=>{const i=Et(t);return i===void 0?e`strpos(${t}, ${n}) > 0`:e`strpos(${i}, ${pt("postgres",n)}) > 0`}}:{},...d.name==="sqlite"?{}:{inList:Ni(d.name)}},g=new WeakMap,m=t=>{const n=g.get(t);if(n!==void 0)return n;const i={...N,serialize:(a,r)=>vt(d,r,a,l=>ne(t,r,l))};return g.set(t,i),i},C=(t,n)=>Qe(d.name,t,n),L=t=>{const n=ie(t.columns),i=se(t.values),a=h=>d.name==="mysql"?e`VALUES(${e.identifier(h)})`:e`excluded.${e.identifier(h)}`,r=h=>d.name==="postgres"?e`${e.identifier(t.table)}.${e.identifier(h)}`:e`${e.identifier(h)}`,l=e.join(Object.entries(t.set(a,r)).map(([h,E])=>e`${e.identifier(h)} = ${E}`),e`, `),_=d.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${l}`:e`ON CONFLICT(${e.identifier(t.conflictKey)}) DO UPDATE SET ${l}`;return e`INSERT INTO ${e.identifier(t.table)} (${n}) VALUES (${i}) ${_}`},b=s.clock??(()=>Date.now()),B=s.idGenerator??(()=>crypto.randomUUID()),V=s.cdc??!1,Y=typeof s.cdcRetentionMs=="number"&&Number.isFinite(s.cdcRetentionMs)&&s.cdcRetentionMs>0?s.cdcRetentionMs:void 0,J=s.auth??{identity:null,userId:null},ae=async(t,n,i,a)=>{if(!V||(await vi(o,b(),t,n,i,a,d),Y===void 0))return;const r=b();if(!(r-It<pi)){It=r;try{await Ti(o,d,Y,r)}catch{}}},Kt=s.scheduler??wn,qe=si();let Re=0;const ke=s.provisionScope??{},W=async()=>{const t=Ve.get(ke);if(t)return t;const n=(async()=>{await Xn(o,f,d),await Qn(o,f,d),await zn(o,f,d),await en(o,d),await on(o,f,d),V&&await yi(o,d)})().catch(i=>{throw Ve.delete(ke),i});return Ve.set(ke,n),n},ce=async(t,n)=>{await W();const i=await ai(o,d,f,t,qe);if(!(n!==void 0&&i!==n))return i},de=new Map,ze=async(t,n)=>{const i=te(t,n);return(await A(o,d,d.tableExists(i))).length>0},Te=async(t,n)=>{const i=`${t}::${n.name}`,a=de.get(i);if(a!==void 0)return a;if(!await ze(t,n.name))return de.set(i,!1),!1;const l=f.tables[t];if(!l)return de.set(i,!1),!1;const _=n.by??[],h=new Map;await Tt(o,d,l,t,p=>{if(n.where&&!xe(p,n.where))return;const w=Q(_,p);_t(h,w,n,p)});const E=te(t,n.name);await q(o,d,e`DELETE FROM ${e.identifier(E)}`);const u=[];for(const[p,w]of h)u.push(e`INSERT INTO ${e.identifier(E)} (${e.identifier("__key__")}, ${e.identifier("__value__")}, ${e.identifier("__count__")}) VALUES (${p}, ${w.value}, ${w.count})`);return await St(o,d,u),de.set(i,!0),!0},jt=async(t,n,i,a)=>{const r=new Map;let l,_=!0;for(;_;){const h=l===void 0?a:[...a,e`${e.identifier("id")} > ${l}`],E=h.length>0?e` WHERE ${e.join(h,e` AND `)}`:e``,u=await A(o,d,e`SELECT ${e.identifier("id")}, ${x(i.field??"")} FROM ${e.identifier(n)}${E} ORDER BY ${e.identifier("id")} ASC LIMIT ${e.raw(String(Ke))}`);for(const p of yt(t,u))_t(r,"",i,p);l=u.at(-1)?.id,_=u.length===Ke}return r.get("")?.value??null},Gt=async(t,n,i)=>{const a=n.field??"",r=[],l=f.tables[t],_=(p,w)=>vt(d,p,w,y=>l?ne(l,p,y):He(y));for(const p of n.by??[]){const w=_(p,i[p]??null);r.push(w===null?e`${x(p)} IS NULL`:e`${x(p)} = ${w}`)}for(const[p,w]of Object.entries(n.where??{})){const y=w!==null&&typeof w=="object"&&!Array.isArray(w)?w.eq:w,T=_(p,y),v=Me(d,p);r.push(T===null?e`${v} IS NULL`:e`${v} = ${T}`)}const h=r.length>0?e` WHERE ${e.join(r,e` AND `)}`:e``;if(l&&xt(l.shape[a]))return jt(l,t,n,r);const E=_e(n.op),u=await A(o,d,e`SELECT ${e.raw(E)}(${x(a)}) AS ${e.identifier("value")} FROM ${e.identifier(t)}${h}`);return re(u[0]?.value)},Vt=async(t,n)=>{await q(o,d,e`DELETE FROM ${e.identifier(t)} WHERE ${e.identifier("__key__")} = ${n} AND ${e.identifier("__count__")} <= 0`)},Yt=async(t,n,i,a)=>{const r=te(t,n.name),{op:l}=n,_=n.field??"",h=i&&(!n.where||xe(i,n.where))?i:void 0,E=a&&(!n.where||xe(a,n.where))?a:void 0;if(!(!h&&!E)){if(l==="count"||l==="sum"||l==="avg"){const u=new Set;for(const[p,w]of[[h,-1],[E,1]]){if(!p)continue;const y=l==="count"?1:Pe(p[_]);if(y===void 0)continue;const T=Q(n.by??[],p);u.add(T),await q(o,d,L({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(v,I)=>({__count__:e`${I("__count__")} + ${v("__count__")}`,__value__:l==="count"?e`${I("__value__")} + ${v("__value__")}`:e`COALESCE(${I("__value__")}, 0) + ${v("__value__")}`}),table:r,values:[T,w*y,w]}))}for(const p of u)await Vt(r,p);return}if(h){const u=Q(n.by??[],h),p=Pe(h[_]),y=(await A(o,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(r)} WHERE ${e.identifier("__key__")} = ${u}`))[0],T=re(y?.value),v=(y?.count??0)-1;if(v<=0)await q(o,d,e`DELETE FROM ${e.identifier(r)} WHERE ${e.identifier("__key__")} = ${u}`);else if(y&&p!==void 0&&T!==null&&p===T){const I=await Gt(t,n,h);await q(o,d,e`UPDATE ${e.identifier(r)} SET ${e.identifier("__value__")} = ${I}, ${e.identifier("__count__")} = ${v} WHERE ${e.identifier("__key__")} = ${u}`)}else await q(o,d,e`UPDATE ${e.identifier(r)} SET ${e.identifier("__count__")} = ${e.identifier("__count__")} - 1 WHERE ${e.identifier("__key__")} = ${u}`)}if(E){const u=Q(n.by??[],E),p=Pe(E[_]);if(p===void 0)await q(o,d,L({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(w,y)=>({__count__:e`${y("__count__")} + 1`}),table:r,values:[u,null,1]}));else{const w=l==="min"?"MIN":"MAX";await q(o,d,L({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(y,T)=>({__count__:e`${T("__count__")} + 1`,__value__:e`${e.raw(w)}(COALESCE(${T("__value__")}, ${y("__value__")}), ${y("__value__")})`}),table:r,values:[u,p,1]}))}}}},Se=async t=>{const n=f.tables[t]?.aggregateIndexes;if(!(!n||n.length===0))for(const i of n)await Te(t,i)},le=async(t,n,i)=>{const a=f.tables[t]?.aggregateIndexes;if(!(!a||a.length===0))for(const r of a){const l=`${t}::${r.name}`;(de.get(l)??await ze(t,r.name))&&await Yt(t,r,n,i)}},ue=new Map,Je=async(t,n)=>{const i=he(t,n);return(await A(o,d,d.tableExists(i))).length>0},De=async(t,n)=>{const i=`${t}::rank::${n.name}`,a=ue.get(i);if(a!==void 0)return a;if(!await Je(t,n.name))return ue.set(i,!1),!1;const l=f.tables[t];if(!l)return ue.set(i,!1),!1;const _=he(t,n.name);await q(o,d,e`DELETE FROM ${e.identifier(_)}`);const h=n.sortBy.map((w,y)=>pe(y)),E=ie(["__id__","__partition__",...h]);let u=[];const p=async()=>{if(u.length===0)return;const w=u;u=[],await St(o,d,w)};return await Tt(o,d,l,t,async w=>{if(n.where&&!wt(w,n.where))return;const y=Ce(n.partitionBy??[],w),T=n.sortBy.map(v=>He(w[v.field]??null));u.push(e`INSERT INTO ${e.identifier(_)} (${E}) VALUES (${se([w._id,y,...T])})`),u.length>=Ke&&await p()}),await p(),ue.set(i,!0),!0},ge=async t=>{const n=f.tables[t]?.rankIndexes;if(!(!n||n.length===0))for(const i of n)await De(t,i)},Z=async(t,n,i,a)=>{const r=f.tables[t],l=r?.rankIndexes;if(!l||l.length===0)return;const _=r?.softDeleteMode?.field,h=_===void 0||a===void 0?void 0:a[_],E=h!=null;for(const u of l){if(i!==void 0&&a!==void 0&&di(u,i,a))continue;const p=`${t}::rank::${u.name}`;if(!(ue.get(p)??await Je(t,u.name)))continue;const T=he(t,u.name);if(i&&await q(o,d,e`DELETE FROM ${e.identifier(T)} WHERE ${e.identifier("__id__")} = ${n}`),a&&!E){if(u.where&&!wt(a,u.where))continue;const v=u.sortBy.map((D,U)=>pe(U)),I=ie(["__id__","__partition__",...v]),M=Ce(u.partitionBy??[],a),k=u.sortBy.map(D=>He(a[D.field]??null));await q(o,d,e`INSERT INTO ${e.identifier(T)} (${I}) VALUES (${se([n,M,...k])})`)}}},fe=nn({dialect:d,exec:o,schema:f}),Ze=new Set;for(const[t,n]of Object.entries(f.tables))for(const i of Object.values(n.triggerMap??{}))Ze.add(`${t} ${i.timing} ${i.op}`);const j=(t,n,i)=>Ze.has(`${t} ${n} ${i}`);let et;const G=async(t,n,i)=>{if(Re+=1,Re>At)throw Re-=1,new oe(`trigger recursion exceeded ${String(At)} levels on "${i.table}" — check for a self-triggering write`,"trigger");try{await bn({ctx:et,event:i,op:n,schema:f,tableName:i.table,timing:t})}finally{Re-=1}},Xt=async(t,n)=>{try{await q(o,d,n)}catch(i){Mt(d,i,t)}},ve=async(t,n)=>(await A(o,d,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} = ${n}`))[0],tt=async(t,n,i,a,r)=>{const l=e`${e.identifier(Ee)}`,_=e.join([C(e`${e.identifier("id")}`,a.id),C(l,a[Ee]??null),...r===void 0?[]:[e`(${r})`]],e` AND `),h=n==="UPDATE"?e`UPDATE ${e.identifier(t)} SET ${i}, ${l} = ${Rt.sql(l)} WHERE ${_}`:e`DELETE FROM ${e.identifier(t)} WHERE ${_}`;try{if(d.supportsReturning)return(await A(o,d,e`${h} RETURNING ${e.identifier("id")}`)).length>0;const E=await q(o,d,h);return(d.affectedRows?d.affectedRows(E??{rowsAffected:0}):0)>0}catch(E){return Mt(d,E,t)}},Oe=async(t,n,i,a)=>{if(a!==void 0&&!await tt(t,n,i,a))throw new oe(`optimistic concurrency conflict on "${t}" — the row changed during this mutation; refetch and retry`,"occ")},Be=(t,n=t?._creationTime)=>({_creationTime:n,[Ee]:Rt.value(t?.[Ee])}),nt=async(t,n,i,a,r,l,_)=>{const h=z(n,r);if(!h||r===void 0)throw new S("INTERNAL",`document not found: ${i}`);const E=Nn(n,a,h),u={...h,...qn(E),_id:i};je(n,E,u,J),kn(_,h,u),We(n,u,!0),j(t,"before","update")&&await G("before","update",{doc:{...u},id:i,op:"update",previous:h,table:t}),await Se(t),await ge(t);const p=Object.keys(n.shape),w=e.join(p.map(y=>e`${e.identifier(y)} = ${ne(n,y,u[y]??null)}`),e`, `);if(await tt(t,"UPDATE",w,r,l))return await le(t,h,u),await Z(t,i,h,u),await fe(t,i,{document:u,previous:h,written:Be(r)}),await ae(t,i,"update",u),j(t,"after","update")&&await G("after","update",{doc:u,id:i,op:"update",previous:h,table:t}),u},Qt=(t,n,i,a)=>{const r=Object.keys(t.shape);return{columns:["id","_creationTime",...r],values:[n,i,...r.map(l=>ne(t,l,a[l]??null))]}},zt=async(t,n,i,a)=>{const r=On(n,i.op,i.field,a.by,a.where);if(!r||!await Te(t,r.index))return;const _=te(t,r.index.name),h=Object.keys(r.partial);if(h.length===(r.index.by??[]).length&&h.length>0){const u=Q(r.index.by??[],r.partial),p=await A(o,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(_)} WHERE ${e.identifier("__key__")} = ${u}`);if(p.length===0)return[];const w=p[0];return[{key:{...r.partial},value:Ue(i.op,{count:w.count,value:re(w.value)})}]}return h.length>0?void 0:(await A(o,d,e`SELECT ${e.identifier("__key__")} AS ${e.identifier("key")}, ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(_)}`)).map(u=>{const p=u;return{key:qt(JSON.parse(p.key)),value:Ue(i.op,{count:p.count,value:re(p.value)})}})},it=t=>{const n=f.tables[t]?.shardMode?.kind;return n==="shardBy"||n==="root"},ot=t=>{throw new S("INTERNAL",`cross-backend relation: a global table cannot load the shard-local relation '${t}' (it spans every shard) — wire a cross-shard reader to support it`)},Jt=(t,n)=>{if(n?.relationMask!==void 0&&n.with!==void 0&&Object.keys(n.with).length>0)throw new S("MASK_UNSUPPORTED",`masking cannot follow a nested \`with\` across the cross-shard relation hop to "${t}" — the nested rows are hydrated on the serving shard, where the mask policy does not exist. Read the nested relation at its own (masked) call site instead.`);const i={};if(n?.relationBaseWhere)for(const a of Object.keys(f.tables)){const r=n.relationBaseWhere(a);r!==void 0&&(i[a]=r)}return{orderBy:n?.orderBy,relationPolicies:i,where:F(n?.baseWhere,n?.where),with:n?.with}},me=(t,n)=>it(t)?$?$(t,Jt(t,n)):ot(t):P.findMany(t,n),Ae=(t,n,i)=>Dn(t,{fetcher:me,maxRelationKeys:R,relationBaseWhere:i,schema:f,tableName:n}),Zt=Zn(async(t,n)=>{const i=await A(o,d,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} IN (${se(n)})`),a=new Map;for(const r of i){const{id:l}=r;typeof l=="string"&&a.set(l,r)}return a}),rt=async(t,n,i)=>{const{fetchLimit:a,limit:r,orderKeys:l,predicate:_}=await Bn(n,i,{fetcher:me,maxRelationKeys:R,schema:f,tableName:t}),h=X(_,m(n)),E=ni(l,d);let u=e`SELECT * FROM ${e.identifier(t)}`;return h&&(u=e`${u} WHERE ${h}`),u=e`${u} ORDER BY ${E}`,a!==void 0&&(u=e`${u} LIMIT ${e.raw(String(a))}`),{limit:r,orderKeys:l,query:u}},P={relationWrites:$n(f),async cdcChangedTables(t,n){if(V)try{return await W(),await Ii(o,t,d,{...n,now:b(),retained:Y!==void 0})}catch{return}},async aggregate(t,n){const i=f.tables[t];if(!i)throw new S("INTERNAL",`unknown table: ${t}`);if(await W(),_e(n.op),n.op==="count")return P.count(t,{baseWhere:n.baseWhere,relationBaseWhere:n.relationBaseWhere,restrictsCounts:n.restrictsCounts,where:n.where});if(!n.field)throw new S("INTERNAL",`aggregate(${t}, { op: "${n.op}" }): "field" is required for non-count reducers`);const a=Le(i.softDeleteMode,void 0),r=F(F(n.baseWhere,n.where),a),l=await Ae(r,t,n.relationBaseWhere),_=l!==r;if(i.aggregateIndexes&&!n.baseWhere&&!_&&!a){const y=Ln(i.aggregateIndexes,n.op,n.field,n.where);if(y&&await Te(t,y.index)){const v=Q(y.index.by??[],y.key),I=te(t,y.index.name),k=(await A(o,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(I)} WHERE ${e.identifier("__key__")} = ${v}`))[0];return Ue(n.op,k===void 0?void 0:{count:k.count,value:re(k.value)})}}Ye(i,n.field,`aggregate(${t}, { op: "${n.op}", field: "${n.field}" })`);const h=X(l,m(i)),E=e.raw(_e(n.op)),u=e`SELECT ${E}(${x(n.field)}) AS ${e.identifier("value")} FROM ${e.identifier(t)}`,w=(await A(o,d,h?e`${u} WHERE ${h}`:u))[0]?.value;return w==null?null:Number(w)},async count(t,n){const i=f.tables[t];if(!i)throw new S("INTERNAL",`unknown table: ${t}`);await W();const a=In(n);if(a.restrictsCounts)throw new dt(t);const r=Le(i.softDeleteMode,void 0),l=F(F(a.baseWhere,a.where),r),_=await Ae(l,t,a.relationBaseWhere),h=_!==l;if(i.aggregateIndexes&&!a.baseWhere&&!h&&!r){const w=Cn(i.aggregateIndexes,a.where);if(w&&await Te(t,w.index)){const T=Q(w.index.by??[],w.key),v=te(t,w.index.name),I=await A(o,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")} FROM ${e.identifier(v)} WHERE ${e.identifier("__key__")} = ${T}`);return Number(I[0]?.value??0)}}const E=X(_,m(i)),u=e`SELECT COUNT(*) AS ${e.identifier("count")} FROM ${e.identifier(t)}`,p=await A(o,d,E?e`${u} WHERE ${E}`:u);return Number(p[0]?.count??0)},async delete(t,n,i){const a=await ce(t,n);if(!a)return;const r=f.tables[a];if(!r)return;const l=await ve(a,t),_=z(r,l),h=i?.hard===!0,E=!h&&r.softDeleteMode?r.softDeleteMode.field:void 0;if(!(E&&(!_||_[E]!==null&&_[E]!==void 0))){if(j(a,"before","delete")&&await G("before","delete",{id:t,op:"delete",previous:_??void 0,table:a}),await An({deletedId:t,deletedReference:u=>_?.[u],findHolders:async(u,p,w)=>{if(f.tables[u]?.shardMode?.kind==="shardBy")throw new S("INTERNAL",`cross-backend cascade from global '${a}' into shardBy '${u}' is not supported — would require Query Coordinator fan-out across shards`);return(await P.findMany(u,{includeDeleted:h,where:{[p]:w}})).page},onCascade:(u,p)=>P.delete(p,void 0,i),onRestrict:u=>{throw new oe(u,"restrict")},onSetNull:(u,p,w)=>P.patch(p,{[w]:null}),schema:f,tableName:a}),await Se(a),await ge(a),E&&_){const u={[E]:b()},p={..._,...u};je(r,u,p,J);const w=e.join(Object.keys(r.shape).map(y=>e`${e.identifier(y)} = ${ne(r,y,p[y]??null)}`),e`, `);await Oe(a,"UPDATE",w,l),await le(a,_,p),await Z(a,t,_,void 0),await fe(a,t,{document:p,previous:_,written:Be(l)}),await ae(a,t,"update",p),j(a,"after","delete")&&await G("after","delete",{id:t,op:"delete",previous:_,table:a});return}await Oe(a,"DELETE",void 0,l),qe.delete(t),await le(a,_??void 0,void 0),await Z(a,t,_??void 0,void 0),await fe(a,t,void 0),await ae(a,t,"delete"),j(a,"after","delete")&&await G("after","delete",{id:t,op:"delete",previous:_??void 0,table:a})}},async findFirst(t,n={}){return(await P.findMany(t,{...n,limit:1})).page[0]??null},async findFirstOrThrow(t,n={}){const i=await P.findFirst(t,n);if(i===null)throw new mn(`findFirstOrThrow: no "${t}" document matched`);return i},async findMany(t,n={}){const i=f.tables[t];if(!i)throw new S("INTERNAL",`unknown table: ${t}`);await W();const a=async(y,T,v,I)=>{if(it(y))return c?Fn(c,y,T,v,I):ot(y);const M=f.tables[y];if(!M)throw new S("INTERNAL",`unknown table: ${y}`);const k=Le(M.softDeleteMode,void 0),D={[T]:{in:v}},U=F(F(D,I),k),we=await Ae(U,y,void 0),Ie=X(we,m(M)),$e=x(T);let H=e`SELECT ${$e} AS __fk__, COUNT(*) AS ${e.identifier("count")} FROM ${e.identifier(y)}`;Ie&&(H=e`${H} WHERE ${Ie}`),H=e`${H} GROUP BY ${$e}`;const O=await A(o,d,H),ee=new Map;for(const st of O)ee.set(st.__fk__,Number(st.count??0));return ee},{limit:r,orderKeys:l,query:_}=await rt(t,i,n),h=await A(o,d,_),E=yt(i,h);if(r===void 0)return n.with&&await lt({fetcher:me,groupedCounter:a,parents:E,...ut(n),schema:f,tableName:t,with:n.with}),{continueCursor:null,isDone:!0,page:ft(E,n.select,n.with)};const u=E.length>r,p=u?E.slice(0,r):E,w=p.at(-1);return n.with&&await lt({fetcher:me,groupedCounter:a,parents:p,...ut(n),schema:f,tableName:t,with:n.with}),{continueCursor:u&&w?vn(w,l):null,isDone:!u,page:ft(p,n.select,n.with)}},async explain(t,n={}){const i=f.tables[t];if(!i)throw new S("INTERNAL",`unknown table: ${t}`);await W();const{query:a}=await rt(t,i,n),r=d.name==="sqlite"?"EXPLAIN QUERY PLAN":"EXPLAIN",l=await A(o,d,e`${e.raw(r)} ${a}`),{params:_,sql:h}=d.render(a);return{params:_,plan:l.map(E=>oi(E)),sql:h}},async get(t,n){const i=await ce(t,n);if(!i)return null;const a=f.tables[i];return a?z(a,await Zt.load(i,t)):null},async groupBy(t,n){const i=f.tables[t];if(!i)throw new S("INTERNAL",`unknown table: ${t}`);await W();const a=n.agg??{op:"count"};if(_e(a.op),a.op!=="count"&&!a.field)throw new S("INTERNAL",`groupBy(${t}, { agg: { op: "${a.op}" } }): "field" is required for non-count reducers`);const r=Le(i.softDeleteMode,void 0),l=F(F(n.baseWhere,n.where),r),_=await Ae(l,t,n.relationBaseWhere),h=_!==l;if(i.aggregateIndexes&&!n.baseWhere&&!h&&!r){const T=await zt(t,i.aggregateIndexes,a,n);if(T!==void 0)return T}hi(i,t,n.by,a);const E=X(_,m(i)),u=n.by.map(T=>e`${x(T)} AS ${e.identifier(T)}`);if(a.op==="count")u.push(e`COUNT(*) AS ${e.identifier("value")}`);else{if(!a.field)throw new S("INTERNAL",`groupBy(${t}, { agg: { op: "${a.op}" } }): "field" is required for non-count reducers`);u.push(e`${e.raw(_e(a.op))}(${x(a.field)}) AS ${e.identifier("value")}`)}const p=e.join(n.by.map(T=>x(T)),e`, `);let w=e`SELECT ${e.join(u,e`, `)} FROM ${e.identifier(t)}`;E&&(w=e`${w} WHERE ${E}`),w=e`${w} GROUP BY ${p}`;const y=await A(o,d,w);return ci(i,n.by,y)},async insert(t,n,i){const a=f.tables[t];if(!a)throw new S("INTERNAL",`unknown table: ${t}`);await W(),be(d.name,n);const r=ii(a,n,J);We(a,r);let l,_=!0;i?.clientId!==void 0?(gn(i.clientId),l=i.clientId):i?.allowExplicitId&&typeof r._id=="string"?l=r._id:(l=B(),_=!1);const h=i?.allowExplicitId&&typeof r._creationTime=="number"?r._creationTime:b(),E={...r,_creationTime:h,_id:l};j(t,"before","insert")&&await G("before","insert",{doc:{...E},id:l,op:"insert",table:t}),await Se(t),await ge(t);const{columns:u,values:p}=Qt(a,l,h,r);return await Xt(t,e`INSERT INTO ${e.identifier(t)} (${ie(u)}) VALUES (${se(p)})`),_&&qe.set(l,t),await le(t,void 0,E),await Z(t,l,void 0,E),await fe(t,l,{document:E,written:{_creationTime:h,[Ee]:null}}),await ae(t,l,"insert",E),j(t,"after","insert")&&await G("after","insert",{doc:E,id:l,op:"insert",table:t}),l},normalizeId(t,n){return Sn(f,t,n)},async patch(t,n,i,a){Fe("patch",n),be(d.name,n);const r=await ce(t,i);if(!r)throw new S("INTERNAL",`document not found: ${t}`);const l=f.tables[r];if(!l)throw new S("INTERNAL",`document not found: ${t}`);const _=Tn(l,n)?Ge:1;for(let h=0;h<_;h+=1){const E=await ve(r,t);if(await nt(r,l,t,n,E,void 0,a))return}throw new oe(`optimistic concurrency conflict on "${r}" — the row changed during this mutation; refetch and retry`,"occ")},async patchIf(t,n,i){Fe("patchIf",i.patch),be(d.name,i.patch);const a=f.tables[t];if(!a)throw new S("INTERNAL",`unknown table: ${t}`);const r=X(i.where,m(a))??e`1 = 1`;for(let l=0;l<Ge;l+=1){const[_]=await A(o,d,e`SELECT *, CASE WHEN (${r}) THEN 1 ELSE 0 END AS ${e.identifier(bt)} FROM ${e.identifier(t)} WHERE ${e.identifier("id")} = ${n}`);if(_===void 0)return{applied:!1,doc:null};const{[bt]:h,...E}=_;if(Number(h)!==1)return{applied:!1,doc:z(a,E)??null};const u=await nt(t,a,n,i.patch,E,r,{verify:i.verify});if(u)return{applied:!0,doc:u}}throw new oe(`patchIf on "${t}" lost the row to concurrent writes ${String(Ge)} times in a row; retry the operation`,"occ")},async restore(t,n){const i=await ce(t,n);if(!i)throw new S("INTERNAL",`document not found: ${t}`);const a=f.tables[i],r=a?.softDeleteMode?.field;if(!a||!r)throw new S("INTERNAL",`ctx.db.restore: table "${i}" is not a .softDelete() table`);const l=await ve(i,t),_=l?.[r]!==null&&l?.[r]!==void 0;if(await P.patch(t,{[r]:null},n),_){const h=z(a,l);h!==null&&await Z(i,t,void 0,{...h,[r]:null})}},query(t){const n=f.tables[t];if(!n)throw new S("INTERNAL",`unknown table: ${t}`);const i="the legacy query()/withIndex() reader is not available on the D1 (global) backend; use findMany",a=[];let r=0,l;const _=d.name!=="mysql",h={...m(n),fieldRef:w=>e`m.${x(w)}`,rangeTypeGuard:()=>{throw new S("INTERNAL","a path range is never pushed into a search")}},E=w=>a.every(y=>y(w)),u=async(w,y)=>{await W();const T=2*cn+w.filters.length,v=l===void 0?void 0:X(l,h,void 0,T),I=v!==void 0&&d.name==="sqlite"&&d.render(v).params.length+T>Kn.boundParams,M=I?void 0:v,k=r>0||I,D=await dn(o,d,n,t,M===void 0?w:{...w,scope:M},ln(k?void 0:y));!k&&y===void 0&&un(D);const U=D.filter(we=>E(we));return y===void 0?U:U.slice(0,y)},p=w=>{const y={async*[Symbol.asyncIterator](){if(!w)throw new S("INTERNAL",i);yield*await u(w,void 0)},async collect(){if(!w)throw new S("INTERNAL",i);return u(w,void 0)},async collectWithScores(){throw new S("GLOBAL_SEARCH_SCORES_UNSUPPORTED",`collectWithScores() is not supported on \`.global()\` tables (table "${t}") — relevance scores are not yet surfaced on this backend; use .collect() instead`)},filter(T){a.push(T);const v=Wn(T);return v!==void 0&&Un(v,n.shape,_)?l=F(l,v):r+=1,y},async first(){if(!w)throw new S("INTERNAL",i);return(await u(w,1))[0]??null},order(){return y},async paginate(T){if(!w)throw new S("INTERNAL",i);const v=sn(T);return an(await u(w,fn(v)),v)},async take(T){if(!w)throw new S("INTERNAL",i);return u(w,T)},async unique(){if(!w)throw new S("INTERNAL",i);const T=await u(w,2);if(T.length>1)throw new Hn(`unique() on table "${t}" matched ${String(T.length)} documents; expected at most one`);return T[0]??null},withGeoIndex(){throw new S("INTERNAL",`geo indexes are not supported on \`.global()\` tables (table "${t}") — geospatial queries run only on sharded tables`)},withIndex(){throw new S("INTERNAL",i)},withSearchIndex(T,v){const I=(n.searchIndexes??[]).find(k=>k.name===T);if(!I)throw new S("INTERNAL",`unknown search index "${T}" on table "${t}"`);const M={definition:I,field:I.field,filters:[],hasQuery:!1,indexName:T,query:""};if(v(rn(M,t,tn(I.language))),!M.hasQuery)throw new S("INTERNAL",`search index "${T}" on table "${t}" requires a .search(field, query) call`);return p(M)}};return y};return p(void 0)},async rank(t,n,i){const a=f.tables[t];if(!a)throw new S("INTERNAL",`unknown table: ${t}`);const r=a.rankIndexes?.find(D=>D.name===n);if(!r)throw new S("INTERNAL",`unknown rankIndex "${n}" on table "${t}"`);if(i.restrictsCounts)throw new dt(t);if(await W(),!await De(t,r))return null;const _=typeof i.row=="string"?i.row:i.row._id;if(!_)return null;const h=he(t,r.name),E=r.sortBy.map((D,U)=>pe(U)),u=ie(["__partition__",...E]),w=(await A(o,d,e`SELECT ${u} FROM ${e.identifier(h)} WHERE ${e.identifier("__id__")} = ${_}`))[0];if(!w)return null;const y=w.__partition__,T=F(i.baseWhere,i.where);at(T,f,t,"rank");const v=ct(r,T);if(v&&Ce(r.partitionBy??[],v)!==y)return null;const I=li(d.name,r,E,w,_),M=await A(o,d,e`SELECT COUNT(*) AS c FROM ${e.identifier(h)} WHERE ${e.identifier("__partition__")} = ${y} AND (${I})`),k=await A(o,d,e`SELECT COUNT(*) AS c FROM ${e.identifier(h)} WHERE ${e.identifier("__partition__")} = ${y}`);return{position:Number(M[0]?.c??0)+1,total:Number(k[0]?.c??0)}},async rankPage(t,n,i={}){at(F(i.baseWhere,i.where),f,t,"rankPage");const a=f.tables[t];if(!a)throw new S("INTERNAL",`unknown table: ${t}`);const r=a.rankIndexes?.find(O=>O.name===n);if(!r)throw new S("INTERNAL",`unknown rankIndex "${n}" on table "${t}"`);if(await W(),!await De(t,r))return{continueCursor:null,isDone:!0,page:[]};const _=he(t,r.name),h=r.sortBy.map((O,ee)=>pe(ee)),E=Math.max(1,Math.min(1e3,Math.floor(i.take??100))),u=F(i.baseWhere,i.where),p=ct(r,u),w=fi(r,h),y=e.join(w.flatMap(O=>Wt(d,{direction:O.direction,field:O.column,nullable:O.nullable})),e`, `),T=[];typeof i.partitionKey=="string"?T.push(e`${e.identifier("__partition__")} = ${i.partitionKey}`):p&&T.push(e`${e.identifier("__partition__")} = ${Ce(r.partitionBy??[],p)}`);const v=Rn(i);if(v!==void 0){const O=ui(d.name,w,v);O!==void 0&&T.push(O)}const I=ie([ye,"__partition__",...h]);let M=e`SELECT ${I} FROM ${e.identifier(_)}`;T.length>0&&(M=e`${M} WHERE ${e.join(T,e` AND `)}`),M=e`${M} ORDER BY ${y} LIMIT ${e.raw(String(E+1))}`;const k=await A(o,d,M),D=k.length>E,U=D?k.slice(0,E):k,we=U.map(O=>O[ye]),Ie=await wi(o,d,a,t,we);let $e=null;const H=U.at(-1);if(D&&H!==void 0){const O=[H.__partition__,...h.map(ee=>H[ee]),H[ye]];$e=$i(O)}return{continueCursor:$e,isDone:!D,page:Ie}},async replace(t,n,i,a){Fe("replace",n),be(d.name,n);const r=await ce(t,i);if(!r)throw new S("INTERNAL",`document not found: ${t}`);const l=f.tables[r];if(!l)throw new S("INTERNAL",`document not found: ${t}`);const _=await ve(r,t);if(_===void 0)throw new S("INTERNAL",`document not found: ${t}`);const E=yn(f,r,"update")||(l.aggregateIndexes??[]).length>0||(l.rankIndexes??[]).length>0?z(l,_)??void 0:void 0,u=a?.allowExplicitId&&typeof n._creationTime=="number"?n._creationTime:void 0,p=typeof _._creationTime=="number"?_._creationTime:void 0,w=u??p??b(),y={...n,_creationTime:w,_id:t};je(l,n,y,J),We(l,y),j(r,"before","update")&&await G("before","update",{doc:{...y},id:t,op:"update",previous:E,table:r}),await Se(r),await ge(r);const T=Object.keys(l.shape),v=e.join([e`${e.identifier("_creationTime")} = ${w}`,...T.map(I=>e`${e.identifier(I)} = ${ne(l,I,y[I]??null)}`)],e`, `);await Oe(r,"UPDATE",v,_),await le(r,E,y),await Z(r,t,E,y),await fe(r,t,{document:y,previous:E,written:Be(_,w)}),await ae(r,t,"update",y),j(r,"after","update")&&await G("after","update",{doc:y,id:t,op:"update",previous:E,table:r})}};return et={db:P,scheduler:Kt},P};export{ji as createSqlCtxDb,Yi as decodeGlobalRow,Ii as readSqlCdcChangedTables,Ki as readSqlCdcChanges,Ht as readSqlCdcFloor,yi as runSqlCdcMigration,Ti as sweepSqlCdcRetention};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as O}from"@lunora/errors";import{aggregateTableName as F,rankTableName as j,bigintSqlKey as K,sortColumnName as N}from"@lunora/shard-engine";import{sql as t}from"drizzle-orm";import{e as G,a as g,B as R,r as Y,i as H,U as V,b as W}from"./value-codec-CiS1h_97.mjs";import{O as _,t as X,a as m,q as h,c as w,b as d,d as Q,s as z,e as J}from"./sql-exec-UH-aJj2C.mjs";const Z=new Set(["any","null"]),C=(e,n)=>{const i=n.kindOf(e);return i!==void 0&&Z.has(i)||n.nullableOf(e)?!0:i==="union"&&(n.membersOf(e)??[]).some(o=>C(o,n))},T=e=>{const n=e._meta?.inner;return e.kind==="optional"&&n?T(n):e},L=e=>C(e,{kindOf:G,membersOf:n=>T(n)._meta?.members,nullableOf:n=>T(n)._meta?.column?.notNull===!1}),q=(e,n,i=!1)=>n.columnType(g(e),{unique:i||e._meta?.column?.unique===!0}),v=e=>{const n=new Set;for(const i of e.indexes)i.unique===!0&&i.fields.length===1&&n.add(i.fields[0]);return n},nn=(e,n,i)=>{if(i.indexKeyPrefix!==void 0)for(const o of n.indexes){if(o.unique!==!0||o.fields.length<=1)continue;const r=o.fields.filter(s=>{const a=n.shape[s];return a!==void 0&&i.indexKeyPrefix?.(g(a))!==void 0});if(r.length>0)throw new Error(`${e}.${o.name}: a composite unique index cannot cover ${r.join(", ")} in full on this engine (its key limit forces a prefix, which would constrain the prefix rather than the value). Split it into single-column unique indexes, or store those fields as a bounded type.`)}},en=e=>e._meta?.column?.notNull===!0&&e.kind!=="optional"&&!L(e),tn=(e,n,i)=>{const o=[],r=v(n);for(const[l,c]of Object.entries(n.shape)){if(!c._meta?.column)continue;const f=en(c)?" NOT NULL":"";o.push(t`${t.identifier(l)} ${t.raw(`${q(c,i,r.has(l))}${f}`)}`)}const s=[...i.frameworkColumns().map(l=>t`${t.identifier(l.name)} ${t.raw(l.type)}`),t`${t.identifier(_)} ${t.raw(i.companionTypes.integer)}`],a=s.length+o.length,u=i.maxTableColumns;if(u!==void 0&&a>u){const l=a-1===u,c="Move one or more fields into a single object field, or split the table — either way the existing rows need a data migration (`defineMigration` + `lunora migrate up`); there is no in-place path.";throw new O("VALIDATION_ERROR",`@lunora/sql-store: global table "${e}" needs ${String(a)} columns, over this engine's ${String(u)}-column limit. ${l?`One of them is "${_}", the row version every guarded write reads, which caps declared fields at ${String(u-s.length)}. A table provisioned at ${String(u)} columns before that column existed cannot be widened to hold it. `:""}${c}`)}return t.join([...s,...o],t`, `)},on=e=>{if(e.indexKeyPrefix?.("string")===void 0)return t`${d("_creationTime")}, ${d("id")}`},x=async(e,n,i,o)=>{const r=t.join(i.refs.map(a=>t`${a} IS NOT NULL`),t` AND `);if((await h(e,n,t`SELECT ${i.columns} FROM ${t.identifier(i.table)} WHERE ${r} GROUP BY ${i.columns} HAVING COUNT(*) > 1 LIMIT 1`)).length>0)throw new O("INTERNAL",`unique index "${i.name}" on "${i.table}" ${o}: existing rows are duplicates under it. De-duplicate the table with a data migration first.`)},rn=async(e,n,i)=>{if(!i.unique){await w(e,n,i);return}try{await w(e,n,i)}catch(o){throw n.isUniqueViolation(o)&&await x(e,n,i,"cannot be created"),o}},U=e=>{const n=e.indexOf("(");return n===-1?void 0:e.slice(n,e.lastIndexOf(")")+1)},sn=(e,n)=>{const i=n.unique?t`UNIQUE `:t``;return U(e.render(t`CREATE ${i}INDEX ${t.identifier(n.name)} ON ${t.identifier(n.table)} (${n.columns})`).sql)},an=(e,n,i)=>{const o=sn(e,i);return o!==void 0&&o!==U(n)},un=async(e,n,i)=>{if(n.name!=="sqlite")return;const r=(await h(e,n,t`SELECT sql FROM sqlite_master WHERE type = 'index' AND name = ${i.name} AND tbl_name = ${i.table}`))[0]?.sql;typeof r=="string"&&an(n,r,i)&&(i.unique&&await x(e,n,i,"cannot be re-created with its new column list; the previous index is left in place"),await m(e,n,t`DROP INDEX IF EXISTS ${t.identifier(i.name)}`))},ln=(e,n,i)=>{nn(e,n,i);const o=v(n),r=u=>{const l=d(u),c=n.shape[u],f=c&&i.indexKeyPrefix&&c._meta?.column?.unique!==!0&&!o.has(u)?i.indexKeyPrefix(g(c)):void 0;return f===void 0?l:t`${l}(${t.raw(String(f))})`},s=on(i),a=[];for(const u of n.indexes){const l=u.fields.map($=>r($)),c=t.join(l,t`, `),f=u.unique??!1;a.push({columns:f||s===void 0?c:t`${c}, ${s}`,kind:"declared",name:`${e}_${u.name}`,refs:l,table:e,unique:f})}s!==void 0&&a.push({columns:s,kind:"default-order",name:`${e}__by_creation`,refs:[],table:e,unique:!1});for(const[u,l]of X(n)){if(!l.unique)continue;const c=r(u);a.push({columns:c,kind:"unique-column",name:`${e}_unique_${u}`,refs:[c],table:e,unique:!0})}return a},cn=async(e,n,i,o)=>{for(const r of ln(n,i,o)){if(r.kind==="default-order"){await w(e,o,r);continue}r.kind==="declared"&&await un(e,o,r),await rn(e,o,r)}},A="__lunora_migration_state",fn=async(e,n)=>{await m(e,n,t`CREATE TABLE IF NOT EXISTS ${t.identifier(A)} (${t.identifier("name")} ${t.raw(n.companionTypes.key)} PRIMARY KEY)`)},dn=async(e,n,i)=>(await h(e,n,t`SELECT ${t.identifier("name")} FROM ${t.identifier(A)} WHERE ${t.identifier("name")} = ${i}`)).length>0,mn=async(e,n,i,o,r,s)=>{r>0&&s!==void 0&&console.warn(`[@lunora/sql-store] storage migration "${i}" on "${o}": ${String(r)} row(s) ${s} and were left as stored — range filters and ORDER BY over them stay wrong until they are corrected by hand.`);try{await m(e,n,t`INSERT INTO ${t.identifier(A)} (${t.identifier("name")}) VALUES (${i})`)}catch(a){if(!n.isUniqueViolation(a))throw a}},$n=100,gn=(e,n)=>{const i=[],o=[];for(const r of n.fields){const s=e[r],a=n.convert(s);a!==void 0&&(i.push(t`${d(r)} = ${a}`),o.push(t`${d(r)} = ${s}`))}return{assignments:i,guards:o}},hn=e=>t.join(e.fields.map(n=>e.probe(n)),t` OR `),k=async(e,n,i,o)=>{if(o.fields.length===0||(await fn(e,i),await dn(e,i,o.marker)))return;const r=hn(o),s=t.join([d("id"),...o.fields.map(l=>d(l))],t`, `);let a="",u=0;for(;;){const l=await h(e,i,t`SELECT ${s} FROM ${t.identifier(n)} WHERE (${r}) AND ${d("id")} > ${a} ORDER BY ${d("id")} LIMIT ${t.raw(String($n))}`);if(l.length===0){await mn(e,i,o.marker,n,u,o.describeUnconvertible);return}const c=[];for(const $ of l){const{id:p}=$,{assignments:S,guards:P}=typeof p=="string"?gn($,o):{assignments:[],guards:[]};if(S.length===0){u+=1;continue}c.push(t`UPDATE ${t.identifier(n)} SET ${t.join(S,t`, `)} WHERE ${d("id")} = ${p} AND ${t.join(P,t` AND `)}`)}await J(e,i,c);const f=l.at(-1)?.id;if(typeof f!="string")return;a=f}},b=e=>`'${e}%'`,pn=e=>Object.entries(e.shape).filter(([,n])=>n._meta?.column!==void 0&&g(n)==="bigint").map(([n])=>n),wn=e=>Object.entries(e.shape).filter(([,n])=>n._meta?.column!==void 0&&H(g(n))).map(([n])=>n),D=(e,n)=>({convert:i=>{if(!(typeof i!="string"||i.length===R))try{return K(BigInt(i))}catch{return}},describeUnconvertible:"hold a value the order-preserving key cannot represent (non-numeric text, or a magnitude past 39 digits)",fields:pn(n),marker:`bigint-key:${e}`,probe:i=>t`(${d(i)} IS NOT NULL AND LENGTH(${d(i)}) <> ${R})`}),En=async(e,n,i,o)=>k(e,n,o,D(n,i)),M=(e,n)=>({convert:Y,fields:wn(n),marker:`untyped-number-key:${e}`,probe:i=>t`(${d(i)} LIKE ${t.raw(b(V))} AND ${d(i)} NOT LIKE ${t.raw(b(W))})`}),yn=async(e,n,i,o)=>k(e,n,o,M(n,i)),I=new Set,E=(e,n)=>{I.has(e)||(I.add(e),console.warn(`[@lunora/sql-store] ${n}`))},_n=e=>{const n=e._meta?.column;return n?.defaultFn?n.defaultFn():n&&"defaultValue"in n?n.defaultValue:void 0},Tn=(e,n,i)=>i._meta?.column?.unique===!0||e.indexes.some(o=>o.unique===!0&&o.fields.includes(n)),Ln=async(e,n,i,o,r,s)=>{if(s.kind==="optional"||L(s))return;const a=Tn(i,r,s),u=a?void 0:_n(s),l=`UPDATE "${n}" SET "${r}" = … WHERE "${r}" IS NULL`;if(u===void 0){const c=a?"is unique, so one default cannot be written to every row":"has no default";E(`backfill:${n}.${r}`,`"${n}.${r}" was added as a required field that ${c}, so rows written before it read NULL. Backfill it with a migration (${l}).`);return}try{await m(e,o,t`UPDATE ${t.identifier(n)} SET ${t.identifier(r)} = ${z(i,r,u)} WHERE ${t.identifier(r)} IS NULL`)}catch(c){E(`backfill:${n}.${r}`,`could not backfill "${n}.${r}" from its default (${c instanceof Error?c.message:String(c)}); rows written before it read NULL. Run ${l}.`)}},An=(e,n)=>[[_,n.companionTypes.integer],...Object.entries(e.shape).filter(([,i])=>i._meta?.column!==void 0).map(([i,o])=>[i,q(o,n),o])],Sn=async(e,n,i,o)=>{const r=An(i,o),s="t",a=async u=>{try{return await h(e,o,t`SELECT ${t.join(u.map(l=>Q(s,l)),t`, `)} FROM ${t.identifier(n)} AS ${t.identifier(s)} LIMIT 0`),!0}catch{return!1}};if(!await a(r.map(([u])=>u)))for(const[u,l,c]of r)await a([u])||(await m(e,o,t`ALTER TABLE ${t.identifier(n)} ADD COLUMN ${t.identifier(u)} ${t.raw(l)}`),c!==void 0&&await Ln(e,n,i,o,u,c))},Rn=(e,n,i)=>{const o=typeof i.charset=="string"?` CHARACTER SET ${i.charset} COLLATE ${String(i.collation)}`:"";return`ALTER TABLE \`${e}\` MODIFY COLUMN \`${n}\` ${String(i.type)}${o} NULL`},bn=async(e,n,i,o)=>{if(o.name==="sqlite")return;const r=Object.keys(i.shape).filter(s=>{const a=i.shape[s];return a._meta?.column?.notNull===!0&&a.kind!=="optional"&&L(a)});if(r.length!==0)try{const s=o.name==="postgres"?t`column_name AS ${t.identifier("name")}`:t`column_name AS ${t.identifier("name")}, column_type AS ${t.identifier("type")}, character_set_name AS ${t.identifier("charset")}, collation_name AS ${t.identifier("collation")}`,a=o.name==="postgres"?t`table_schema = ANY (current_schemas(false))`:t`table_schema = DATABASE()`,u=await h(e,o,t`SELECT ${s} FROM information_schema.columns WHERE ${a} AND table_name = ${n} AND is_nullable = 'NO' AND column_name IN (${t.join(r.map(l=>t`${l}`),t`, `)})`);for(const l of u){const c=String(l.name);if(o.name==="postgres"){await m(e,o,t`ALTER TABLE ${t.identifier(n)} ALTER COLUMN ${t.identifier(c)} DROP NOT NULL`);continue}E(`${n}.${c}`,`"${n}"."${c}" accepts null in the schema but its column is NOT NULL, so writing null there fails. Relaxing it on MySQL can rebuild the table, so it is not done at startup; run this once, in a maintenance window: ${Rn(n,c,l)};`)}}catch(s){E(`${n}:relax-failed`,`could not relax NOT NULL on the null-accepting columns of "${n}" (${r.join(", ")}): ${s instanceof Error?s.message:String(s)}. Writing null to them fails until the constraint is dropped by hand.`)}},In=async(e,n,i,o)=>{const r=tn(n,i,o);await m(e,o,t`CREATE TABLE IF NOT EXISTS ${t.identifier(n)} (${r})`),await Sn(e,n,i,o),await bn(e,n,i,o),await cn(e,n,i,o),await En(e,n,i,o),await yn(e,n,i,o)},kn=(e,n)=>[D(e,n),M(e,n)].filter(i=>i.fields.length>0),Dn=async(e,n,i,o)=>{for(const[r,s]of Object.entries(n.tables))s.shardMode?.kind!=="global"||o!==void 0&&!o.has(r)||await In(e,r,s,i)},B=e=>e.shardMode===void 0||e.shardMode.kind==="global",Mn=async(e,n,i)=>{const{integer:o,key:r,real:s}=i.companionTypes;for(const[a,u]of Object.entries(n.tables)){const l=u.aggregateIndexes;if(!(!l||l.length===0||!B(u)))for(const c of l){const f=F(a,c.name);await m(e,i,t`CREATE TABLE IF NOT EXISTS ${t.identifier(f)} (${t.identifier("__key__")} ${t.raw(r)} PRIMARY KEY, ${t.identifier("__value__")} ${t.raw(s)}, ${t.identifier("__count__")} ${t.raw(o)} NOT NULL DEFAULT 0)`),i.name==="sqlite"&&((await h(e,i,t`PRAGMA table_info(${t.identifier(f)})`)).some(p=>p.name==="__count__")||await m(e,i,t`ALTER TABLE ${t.identifier(f)} ADD COLUMN ${t.identifier("__count__")} ${t.raw(o)} NOT NULL DEFAULT 0`))}}},y=(e,n,i,o)=>{const r=e.name==="mysql"&&o?t`${t.identifier(n)}(191)`:t`${t.identifier(n)}`;return t`${r} ${t.raw(i)}`},On=(e,n,i)=>{const o=[y(e,"__partition__","ASC",!0)];for(const[r,s]of n.sortBy.entries()){const a=i.shape[s.field],u=a!==void 0&&e.indexKeyPrefix?.(g(a))!==void 0;o.push(y(e,N(r),s.direction==="desc"?"DESC":"ASC",u))}return o.push(y(e,"__id__","ASC",!0)),o},Nn=(e,n,i)=>n.sortBy.map((o,r)=>{const s=i.shape[o.field],a=e.columnType(s?g(s):void 0);return t`${t.identifier(N(r))} ${t.raw(a)}`}),Bn=async(e,n,i)=>{const{key:o}=i.companionTypes;for(const[r,s]of Object.entries(n.tables)){const a=s.rankIndexes;if(!(!a||a.length===0||!B(s)))for(const u of a){const l=j(r,u.name),c=Nn(i,u,s),f=c.length>0?t`, ${t.join(c,t`, `)}`:t``;await m(e,i,t`CREATE TABLE IF NOT EXISTS ${t.identifier(l)} (${t.identifier("__id__")} ${t.raw(o)} PRIMARY KEY, ${t.identifier("__partition__")} ${t.raw(o)} NOT NULL${f})`);const $=On(i,u,s),p=`${r}__rank_${u.name}__btree`;await w(e,i,{columns:t.join($,t`, `),name:p,table:l,unique:!1})}}};export{A as MIGRATION_STATE_TABLE,sn as declaredIndexColumnList,tn as globalTableColumnsDdl,ln as globalTableIndexSpecs,U as indexColumnList,an as indexShapeChanged,kn as legacyRewrites,hn as legacyRowPredicate,In as provisionGlobalTable,Dn as provisionGlobalTablesSequentially,Mn as runSqlAggregateMigrations,Bn as runSqlRankMigrations,An as wantedGlobalColumns};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{sql as l}from"drizzle-orm";import{provisionGlobalTablesSequentially as m,MIGRATION_STATE_TABLE as E,globalTableColumnsDdl as w,globalTableIndexSpecs as g,legacyRewrites as I,wantedGlobalColumns as b,indexShapeChanged as R,legacyRowPredicate as O}from"./runSqlAggregateMigrations-CUAkKj8T.mjs";import{r as y,q as p}from"./sql-exec-UH-aJj2C.mjs";const $=50,u=e=>e.replaceAll(/[A-Z]/gu,s=>s.toLowerCase()),T=new Map([["'","'"],["[","]"],["`","`"],['"','"']]),A=/^[\w$]+/u,h=(e,s)=>{const n=T.get(e[s]);let t=s+1;for(;t<e.length;)if(e[t]!==n)t+=1;else if(n!=="]"&&e[t+1]===n)t+=2;else return t+1;return t},C=e=>{const s=e.trim(),n=T.get(s[0]??"");if(n===void 0)return A.exec(s)?.[0];const t=h(s,0),o=s.slice(1,s[t-1]===n?t-1:t);return n==="]"?o:o.replaceAll(`${n}${n}`,n)},q=e=>{const s=new Set,n=i=>{const a=C(i);a!==void 0&&s.add(a)};let t=0,o=0,r=0;for(;r<e.length;){const i=e[r];if(T.has(i)){r=h(e,r);continue}if(i==="(")t+=1,o=t===1?r+1:o;else if(i===")"){if(t-=1,t===0)return n(e.slice(o,r)),s}else i===","&&t===1&&(n(e.slice(o,r)),o=r+1);r+=1}return s},M=async(e,s)=>{try{const n=await p(e,s,l`SELECT type, name, sql FROM sqlite_master WHERE type IN ('table', 'index')`),t=new Set,o=new Map,r=new Map;for(const a of n){const f=String(a.name);if(f.startsWith("sqlite_"))continue;const c=typeof a.sql=="string"?a.sql:void 0;a.type==="table"?(t.add(u(f)),o.set(u(f),new Set([...c===void 0?[]:q(c)].map(d=>u(d))))):r.set(u(f),c)}const i=new Set;if(t.has(u(E)))for(const a of await p(e,s,l`SELECT name FROM ${l.identifier(E)}`))i.add(String(a.name));return{columns:o,indexes:r,markers:i,tables:t}}catch{return}},S=e=>{const s=e.unique?l`UNIQUE `:l``;return l`CREATE ${s}INDEX IF NOT EXISTS ${l.identifier(e.name)} ON ${l.identifier(e.table)} (${e.columns})`},v=(e,s,n)=>n.length===0?[]:[l`CREATE TABLE IF NOT EXISTS ${l.identifier(E)} (${l.identifier("name")} ${l.raw(e.companionTypes.key)} PRIMARY KEY)`,...n.map(t=>l`INSERT OR IGNORE INTO ${l.identifier(E)} (${l.identifier("name")}) SELECT ${t.marker} WHERE NOT EXISTS (SELECT 1 FROM ${l.identifier(s)} WHERE ${O(t)})`)],k=(e,s,n,t)=>{const o=w(e,s,n),r=g(e,s,n),i=I(e,s);if(!t.tables.has(u(e)))return[l`CREATE TABLE IF NOT EXISTS ${l.identifier(e)} (${o})`,...r.map(c=>S(c)),...v(n,e,i)];const a=t.columns.get(u(e))??new Set;if(b(s,n).some(([c])=>!a.has(u(c)))||i.some(c=>!t.markers.has(c.marker)))return;const f=[];for(const c of r){if(!t.indexes.has(u(c.name))){f.push(S(c));continue}const d=t.indexes.get(u(c.name));if(c.kind==="declared"&&typeof d=="string"&&R(n,d,c))return}return f},L=e=>{const s=[];let n=[],t=0;for(const o of e)n.length>0&&t+o.statements.length>$&&(s.push(n),n=[],t=0),n.push(o),t+=o.statements.length;return n.length>0&&s.push(n),s},F=async(e,s,n)=>{if(!e.batch||n.name!=="sqlite"){await m(e,s,n);return}const t=await M(e,n);if(t===void 0){await m(e,s,n);return}const o=[],r=new Set;for(const[i,a]of Object.entries(s.tables)){if(a.shardMode?.kind!=="global")continue;const f=k(i,a,n,t);f===void 0?r.add(i):f.length>0&&o.push({statements:f,table:i})}for(const i of L(o))try{await y(e,n,i.flatMap(a=>a.statements))}catch{for(const a of i)r.add(a.table)}await m(e,s,n,r)};export{q as columnNamesOfCreateTable,F as runSqlGlobalTableMigrations};
|