@lunora/sql-store 1.0.0-alpha.139 → 1.0.0-alpha.140
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/{backfillSqlSearchIndexes-CThTvyK4.mjs → backfillSqlSearchIndexes-BLqaJ3r6.mjs} +1 -1
- package/dist/packem_shared/createSqlCtxDb-Dv950TKo.mjs +7 -0
- package/dist/packem_shared/{ctx-db-search-Dxuv99A0.mjs → ctx-db-search-DRg-JPjJ.mjs} +1 -1
- package/package.json +2 -2
- package/dist/packem_shared/createSqlCtxDb-D-mXDmWV.mjs +0 -7
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-Dv950TKo.mjs";import{runSqlAggregateMigrations as d,runSqlGlobalTableMigrations as S,runSqlRankMigrations as c}from"./packem_shared/runSqlAggregateMigrations-CmsPHIJb.mjs";import{d as g,r as f}from"./packem_shared/ctx-db-search-DRg-JPjJ.mjs";import{S as m,m as x}from"./packem_shared/ctx-db-search-state-DGbsWTp6.mjs";import{f as p,e as u,b as h,s as M,t as T}from"./packem_shared/value-codec-DC_dHxUY.mjs";import{decodeGlobalRow as E}from"./packem_shared/decodeGlobalRow-VOl_TIbk.mjs";export{m as SEARCH_STATE_TABLE,g as backfillSqlSearchIndexes,a as createSqlCtxDb,p as decodeBigint,E as decodeGlobalRow,u as effectiveColumnKind,x as migrateSearchState,o as readSqlCdcChangedTables,t as readSqlCdcChanges,l as readSqlCdcFloor,d as runSqlAggregateMigrations,s as runSqlCdcMigration,S as runSqlGlobalTableMigrations,c as runSqlRankMigrations,f as runSqlSearchMigrations,h as sqliteDecode,M as sqliteEncode,n as sweepSqlCdcRetention,T as tryJsonParse};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./ctx-db-search-state-DGbsWTp6.mjs";import{d as S,c as s,a as i,r as l}from"./ctx-db-search-
|
|
1
|
+
import"./ctx-db-search-state-DGbsWTp6.mjs";import{d as S,c as s,a as i,r as l}from"./ctx-db-search-DRg-JPjJ.mjs";import"./decodeGlobalRow-VOl_TIbk.mjs";export{S as backfillSqlSearchIndexes,s as createSearchSync,i as runSqlSearch,l as runSqlSearchMigrations};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import{LunoraError as T}from"@lunora/errors";import{m as Ut,D as Pt}from"./ctx-db-search-state-DGbsWTp6.mjs";import{c as Ht,r as Kt,A as jt,C as Gt,R as Vt,u as Yt,a as Xt,T as zt,x as Qt,b as Jt}from"./ctx-db-search-DRg-JPjJ.mjs";import{d as Ii}from"./ctx-db-search-DRg-JPjJ.mjs";import{literalInList as Zt,throwingScheduler as en,CDC_LOG_TABLE as H,cdcForkedError as tn,cursorBelowRetainedFloor as nn,cdcTrimmedError as on,assertNoExplicitUndefined as nt,hasTrigger as rn,runRowValidators as De,assertFlatPredicate as it,mergeWhere as W,rankTableName as $e,sortColumnName as pe,resolveRankPartition as ot,encodePartitionKey as Le,resolveRankSeekTuple as sn,RANK_TIEBREAK as Re,CountRlsUnsupportedError as rt,stripReservedPatchFields as an,normalizeIdStructurally as cn,assertValidClientId as dn,aggregateSqlFunction as he,softDeleteScope as Ee,compileWhereSql as J,normalizeOrderKeys as ln,uniqueIndexFields as un,equalityPinnedFields as fn,buildSeekWhere as wn,decodeCursor as _n,resolveRelationPredicates as st,resolveWith as at,relationHooks as ct,applySelect as dt,encodeCursor as $n,NotFoundError as hn,applyOnDelete as En,ConflictError as Ae,normalizeCountArgument as yn,selectIndexForCount as pn,encodeAggregateKey as Y,aggregateTableName as Z,selectIndexForAggregate as Rn,readAggregateValue as Be,runTriggers as Sn,matchesRankStaticWhere as lt,rankPivotConditionSql as St,CURSOR_PREFIX as vn,selectIndexForGroupBy as gn,tiebreakDirectionFor as Tn,fanOutScalarCounts as mn,matchesStaticWhere as We,foldAggregateTally as ut,coerceAggregateNumber as Fe,whereOfFilter as bn,isPushableWhere as In,CDC_LOG_TABLE_SEQ_INDEX as Cn,NotUniqueError as Ln,renderSql as An,WORKERD_SQLITE_LIMITS as kn}from"@lunora/shard-engine";import{sql as e}from"drizzle-orm";import{d as vt,b as Mn,e as gt,n as Nn,c as qn}from"./value-codec-DC_dHxUY.mjs";import{runSqlGlobalTableMigrations as Dn,runSqlAggregateMigrations as Bn,runSqlRankMigrations as Wn}from"./runSqlAggregateMigrations-CmsPHIJb.mjs";import{queryAll as I,queryRun as N,createIndexIfNotExists as Fn,decodeRow as ne,serializeDocumentColumn as ee,OCC_VERSION_COLUMN as ye,columnRefSql as F,decodeRows as ft,tableColumns as Tt,nextRowVersion as wt,serializeColumnValue as Oe,forEachRowPaged as _t,BACKFILL_BATCH_SIZE as xe,nullSafeEqualsSql as Ke,queryBatch as $t}from"./decodeGlobalRow-VOl_TIbk.mjs";import{decodeGlobalRow as Li}from"./decodeGlobalRow-VOl_TIbk.mjs";const On=(a,d)=>{if(a.size<d)return;const h=a.keys().next().value;h!==void 0&&a.delete(h)},ht=50,xn=(a,d={})=>{const h=d.maxBatch??ht,r=Number.isInteger(h)&&h>0?h:ht,R=new Map;let f=!1;const c=async(v,m)=>{const C=[...m.keys()];for(let A=0;A<C.length;A+=r){const q=C.slice(A,A+r);try{const x=await a(v,q);for(const G of q){const V=x.get(G);for(const re of m.get(G)??[])re.resolve(V)}}catch(x){for(const G of q)for(const V of m.get(G)??[])V.reject(x)}}},M=()=>{f=!1;const v=[...R.entries()];R.clear();for(const[m,C]of v)c(m,C).catch(()=>{})};return{async load(v,m){return new Promise((C,A)=>{let q=R.get(v);q||(q=new Map,R.set(v,q));const x=q.get(m);x?x.push({reject:A,resolve:C}):q.set(m,[{reject:A,resolve:C}]),f||(f=!0,queueMicrotask(M))})}}},Un=new Set(["_id","id"]),mt=(a,d)=>a.name!=="postgres"||d.nullable!==!0?"":d.direction==="desc"?" NULLS LAST":" NULLS FIRST",Pn=(a,d)=>{const h=a.map(r=>e`${F(r.field)} ${e.raw(`${r.direction==="desc"?"DESC":"ASC"}${mt(d,r)}`)}`);if(!a.some(r=>Un.has(r.field))){const r=Tn(a);h.push(e`${F("id")} ${e.raw(r==="desc"?"DESC":"ASC")}`)}return e.join(h,e`, `)},Et=50,Hn=(a,d,h)=>{const r={...d};for(const[R,f]of Tt(a)){if(f.serverDefault){r[R]=f.serverDefault({auth:h});continue}r[R]===void 0&&(f.defaultFn?r[R]=f.defaultFn():"defaultValue"in f&&(r[R]=f.defaultValue))}return r},yt=(a,d,h,r)=>{for(const[R,f]of Tt(a)){if(f.serverDefault){R in d&&(h[R]=f.serverDefault({auth:r}));continue}f.onUpdateFn&&!(R in d)&&(h[R]=f.onUpdateFn())}},Kn=128,jn=()=>{const a=new Map;return{delete:d=>{a.delete(d)},get:d=>{const h=a.get(d);if(h!==void 0)return a.delete(d),a.set(d,h),h},set:(d,h)=>{a.has(d)?a.delete(d):On(a,Kn),a.set(d,h)}}},Gn=async(a,d,h,r,R)=>{const f=R.get(r);if(f!==void 0)return f;const c=Object.entries(h.tables).filter(([,v])=>v.shardMode===void 0||v.shardMode.kind==="global").map(([v])=>v),M=await Promise.all(c.map(async v=>({found:(await I(a,d,e`SELECT 1 FROM ${e.identifier(v)} WHERE ${e.identifier("id")} = ${r} LIMIT 1`)).length>0,tableName:v})));for(const v of M)if(v.found)return R.set(r,v.tableName),v.tableName},ie=a=>a==null?null:Number(a),Vn=(a,d,h)=>h.map(r=>{const R={};for(const f of d){const c=a.shape[f];R[f]=Mn(r[f]??null,c&>(c))}return{key:R,value:ie(r.value)}}),Yn=(a,d,h)=>[...a.partitionBy??[],...a.sortBy.map(R=>R.field),...a.where?Object.keys(a.where):[]].every(R=>d[R]===h[R]),bt=a=>a.length===1?a[0]:e`(${e.join(a,e` AND `)})`,te=a=>e.join(a.map(d=>e`${e.identifier(d)}`),e`, `),oe=a=>e.join(a.map(d=>e`${d}`),e`, `),Xn=(a,d,h,r,R)=>{const f=[];for(let c=0;c<h.length+1;c+=1){const M=h[c],v=d.sortBy[c],m=c<h.length&&M!==void 0&&v!==void 0,C=v?.direction==="desc"?"desc":"asc",A=m?St(M,r[M],C,!1):e`${e.identifier(Re)} < ${R}`;if(A===void 0)continue;const q=h.slice(0,c).map(x=>Ke(a,e`${e.identifier(x)}`,r[x]));q.push(A),f.push(bt(q))}return f.length>0?e.join(f,e` OR `):e`1 = 0`},zn=(a,d,h)=>{if(h.length!==d.length)return;const r=[];for(const[R,f]of d.entries()){const c=St(f.column,h[R],f.direction,!0);if(c===void 0)continue;const M=[];for(let v=0;v<R;v+=1){const m=d[v];m!==void 0&&M.push(Ke(a,e`${e.identifier(m.column)}`,h[v]))}M.push(c),r.push(bt(M))}return r.length===0?e`(1 = 0)`:e`(${e.join(r,e` OR `)})`},Qn=(a,d)=>{if(a.sortBy.length===0)throw new T("INTERNAL",`rankIndex "${a.name}" requires at least one "sortBy" column for stable pagination`);const h=[{column:"__partition__",direction:"asc",nullable:!1}];for(const[r,R]of a.sortBy.entries())h.push({column:d[r]??pe(r),direction:R.direction,nullable:!0});return h.push({column:Re,direction:"asc",nullable:!1}),h},Jn=async(a,d,h,r,R)=>{const c=[];for(let C=0;C<R.length;C+=50)c.push(R.slice(C,C+50));const M=await Promise.all(c.map(async C=>I(a,d,e`SELECT * FROM ${e.identifier(r)} WHERE ${e.identifier("id")} IN (${oe(C)})`))),v=new Map;for(const C of M)for(const A of C)v.set(A.id,A);const m=[];for(const C of R){const A=ne(h,v.get(C));A&&m.push(A)}return m},Zn=a=>{const d=JSON.stringify(a),h=new TextEncoder().encode(d);let r="";for(const R of h)r+=String.fromCodePoint(R);return vn+btoa(r)},It=a=>{if(a===void 0)return!1;const d=gt(a);return d==="any"||d==="bigint"||d==="from"||d==="union"},Pe=(a,d,h)=>{if(It(a.shape[d]))throw new T("BAD_REQUEST",`${h}: "${d}" 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)`)},ei=(a,d,h,r)=>{for(const R of h)Pe(a,R,`groupBy(${d}, { by: [..."${R}"] })`);r.field!==void 0&&Pe(a,r.field,`groupBy(${d}, { agg: { op: "${r.op}", field: "${r.field}" } })`)},He="__cdc_sweep",Ct="global",ti=6e4,ni=1e4,ii=3e4;let pt=0;const oi=async(a,d)=>{const{autoincrementPrimaryKey:h,key:r,real:R,text:f}=d.companionTypes;await N(a,d,e`CREATE TABLE IF NOT EXISTS ${e.identifier(H)} (${e.identifier("seq")} ${e.raw(h)}, ${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(f)})`);const c=d.indexKeyPrefix?.("string");await Fn(a,d,{columns:e`${c===void 0?e`${e.identifier("table")}`:e`${e.identifier("table")}(${e.raw(String(c))})`}, ${e.identifier("seq")}`,name:Cn,table:H,unique:!1}),await N(a,d,e`CREATE TABLE IF NOT EXISTS ${e.identifier(He)} (${e.identifier("id")} ${e.raw(r)} NOT NULL PRIMARY KEY, ${e.identifier("lease_until")} ${e.raw(R)} NOT NULL)`),await N(a,d,e`INSERT INTO ${e.identifier(He)} (${e.identifier("id")}, ${e.identifier("lease_until")}) VALUES (${Ct}, ${0}) ${d.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${e.identifier("id")} = ${e.identifier("id")}`:e`ON CONFLICT(${e.identifier("id")}) DO NOTHING`}`)},ri=async(a,d,h)=>{const r=e`UPDATE ${e.identifier(He)} SET ${e.identifier("lease_until")} = ${h+ti} WHERE ${e.identifier("id")} = ${Ct} AND ${e.identifier("lease_until")} <= ${h}`;if(d.supportsReturning)return(await I(a,d,e`${r} RETURNING ${e.identifier("id")}`)).length>0;const R=await N(a,d,r);return(d.affectedRows?d.affectedRows(R??{rowsAffected:0}):0)>0},si=async(a,d,h,r)=>{if(!await ri(a,d,r))return;const R=r-h;await N(a,d,e`
|
|
2
|
+
DELETE FROM ${e.identifier(H)} WHERE ${e.identifier("seq")} IN (
|
|
3
|
+
SELECT ${e.identifier("seq")} FROM (
|
|
4
|
+
SELECT ${e.identifier("seq")} FROM ${e.identifier(H)} WHERE ${e.identifier("ts")} <= ${R} ORDER BY ${e.identifier("seq")} ASC LIMIT ${e.raw(String(ni))}
|
|
5
|
+
) AS ${e.identifier("expired")}
|
|
6
|
+
)
|
|
7
|
+
`)},Lt=async(a,d)=>{const h=await I(a,d,e`SELECT MIN(${e.identifier("seq")}) AS ${e.identifier("seq")} FROM ${e.identifier(H)}`),r=Number(h[0]?.seq??Number.NaN);return Number.isFinite(r)?r:void 0},ai=async(a,d)=>{if(d.name!=="sqlite")return;const h=await I(a,d,e`SELECT ${e.identifier("seq")} AS ${e.identifier("seq")} FROM ${e.identifier("sqlite_sequence")} WHERE ${e.identifier("name")} = ${H}`),r=Number(h[0]?.seq??Number.NaN);return Number.isFinite(r)?r:0},ci=a=>JSON.stringify(Nn(a)?qn(a):a),di=async(a,d,h,r,R,f,c)=>{await N(a,c,e`INSERT INTO ${e.identifier(H)} (${e.identifier("ts")}, ${e.identifier("table")}, ${e.identifier("id")}, ${e.identifier("op")}, ${e.identifier("doc")}) VALUES (${d}, ${h}, ${r}, ${R}, ${f===void 0?null:ci(f)})`)},gi=async(a,d,h)=>{const r=d.sinceSeq??0,R=Math.max(1,Math.min(d.limit??1e3,1e4)),f=await Lt(a,h),c=await ai(a,h);if(c!==void 0&&r>c)throw tn(c,r,"global");if(f!==void 0&&nn(f,r))throw on(f,r,"global");const v=(await I(a,h,e`SELECT seq, ts, ${e.identifier("table")}, id, op, doc FROM ${e.identifier(H)} WHERE seq > ${r} ORDER BY seq ASC LIMIT ${e.raw(String(R))}`)).map(m=>{const{doc:C}=m,A={id:String(m.id),op:String(m.op),seq:Number(m.seq),table:String(m.table),ts:Number(m.ts)};return typeof C=="string"?{...A,doc:vt(JSON.parse(C))}:A});return{changes:v,cursor:v.at(-1)?.seq??r}},li=async(a,d,h,r={})=>{if(r.cursorOnly){const v=await I(a,h,e`SELECT MAX(seq) AS seq FROM ${e.identifier(H)}`),m=Number(v[0]?.seq??d);return{cursor:Number.isFinite(m)?m:d,tables:[]}}const R=await I(a,h,e`SELECT ${e.identifier("table")} AS ${e.identifier("table")}, MAX(seq) AS seq FROM ${e.identifier(H)} WHERE seq > ${d} GROUP BY ${e.identifier("table")}`);let f=d;const c=[];for(const v of R){const m=Number(v.seq);Number.isFinite(m)&&m>f&&(f=m),c.push(String(v.table))}const M=r.retained===!0?await Lt(a,h):void 0;return{cursor:f,tables:c,...M===void 0?{}:{floor:M}}},ui=/string or blob too big/iu,fi=/row is too big/iu,wi=(a,d)=>{const{code:h,errno:r}=d,R=d instanceof Error?d.message:"";switch(a.name){case"mysql":return r===1118||h==="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 fi.test(R)?"the 8 KB heap page a tuple must fit once its wide columns have been TOASTed out":void 0;default:return ui.test(R)?"the storage engine's per-row ceiling (2 MB on D1)":void 0}},_i=(a,d,h)=>{const r=wi(a,d);if(r!==void 0)throw new T("PAYLOAD_TOO_LARGE",`document is too large to store in "${h}": 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.`)},Rt=(a,d,h)=>{throw a.isUniqueViolation(d)?new Ae(`unique constraint violation on "${h}"`,"unique"):(_i(a,d,h),d)},Ue=new WeakMap,Ti=a=>{const{crossShardCounter:d,crossShardReader:h,exec:r,maxRelationKeys:R,schema:f}=a,{dialect:c}=a,M={fieldRef:F,...c.name==="mysql"?{containsExpr:(t,n)=>e`LOCATE(${n}, ${t}) > 0`}:{},...c.name==="postgres"?{containsExpr:(t,n)=>e`strpos(${t}, ${n}) > 0`}:{},...c.name==="sqlite"?{}:{inList:Zt}},v=new WeakMap,m=t=>{const n=v.get(t);if(n!==void 0)return n;const i={...M,serialize:(o,s)=>ee(t,s,o)};return v.set(t,i),i},C=(t,n)=>Ke(c.name,t,n),A=t=>{const n=te(t.columns),i=oe(t.values),o=_=>c.name==="mysql"?e`VALUES(${e.identifier(_)})`:e`excluded.${e.identifier(_)}`,s=_=>c.name==="postgres"?e`${e.identifier(t.table)}.${e.identifier(_)}`:e`${e.identifier(_)}`,u=e.join(Object.entries(t.set(o,s)).map(([_,y])=>e`${e.identifier(_)} = ${y}`),e`, `),$=c.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${u}`:e`ON CONFLICT(${e.identifier(t.conflictKey)}) DO UPDATE SET ${u}`;return e`INSERT INTO ${e.identifier(t.table)} (${n}) VALUES (${i}) ${$}`},q=a.clock??(()=>Date.now()),x=a.idGenerator??(()=>crypto.randomUUID()),G=a.cdc??!1,V=typeof a.cdcRetentionMs=="number"&&Number.isFinite(a.cdcRetentionMs)&&a.cdcRetentionMs>0?a.cdcRetentionMs:void 0,re=a.auth??{identity:null,userId:null},se=async(t,n,i,o)=>{if(!G||(await di(r,q(),t,n,i,o,c),V===void 0))return;const s=q();if(!(s-pt<ii)){pt=s;try{await si(r,c,V,s)}catch{}}},At=a.scheduler??en,ke=jn();let Se=0;const Me=a.provisionScope??{},U=async()=>{const t=Ue.get(Me);if(t)return t;const n=(async()=>{await Dn(r,f,c),await Bn(r,f,c),await Wn(r,f,c),await Ut(r,c),await Kt(r,f,c),G&&await oi(r,c)})().catch(i=>{throw Ue.delete(Me),i});return Ue.set(Me,n),n},ae=async(t,n)=>{await U();const i=await Gn(r,c,f,t,ke);if(!(n!==void 0&&i!==n))return i},ce=new Map,je=async(t,n)=>{const i=Z(t,n);return(await I(r,c,c.tableExists(i))).length>0},ve=async(t,n)=>{const i=`${t}::${n.name}`,o=ce.get(i);if(o!==void 0)return o;if(!await je(t,n.name))return ce.set(i,!1),!1;const u=f.tables[t];if(!u)return ce.set(i,!1),!1;const $=n.by??[],_=new Map;await _t(r,c,u,t,E=>{if(n.where&&!We(E,n.where))return;const l=Y($,E);ut(_,l,n,E)});const y=Z(t,n.name);await N(r,c,e`DELETE FROM ${e.identifier(y)}`);const w=[];for(const[E,l]of _)w.push(e`INSERT INTO ${e.identifier(y)} (${e.identifier("__key__")}, ${e.identifier("__value__")}, ${e.identifier("__count__")}) VALUES (${E}, ${l.value}, ${l.count})`);return await $t(r,c,w),ce.set(i,!0),!0},kt=async(t,n,i,o)=>{const s=new Map;let u,$=!0;for(;$;){const _=u===void 0?o:[...o,e`${e.identifier("id")} > ${u}`],y=_.length>0?e` WHERE ${e.join(_,e` AND `)}`:e``,w=await I(r,c,e`SELECT ${e.identifier("id")}, ${F(i.field??"")} FROM ${e.identifier(n)}${y} ORDER BY ${e.identifier("id")} ASC LIMIT ${e.raw(String(xe))}`);for(const E of ft(t,w))ut(s,"",i,E);u=w.at(-1)?.id,$=w.length===xe}return s.get("")?.value??null},Mt=async(t,n,i)=>{const o=n.field??"",s=[],u=f.tables[t],$=(E,l)=>u?ee(u,E,l):Oe(l);for(const E of n.by??[]){const l=$(E,i[E]??null);s.push(l===null?e`${F(E)} IS NULL`:e`${F(E)} = ${l}`)}for(const[E,l]of Object.entries(n.where??{})){const S=l!==null&&typeof l=="object"&&!Array.isArray(l)?l.eq:l,p=$(E,S);s.push(p===null?e`${F(E)} IS NULL`:e`${F(E)} = ${p}`)}const _=s.length>0?e` WHERE ${e.join(s,e` AND `)}`:e``;if(u&&It(u.shape[o]))return kt(u,t,n,s);const y=he(n.op),w=await I(r,c,e`SELECT ${e.raw(y)}(${F(o)}) AS ${e.identifier("value")} FROM ${e.identifier(t)}${_}`);return ie(w[0]?.value)},Nt=async(t,n)=>{await N(r,c,e`DELETE FROM ${e.identifier(t)} WHERE ${e.identifier("__key__")} = ${n} AND ${e.identifier("__count__")} <= 0`)},qt=async(t,n,i,o)=>{const s=Z(t,n.name),{op:u}=n,$=n.field??"",_=i&&(!n.where||We(i,n.where))?i:void 0,y=o&&(!n.where||We(o,n.where))?o:void 0;if(!(!_&&!y)){if(u==="count"||u==="sum"||u==="avg"){const w=new Set;for(const[E,l]of[[_,-1],[y,1]]){if(!E)continue;const S=u==="count"?1:Fe(E[$]);if(S===void 0)continue;const p=Y(n.by??[],E);w.add(p),await N(r,c,A({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(g,b)=>({__count__:e`${b("__count__")} + ${g("__count__")}`,__value__:u==="count"?e`${b("__value__")} + ${g("__value__")}`:e`COALESCE(${b("__value__")}, 0) + ${g("__value__")}`}),table:s,values:[p,l*S,l]}))}for(const E of w)await Nt(s,E);return}if(_){const w=Y(n.by??[],_),E=Fe(_[$]),S=(await I(r,c,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(s)} WHERE ${e.identifier("__key__")} = ${w}`))[0],p=ie(S?.value),g=(S?.count??0)-1;if(g<=0)await N(r,c,e`DELETE FROM ${e.identifier(s)} WHERE ${e.identifier("__key__")} = ${w}`);else if(S&&E!==void 0&&p!==null&&E===p){const b=await Mt(t,n,_);await N(r,c,e`UPDATE ${e.identifier(s)} SET ${e.identifier("__value__")} = ${b}, ${e.identifier("__count__")} = ${g} WHERE ${e.identifier("__key__")} = ${w}`)}else await N(r,c,e`UPDATE ${e.identifier(s)} SET ${e.identifier("__count__")} = ${e.identifier("__count__")} - 1 WHERE ${e.identifier("__key__")} = ${w}`)}if(y){const w=Y(n.by??[],y),E=Fe(y[$]);if(E===void 0)await N(r,c,A({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(l,S)=>({__count__:e`${S("__count__")} + 1`}),table:s,values:[w,null,1]}));else{const l=u==="min"?"MIN":"MAX";await N(r,c,A({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(S,p)=>({__count__:e`${p("__count__")} + 1`,__value__:e`${e.raw(l)}(COALESCE(${p("__value__")}, ${S("__value__")}), ${S("__value__")})`}),table:s,values:[w,E,1]}))}}}},ge=async t=>{const n=f.tables[t]?.aggregateIndexes;if(!(!n||n.length===0))for(const i of n)await ve(t,i)},de=async(t,n,i)=>{const o=f.tables[t]?.aggregateIndexes;if(!(!o||o.length===0))for(const s of o){const u=`${t}::${s.name}`;(ce.get(u)??await je(t,s.name))&&await qt(t,s,n,i)}},le=new Map,Ge=async(t,n)=>{const i=$e(t,n);return(await I(r,c,c.tableExists(i))).length>0},Ne=async(t,n)=>{const i=`${t}::rank::${n.name}`,o=le.get(i);if(o!==void 0)return o;if(!await Ge(t,n.name))return le.set(i,!1),!1;const u=f.tables[t];if(!u)return le.set(i,!1),!1;const $=$e(t,n.name);await N(r,c,e`DELETE FROM ${e.identifier($)}`);const _=n.sortBy.map((l,S)=>pe(S)),y=te(["__id__","__partition__",..._]);let w=[];const E=async()=>{if(w.length===0)return;const l=w;w=[],await $t(r,c,l)};return await _t(r,c,u,t,async l=>{if(n.where&&!lt(l,n.where))return;const S=Le(n.partitionBy??[],l),p=n.sortBy.map(g=>Oe(l[g.field]??null));w.push(e`INSERT INTO ${e.identifier($)} (${y}) VALUES (${oe([l._id,S,...p])})`),w.length>=xe&&await E()}),await E(),le.set(i,!0),!0},Te=async t=>{const n=f.tables[t]?.rankIndexes;if(!(!n||n.length===0))for(const i of n)await Ne(t,i)},X=async(t,n,i,o)=>{const s=f.tables[t],u=s?.rankIndexes;if(!u||u.length===0)return;const $=s?.softDeleteMode?.field,_=$===void 0||o===void 0?void 0:o[$],y=_!=null;for(const w of u){if(i!==void 0&&o!==void 0&&Yn(w,i,o))continue;const E=`${t}::rank::${w.name}`;if(!(le.get(E)??await Ge(t,w.name)))continue;const p=$e(t,w.name);if(i&&await N(r,c,e`DELETE FROM ${e.identifier(p)} WHERE ${e.identifier("__id__")} = ${n}`),o&&!y){if(w.where&&!lt(o,w.where))continue;const g=w.sortBy.map((D,O)=>pe(O)),b=te(["__id__","__partition__",...g]),k=Le(w.partitionBy??[],o),L=w.sortBy.map(D=>Oe(o[D.field]??null));await N(r,c,e`INSERT INTO ${e.identifier(p)} (${b}) VALUES (${oe([n,k,...L])})`)}}},ue=Ht({dialect:c,exec:r,schema:f}),Ve=new Set;for(const[t,n]of Object.entries(f.tables))for(const i of Object.values(n.triggerMap??{}))Ve.add(`${t} ${i.timing} ${i.op}`);const K=(t,n,i)=>Ve.has(`${t} ${n} ${i}`);let Ye;const j=async(t,n,i)=>{if(Se+=1,Se>Et)throw Se-=1,new Ae(`trigger recursion exceeded ${String(Et)} levels on "${i.table}" — check for a self-triggering write`,"trigger");try{await Sn({ctx:Ye,event:i,op:n,schema:f,tableName:i.table,timing:t})}finally{Se-=1}},Dt=async(t,n)=>{try{await N(r,c,n)}catch(i){Rt(c,i,t)}},me=async(t,n)=>(await I(r,c,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} = ${n}`))[0],be=async(t,n,i,o)=>{if(o===void 0)return;const s=e`${e.identifier(ye)}`,u=e.join([C(e`${e.identifier("id")}`,o.id),C(s,o[ye]??null)],e` AND `),$=n==="UPDATE"?e`UPDATE ${e.identifier(t)} SET ${i}, ${s} = ${wt.sql(s)} WHERE ${u}`:e`DELETE FROM ${e.identifier(t)} WHERE ${u}`,_=()=>{throw new Ae(`optimistic concurrency conflict on "${t}" — the row changed during this mutation; refetch and retry`,"occ")};try{if(c.supportsReturning)(await I(r,c,e`${$} RETURNING ${e.identifier("id")}`)).length===0&&_();else{const y=await N(r,c,$);(c.affectedRows?c.affectedRows(y??{rowsAffected:0}):0)===0&&_()}}catch(y){Rt(c,y,t)}},qe=(t,n=t?._creationTime)=>({_creationTime:n,[ye]:wt.value(t?.[ye])}),Bt=(t,n,i,o)=>{const s=Object.keys(t.shape);return{columns:["id","_creationTime",...s],values:[n,i,...s.map(u=>ee(t,u,o[u]??null))]}},Wt=async(t,n,i,o)=>{const s=gn(n,i.op,i.field,o.by,o.where);if(!s||!await ve(t,s.index))return;const $=Z(t,s.index.name),_=Object.keys(s.partial);if(_.length===(s.index.by??[]).length&&_.length>0){const w=Y(s.index.by??[],s.partial),E=await I(r,c,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier($)} WHERE ${e.identifier("__key__")} = ${w}`);if(E.length===0)return[];const l=E[0];return[{key:{...s.partial},value:Be(i.op,{count:l.count,value:ie(l.value)})}]}return _.length>0?void 0:(await I(r,c,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(w=>{const E=w;return{key:vt(JSON.parse(E.key)),value:Be(i.op,{count:E.count,value:ie(E.value)})}})},Xe=t=>{const n=f.tables[t]?.shardMode?.kind;return n==="shardBy"||n==="root"},ze=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`)},Ft=(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 o of Object.keys(f.tables)){const s=n.relationBaseWhere(o);s!==void 0&&(i[o]=s)}return{orderBy:n?.orderBy,relationPolicies:i,where:W(n?.baseWhere,n?.where),with:n?.with}},Qe=(t,n)=>Xe(t)?h?h(t,Ft(t,n)):ze(t):P.findMany(t,n),Ie=(t,n,i)=>st(t,{fetcher:Qe,maxRelationKeys:R,relationBaseWhere:i,schema:f,tableName:n}),Ot=xn(async(t,n)=>{const i=await I(r,c,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} IN (${oe(n)})`),o=new Map;for(const s of i){const{id:u}=s;typeof u=="string"&&o.set(u,s)}return o}),P={async cdcChangedTables(t,n){if(G)try{return await U(),await li(r,t,c,{...n,retained:V!==void 0})}catch{return}},async aggregate(t,n){const i=f.tables[t];if(!i)throw new T("INTERNAL",`unknown table: ${t}`);if(await U(),he(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 o=Ee(i.softDeleteMode,void 0),s=W(W(n.baseWhere,n.where),o),u=await Ie(s,t,n.relationBaseWhere),$=u!==s;if(i.aggregateIndexes&&!n.baseWhere&&!$&&!o){const S=Rn(i.aggregateIndexes,n.op,n.field,n.where);if(S&&await ve(t,S.index)){const g=Y(S.index.by??[],S.key),b=Z(t,S.index.name),L=(await I(r,c,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(b)} WHERE ${e.identifier("__key__")} = ${g}`))[0];return Be(n.op,L===void 0?void 0:{count:L.count,value:ie(L.value)})}}Pe(i,n.field,`aggregate(${t}, { op: "${n.op}", field: "${n.field}" })`);const _=J(u,m(i)),y=e.raw(he(n.op)),w=e`SELECT ${y}(${F(n.field)}) AS ${e.identifier("value")} FROM ${e.identifier(t)}`,l=(await I(r,c,_?e`${w} WHERE ${_}`:w))[0]?.value;return l==null?null:Number(l)},async count(t,n){const i=f.tables[t];if(!i)throw new T("INTERNAL",`unknown table: ${t}`);await U();const o=yn(n);if(o.restrictsCounts)throw new rt(t);const s=Ee(i.softDeleteMode,void 0),u=W(W(o.baseWhere,o.where),s),$=await Ie(u,t,o.relationBaseWhere),_=$!==u;if(i.aggregateIndexes&&!o.baseWhere&&!_&&!s){const l=pn(i.aggregateIndexes,o.where);if(l&&await ve(t,l.index)){const p=Y(l.index.by??[],l.key),g=Z(t,l.index.name),b=await I(r,c,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")} FROM ${e.identifier(g)} WHERE ${e.identifier("__key__")} = ${p}`);return Number(b[0]?.value??0)}}const y=J($,m(i)),w=e`SELECT COUNT(*) AS ${e.identifier("count")} FROM ${e.identifier(t)}`,E=await I(r,c,y?e`${w} WHERE ${y}`:w);return Number(E[0]?.count??0)},async delete(t,n,i){const o=await ae(t,n);if(!o)return;const s=f.tables[o];if(!s)return;const u=await me(o,t),$=ne(s,u),_=i?.hard===!0,y=!_&&s.softDeleteMode?s.softDeleteMode.field:void 0;if(!(y&&(!$||$[y]!==null&&$[y]!==void 0))){if(K(o,"before","delete")&&await j("before","delete",{id:t,op:"delete",previous:$??void 0,table:o}),await En({deletedId:t,deletedReference:w=>$?.[w],findHolders:async(w,E,l)=>{if(f.tables[w]?.shardMode?.kind==="shardBy")throw new T("INTERNAL",`cross-backend cascade from global '${o}' into shardBy '${w}' is not supported — would require Query Coordinator fan-out across shards`);return(await P.findMany(w,{includeDeleted:_,where:{[E]:l}})).page},onCascade:(w,E)=>P.delete(E,void 0,i),onRestrict:w=>{throw new Ae(w,"restrict")},onSetNull:(w,E,l)=>P.patch(E,{[l]:null}),schema:f,tableName:o}),await ge(o),await Te(o),y&&$){const w={...$,[y]:q()},E=e.join(Object.keys(s.shape).map(l=>e`${e.identifier(l)} = ${ee(s,l,w[l]??null)}`),e`, `);await be(o,"UPDATE",E,u),await de(o,$,w),await X(o,t,$,void 0),await ue(o,t,{document:w,previous:$,written:qe(u)}),await se(o,t,"update",w),K(o,"after","delete")&&await j("after","delete",{id:t,op:"delete",previous:$,table:o});return}await be(o,"DELETE",void 0,u),ke.delete(t),await de(o,$??void 0,void 0),await X(o,t,$??void 0,void 0),await ue(o,t,void 0),await se(o,t,"delete"),K(o,"after","delete")&&await j("after","delete",{id:t,op:"delete",previous:$??void 0,table:o})}},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 hn(`findFirstOrThrow: no "${t}" document matched`);return i},async findMany(t,n={}){const i=f.tables[t];if(!i)throw new T("INTERNAL",`unknown table: ${t}`);await U();const o=ln(n.orderBy,i.shape,{pinned:fn(n.where),uniqueBy:un(i.indexes,i.shape)}),s=n.cursor?wn(o,_n(n.cursor)):void 0,u=Qe,$=async(L,D,O,z)=>{if(Xe(L))return d?mn(d,L,D,O,z):ze(L);const fe=f.tables[L];if(!fe)throw new T("INTERNAL",`unknown table: ${L}`);const Ce=Ee(fe.softDeleteMode,void 0),Q={[D]:{in:O}},B=W(W(Q,z),Ce),we=await Ie(B,L,void 0),Je=J(we,m(fe)),Ze=F(D);let _e=e`SELECT ${Ze} AS __fk__, COUNT(*) AS ${e.identifier("count")} FROM ${e.identifier(L)}`;Je&&(_e=e`${_e} WHERE ${Je}`),_e=e`${_e} GROUP BY ${Ze}`;const xt=await I(r,c,_e),et=new Map;for(const tt of xt)et.set(tt.__fk__,Number(tt.count??0));return et};let _=W(n.baseWhere,n.where);_=W(_,Ee(i.softDeleteMode,n.includeDeleted)),_=await st(_,{fetcher:u,maxRelationKeys:R,relationBaseWhere:n.relationBaseWhere,schema:f,tableName:t}),s&&(_=_?{AND:[_,s]}:s);const y=J(_,m(i)),w=Pn(o,c);let E=e`SELECT * FROM ${e.identifier(t)}`;y&&(E=e`${E} WHERE ${y}`),E=e`${E} ORDER BY ${w}`;const l=typeof n.limit=="number"?Math.max(0,Math.floor(n.limit)):void 0;l!==void 0&&(E=e`${E} LIMIT ${e.raw(String(l+1))}`);const S=await I(r,c,E),p=ft(i,S);if(l===void 0)return n.with&&await at({fetcher:u,groupedCounter:$,parents:p,...ct(n),schema:f,tableName:t,with:n.with}),{continueCursor:null,isDone:!0,page:dt(p,n.select,n.with)};const g=p.length>l,b=g?p.slice(0,l):p,k=b.at(-1);return n.with&&await at({fetcher:u,groupedCounter:$,parents:b,...ct(n),schema:f,tableName:t,with:n.with}),{continueCursor:g&&k?$n(k,o):null,isDone:!g,page:dt(b,n.select,n.with)}},async get(t,n){const i=await ae(t,n);if(!i)return null;const o=f.tables[i];return o?ne(o,await Ot.load(i,t)):null},async groupBy(t,n){const i=f.tables[t];if(!i)throw new T("INTERNAL",`unknown table: ${t}`);await U();const o=n.agg??{op:"count"};if(he(o.op),o.op!=="count"&&!o.field)throw new T("INTERNAL",`groupBy(${t}, { agg: { op: "${o.op}" } }): "field" is required for non-count reducers`);const s=Ee(i.softDeleteMode,void 0),u=W(W(n.baseWhere,n.where),s),$=await Ie(u,t,n.relationBaseWhere),_=$!==u;if(i.aggregateIndexes&&!n.baseWhere&&!_&&!s){const p=await Wt(t,i.aggregateIndexes,o,n);if(p!==void 0)return p}ei(i,t,n.by,o);const y=J($,m(i)),w=n.by.map(p=>e`${F(p)} AS ${e.identifier(p)}`);if(o.op==="count")w.push(e`COUNT(*) AS ${e.identifier("value")}`);else{if(!o.field)throw new T("INTERNAL",`groupBy(${t}, { agg: { op: "${o.op}" } }): "field" is required for non-count reducers`);w.push(e`${e.raw(he(o.op))}(${F(o.field)}) AS ${e.identifier("value")}`)}const E=e.join(n.by.map(p=>F(p)),e`, `);let l=e`SELECT ${e.join(w,e`, `)} FROM ${e.identifier(t)}`;y&&(l=e`${l} WHERE ${y}`),l=e`${l} GROUP BY ${E}`;const S=await I(r,c,l);return Vn(i,n.by,S)},async insert(t,n,i){const o=f.tables[t];if(!o)throw new T("INTERNAL",`unknown table: ${t}`);await U();const s=Hn(o,n,re);De(o,s);let u,$=!0;i?.clientId!==void 0?(dn(i.clientId),u=i.clientId):i?.allowExplicitId&&typeof s._id=="string"?u=s._id:(u=x(),$=!1);const _=i?.allowExplicitId&&typeof s._creationTime=="number"?s._creationTime:q(),y={...s,_creationTime:_,_id:u};K(t,"before","insert")&&await j("before","insert",{doc:{...y},id:u,op:"insert",table:t}),await ge(t),await Te(t);const{columns:w,values:E}=Bt(o,u,_,s);return await Dt(t,e`INSERT INTO ${e.identifier(t)} (${te(w)}) VALUES (${oe(E)})`),$&&ke.set(u,t),await de(t,void 0,y),await X(t,u,void 0,y),await ue(t,u,{document:y,written:{_creationTime:_,[ye]:null}}),await se(t,u,"insert",y),K(t,"after","insert")&&await j("after","insert",{doc:y,id:u,op:"insert",table:t}),u},normalizeId(t,n){return cn(f,t,n)},async patch(t,n,i){nt("patch",n);const o=await ae(t,i);if(!o)throw new T("INTERNAL",`document not found: ${t}`);const s=f.tables[o];if(!s)throw new T("INTERNAL",`document not found: ${t}`);const u=await me(o,t),$=ne(s,u);if(!$)throw new T("INTERNAL",`document not found: ${t}`);const _={...$,...an(n),_id:t};yt(s,n,_,re),De(s,_,!0),K(o,"before","update")&&await j("before","update",{doc:{..._},id:t,op:"update",previous:$,table:o}),await ge(o),await Te(o);const y=Object.keys(s.shape),w=e.join(y.map(E=>e`${e.identifier(E)} = ${ee(s,E,_[E]??null)}`),e`, `);await be(o,"UPDATE",w,u),await de(o,$,_),await X(o,t,$,_),await ue(o,t,{document:_,previous:$,written:qe(u)}),await se(o,t,"update",_),K(o,"after","update")&&await j("after","update",{doc:_,id:t,op:"update",previous:$,table:o})},async restore(t,n){const i=await ae(t,n);if(!i)throw new T("INTERNAL",`document not found: ${t}`);const o=f.tables[i],s=o?.softDeleteMode?.field;if(!o||!s)throw new T("INTERNAL",`ctx.db.restore: table "${i}" is not a .softDelete() table`);const u=await me(i,t),$=u?.[s]!==null&&u?.[s]!==void 0;if(await P.patch(t,{[s]:null},n),$){const _=ne(o,u);_!==null&&await X(i,t,void 0,{..._,[s]:null})}},query(t){const n=f.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",o=[];let s=0,u;const $=c.name!=="mysql",_={...m(n),fieldRef:l=>e`m.${F(l)}`},y=l=>o.every(S=>S(l)),w=async(l,S)=>{await U();const p=2*Yt+l.filters.length,g=u===void 0?void 0:J(u,_,void 0,p),b=g!==void 0&&c.name==="sqlite"&&An(c.name,g).params.length+p>kn.boundParams,k=b?void 0:g,L=s>0||b,D=await Xt(r,c,n,t,k===void 0?l:{...l,scope:k},zt(L?void 0:S));!L&&S===void 0&&Qt(D);const O=D.filter(z=>y(z));return S===void 0?O:O.slice(0,S)},E=l=>{const S={async*[Symbol.asyncIterator](){if(!l)throw new T("INTERNAL",i);yield*await w(l,void 0)},async collect(){if(!l)throw new T("INTERNAL",i);return w(l,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(p){o.push(p);const g=bn(p);return g!==void 0&&In(g,n.shape,$)?u=W(u,g):s+=1,S},async first(){if(!l)throw new T("INTERNAL",i);return(await w(l,1))[0]??null},order(){return S},async paginate(p){if(!l)throw new T("INTERNAL",i);const g=Gt(p);return Vt(await w(l,Jt(g)),g)},async take(p){if(!l)throw new T("INTERNAL",i);return w(l,p)},async unique(){if(!l)throw new T("INTERNAL",i);const p=await w(l,2);if(p.length>1)throw new Ln(`unique() on table "${t}" matched ${String(p.length)} documents; expected at most one`);return p[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(p,g){const b=(n.searchIndexes??[]).find(L=>L.name===p);if(!b)throw new T("INTERNAL",`unknown search index "${p}" on table "${t}"`);const k={definition:b,field:b.field,filters:[],hasQuery:!1,indexName:p,query:""};if(g(jt(k,t,Pt(b.language))),!k.hasQuery)throw new T("INTERNAL",`search index "${p}" on table "${t}" requires a .search(field, query) call`);return E(k)}};return S};return E(void 0)},async rank(t,n,i){const o=f.tables[t];if(!o)throw new T("INTERNAL",`unknown table: ${t}`);const s=o.rankIndexes?.find(D=>D.name===n);if(!s)throw new T("INTERNAL",`unknown rankIndex "${n}" on table "${t}"`);if(i.restrictsCounts)throw new rt(t);if(await U(),!await Ne(t,s))return null;const $=typeof i.row=="string"?i.row:i.row._id;if(!$)return null;const _=$e(t,s.name),y=s.sortBy.map((D,O)=>pe(O)),w=te(["__partition__",...y]),l=(await I(r,c,e`SELECT ${w} FROM ${e.identifier(_)} WHERE ${e.identifier("__id__")} = ${$}`))[0];if(!l)return null;const S=l.__partition__,p=W(i.baseWhere,i.where);it(p,f,t,"rank");const g=ot(s,p);if(g&&Le(s.partitionBy??[],g)!==S)return null;const b=Xn(c.name,s,y,l,$),k=await I(r,c,e`SELECT COUNT(*) AS c FROM ${e.identifier(_)} WHERE ${e.identifier("__partition__")} = ${S} AND (${b})`),L=await I(r,c,e`SELECT COUNT(*) AS c FROM ${e.identifier(_)} WHERE ${e.identifier("__partition__")} = ${S}`);return{position:Number(k[0]?.c??0)+1,total:Number(L[0]?.c??0)}},async rankPage(t,n,i={}){it(W(i.baseWhere,i.where),f,t,"rankPage");const o=f.tables[t];if(!o)throw new T("INTERNAL",`unknown table: ${t}`);const s=o.rankIndexes?.find(B=>B.name===n);if(!s)throw new T("INTERNAL",`unknown rankIndex "${n}" on table "${t}"`);if(await U(),!await Ne(t,s))return{continueCursor:null,isDone:!0,page:[]};const $=$e(t,s.name),_=s.sortBy.map((B,we)=>pe(we)),y=Math.max(1,Math.min(1e3,Math.floor(i.take??100))),w=W(i.baseWhere,i.where),E=ot(s,w),l=Qn(s,_),S=e.join(l.map(B=>e`${e.identifier(B.column)} ${e.raw(`${B.direction==="desc"?"DESC":"ASC"}${mt(c,{direction:B.direction,nullable:B.nullable})}`)}`),e`, `),p=[];typeof i.partitionKey=="string"?p.push(e`${e.identifier("__partition__")} = ${i.partitionKey}`):E&&p.push(e`${e.identifier("__partition__")} = ${Le(s.partitionBy??[],E)}`);const g=sn(i);if(g!==void 0){const B=zn(c.name,l,g);B!==void 0&&p.push(B)}const b=te([Re,"__partition__",..._]);let k=e`SELECT ${b} FROM ${e.identifier($)}`;p.length>0&&(k=e`${k} WHERE ${e.join(p,e` AND `)}`),k=e`${k} ORDER BY ${S} LIMIT ${e.raw(String(y+1))}`;const L=await I(r,c,k),D=L.length>y,O=D?L.slice(0,y):L,z=O.map(B=>B[Re]),fe=await Jn(r,c,o,t,z);let Ce=null;const Q=O.at(-1);if(D&&Q!==void 0){const B=[Q.__partition__,..._.map(we=>Q[we]),Q[Re]];Ce=Zn(B)}return{continueCursor:Ce,isDone:!D,page:fe}},async replace(t,n,i,o){nt("replace",n);const s=await ae(t,i);if(!s)throw new T("INTERNAL",`document not found: ${t}`);const u=f.tables[s];if(!u)throw new T("INTERNAL",`document not found: ${t}`);const $=await me(s,t);if($===void 0)throw new T("INTERNAL",`document not found: ${t}`);const y=rn(f,s,"update")||(u.aggregateIndexes??[]).length>0||(u.rankIndexes??[]).length>0?ne(u,$)??void 0:void 0,w=o?.allowExplicitId&&typeof n._creationTime=="number"?n._creationTime:void 0,E=typeof $._creationTime=="number"?$._creationTime:void 0,l=w??E??q(),S={...n,_creationTime:l,_id:t};yt(u,n,S,re),De(u,S),K(s,"before","update")&&await j("before","update",{doc:{...S},id:t,op:"update",previous:y,table:s}),await ge(s),await Te(s);const p=Object.keys(u.shape),g=e.join([e`${e.identifier("_creationTime")} = ${l}`,...p.map(b=>e`${e.identifier(b)} = ${ee(u,b,S[b]??null)}`)],e`, `);await be(s,"UPDATE",g,$),await de(s,y,S),await X(s,t,y,S),await ue(s,t,{document:S,previous:y,written:qe($,l)}),await se(s,t,"update",S),K(s,"after","update")&&await j("after","update",{doc:S,id:t,op:"update",previous:y,table:s})}};return Ye={db:P,scheduler:At},P};export{Ii as backfillSqlSearchIndexes,Ti as createSqlCtxDb,Li as decodeGlobalRow,li as readSqlCdcChangedTables,gi as readSqlCdcChanges,Lt as readSqlCdcFloor,oi as runSqlCdcMigration,Kt as runSqlSearchMigrations,si as sweepSqlCdcRetention};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraError as p}from"@lunora/errors";import{D as I,c as vt,m as Dt,r as U,a as qt,l as lt,w as F,b as Ft}from"./ctx-db-search-state-DGbsWTp6.mjs";import{ftsUnmappedPage as Ut,groupUnmappedRows as bt,ftsWriteDocument as mt,ftsPurgeDocument as ht,ftsCompanionDdl as kt,unionAll as Ht,ftsRowidMapName as Pt}from"@lunora/shard-engine";import{sql as i}from"drizzle-orm";import{queryAll as h,OCC_VERSION_COLUMN as Bt,nullSafeEqualsSql as Wt,decodeRow as V,runInOrder as O,indexState as L,createIndexIfNotExists as X,dropIndexIfExists as M,queryRun as T,decodeRows as pt,columnRefSql as rt,serializeColumnValue as jt,readRowsPage as xt,forEachRowPaged as Kt}from"./decodeGlobalRow-VOl_TIbk.mjs";const Gt=n=>{const t=atob(n),e=Uint8Array.from(t,r=>r.codePointAt(0)??0);return new TextDecoder().decode(e)},Yt=()=>new p("BAD_REQUEST","invalid cursor"),ot=16,st=8,y=1024,b=(n,t)=>t.query(n),Qt=(n,t)=>{if(!t)return{exact:!0,lower:n,upper:n};const e=[...n].at(-1)??"",r=(e.codePointAt(0)??0)+1;if(r>=55296&&r<=57343||r>1114111)return{exact:!0,lower:n,upper:n};const o=n.slice(0,n.length-e.length);return{exact:!1,lower:n,upper:o+String.fromCodePoint(r)}},Cn=(n,t,e)=>{const r={eq:(o,s)=>{if(!n.definition.filterFields?.includes(o))throw new p("INTERNAL",`field "${o}" is not a filter field of search index "${n.indexName}" on table "${t}"`);if(n.filters.length>=st)throw new p("BAD_REQUEST",`search index "${n.indexName}" on table "${t}": at most ${String(st)} .eq() filters are supported per search query`);return n.filters.push({field:o,value:s}),r},search:(o,s)=>{const a=n;if(o!==a.definition.field)throw new p("INTERNAL",`search index "${a.indexName}" on table "${t}" indexes "${a.definition.field}", not "${o}"`);const f=b(s,e).length;if(f>ot)throw new p("BAD_REQUEST",`search index "${a.indexName}" on table "${t}": at most ${String(ot)} search terms are supported (got ${String(f)})`);return a.field=o,a.query=s,a.hasQuery=!0,r}};return r},Ln=n=>{if(n.length>y)throw new p("BAD_REQUEST",`more than ${String(y)} documents match this search — narrow it with filters or read it a page at a time with .paginate()`)},Mn=n=>Math.min(n.offset+n.numItems+1,y),Vt=n=>btoa(`search:${String(n)}`),Xt=n=>{let t;try{t=Gt(n)}catch{return}if(!t.startsWith("search:"))return;const e=Number(t.slice(7));return Number.isInteger(e)&&e>=0?e:void 0},vn=n=>{if(typeof n.endCursor=="string")throw new p("BAD_REQUEST","bounded (endCursor) pagination is not supported on search queries — relevance order has no stable range boundary");if(!Number.isFinite(n.numItems))throw new p("BAD_REQUEST",`search pagination needs a finite numItems, got ${String(n.numItems)}`);const t=Math.max(0,Math.floor(n.numItems)),e=n.cursor?Xt(n.cursor):0;if(e===void 0)throw Yt();if(e+t>=y)throw new p("BAD_REQUEST",`search pagination reaches the ${String(y)}-document limit (offset ${String(e)} + ${String(t)} requested) — a page must end below the cap so the probe row that answers \`hasMore\` still fits: retry with numItems ${String(Math.max(1,y-e-1))} or fewer, or narrow the query or the filters instead`);return{numItems:t,offset:e}},Dn=(n,t)=>{const e=t.offset+t.numItems,r=t.numItems>0&&n.length>e;return{continueCursor:r?Vt(e):null,isDone:!r,page:n.slice(t.offset,e)}},qn=n=>{if(n===void 0)return y+1;if(!Number.isFinite(n))return y;const t=Math.max(0,Math.floor(n));if(t>y)throw new p("BAD_REQUEST",`search returns at most ${String(y)} documents (asked for ${String(t)}) — narrow the query or paginate instead`);return t},Jt=(n,t)=>{if(!t.includes("."))return n[t];let e=n;for(const r of t.split(".")){if(e===null||typeof e!="object"||Array.isArray(e))return;e=e[r]}return e},k=(n,t)=>`${n}__fts_${t}`,S="__id__",g="__token__",A="__n__",zt=1e3,gt=(n,t)=>t.document(n),x=(n,t)=>Jt(n,t),Zt=n=>typeof n=="string"?n:n==null?"":typeof n=="number"||typeof n=="bigint"||typeof n=="boolean"?String(n):JSON.stringify(n)??"",tn=(n,t,e)=>n!==void 0&&t!==void 0&&x(n,e.field)===x(t,e.field),nn=(n,t)=>gt(Zt(x(n,t.field)),I(t.language)).slice(0,zt),q=(n,t)=>nn(n,t).join(" "),Tt=(n,t)=>{const e=new Map;for(const r of gt(n,t))e.set(r,(e.get(r)??0)+1);return e},yt=[Bt,"_creationTime"],at=50,$=(n,t)=>i`${i.identifier(n)}.${i.identifier(t)}`,v=(n,t,e)=>i`DELETE FROM ${i.identifier(n)} WHERE ${$(n,S)} = ${t}${e===void 0?i``:i` AND ${e}`}`,Rt=(n,{row:t,table:e})=>{const r=yt.map(o=>i` AND ${Wt(n.name,$(e,o),t[o]??null)}`);return i`SELECT 1 FROM ${i.identifier(e)} WHERE ${$(e,"id")} = ${t.id}${i.join(r)}${n.name==="postgres"?i` FOR SHARE`:i``}`},D=(n,t)=>i`EXISTS (${Rt(n,t)})`,H=(n,t)=>i`NOT EXISTS (SELECT 1 FROM ${i.identifier(n)} WHERE ${$(n,"id")} = ${t})`,_t=(n,t,e,r)=>r===void 0?H(t,e):D(n,{row:r,table:t}),J=async(n,t,e,r)=>{const o=new Map;for(let s=0;s<r.length;s+=at){const a=r.slice(s,s+at),f=await h(n,t,i`SELECT * FROM ${i.identifier(e)} WHERE ${$(e,"id")} IN (${i.join(a.map(u=>i`${u}`),i`, `)})`);for(const u of f)o.set(String(u.id),u)}return o},K=(n,t,e)=>n.name==="mysql"?i`${i.identifier(t)}(${i.raw(String(e))})`:n.textPatternOperatorClass===void 0?i`${i.identifier(t)}`:i`${i.identifier(t)} ${i.raw(n.textPatternOperatorClass)}`,z=512,Z=(n,t)=>({columns:i`${K(n,g,256)}, ${K(n,S,z)}`,concurrently:!0,name:`${t}__unique`,table:t,unique:!0}),W=n=>i`${n} COLLATE utf8mb4_0900_bin`,j=(n,t)=>{const e=JSON.stringify(t.map(([r,o])=>({o,t:r})));switch(n.name){case"mysql":return i`JSON_TABLE(${e}, '$[*]' COLUMNS (${i.identifier("t")} VARCHAR(256) PATH '$.t', ${i.identifier("o")} INT PATH '$.o')) AS ${i.identifier("j")}`;case"postgres":return i`json_to_recordset(${e}::json) AS ${i.identifier("j")}(${i.identifier("t")} text, ${i.identifier("o")} integer)`;default:return i`(SELECT json_extract(${$("e","value")}, '$.t') AS ${i.identifier("t")}, json_extract(${$("e","value")}, '$.o') AS ${i.identifier("o")} FROM json_each(${e}) AS ${i.identifier("e")}) AS ${i.identifier("j")}`}},Nt=(n,t,e,r,o)=>{const s=i.join([g,S,A].map(f=>i.identifier(f)),i`, `),a=f=>i`INSERT INTO ${i.identifier(t)} (${s}) SELECT ${$("j","t")}, ${e}, ${$("j","o")} FROM ${j(n,r)} WHERE ${f}`;switch(n.name){case"mysql":{const f=D(n,o),u=$(t,g),c=i.identifier("held");return[i`${a(i`${f} AND NOT EXISTS (SELECT 1 FROM ${i.identifier(t)} AS ${c} WHERE ${$("held",S)} = ${e} AND ${$("held",g)} = ${W($("j","t"))})`)} ON DUPLICATE KEY UPDATE ${$(t,A)} = ${$(t,A)}`,i`UPDATE ${i.identifier(t)} JOIN ${j(n,r)} ON ${u} = ${W($("j","t"))} SET ${$(t,A)} = ${$("j","o")} WHERE ${$(t,S)} = ${e} AND ${f}`,i`DELETE FROM ${i.identifier(t)} WHERE ${$(t,S)} = ${e} AND ${f} AND ${u} NOT IN (SELECT ${W($("j","t"))} FROM ${j(n,r)})`]}case"postgres":{const f=i.identifier("__source__"),u=i.identifier("__purged__"),c=i`EXISTS (SELECT 1 FROM ${f})`;return[i`WITH ${f} AS (${Rt(n,o)}), ${u} AS (${v(t,e,c)} RETURNING 1) ${a(i`${c} AND (SELECT COUNT(*) FROM ${u}) >= 0`)} ON CONFLICT DO NOTHING`]}default:{const f=D(n,o);return[v(t,e,f),i`${a(f)} ON CONFLICT DO NOTHING`]}}},At={uniqueKeyMissing:!1,unmappedSkipped:0},ft=new Set,ut=(n,t)=>{ft.has(n)||(ft.add(n),console.warn(`[@lunora/sql-store] ${t}`))},ct=100,dt=3,G=async(n,t,e,r=0)=>{const{companion:o,definition:s,index:a,tableName:f}=e,u={done:!0,last:r,left:0};if((await h(n,t,t.tableExists(f))).length===0)return u;const d=await h(n,t,Ut(o,ct,r));if(d.length===0)return u;const w=Math.max(...d.map(_=>Number(_.rowid))),E=bt(d),l=await J(n,t,f,[...E.keys()]),m=[];for(const[_,nt]of E){const B=l.get(_),et=B===void 0?void 0:V(s,B),it=_t(t,f,_,B);m.push(...et?mt(o,_,q(et,a),{guard:it,unmappedRowids:nt}):ht(o,_,{guard:it,unmappedRowids:nt}))}await O(n,t,m);const R=await h(n,t,i`SELECT COUNT(*) AS ${i.identifier("n")} FROM ${i.identifier(o)} WHERE ${$(o,"rowid")} > ${Math.max(0,r)} AND ${$(o,"rowid")} <= ${w}`);return{done:d.length<ct,last:w,left:Number(R[0]?.n??0)}},en=async(n,t,e)=>{let r=0,o=0;for(;;){let s=await G(n,t,e,r);for(let a=1;s.left>0&&a<dt;a+=1)s=await G(n,t,e,r);if(s.left>0&&(o+=s.left,console.warn(`[@lunora/sql-store] search migration of "${e.companion}": ${String(s.left)} row(s) lost every one of ${String(dt)} attempts to a concurrent write and were left for a later pass.`)),s.done)return o;r=s.last}},rn=async(n,t,e,r)=>r==="step"?(await G(n,t,e),At):{uniqueKeyMissing:!1,unmappedSkipped:await en(n,t,e)},Y=n=>`${n}__btree`,on=async(n,t,e)=>{const r=Z(t,e),o=await L(n,t,e,r.name);if(o!=="valid"){if(o==="absent"&&(await h(n,t,i`SELECT 1 AS ${i.identifier("present")} FROM ${i.identifier(e)} LIMIT 1`)).length===0)try{await X(n,t,{...r,concurrently:!1}),await M(n,t,e,Y(e));return}catch(a){if(await L(n,t,e,r.name).catch(()=>{})==="valid")return;ut(`${e}:empty`,`search companion "${e}": adding its unique key failed (${String(a)}); run backfillSqlSearchIndexes.`);return}ut(`${e}:missing`,`search companion "${e}" has no usable unique key, so two backfills racing over one row can index it twice. Run backfillSqlSearchIndexes, which adds it.`)}},sn=async(n,t,e)=>{const r=$(e,S),o=$(e,g);return(await h(n,t,i`SELECT ${r} AS ${i.identifier("id")}, ${o} AS ${i.identifier("token")}, COUNT(*) AS ${i.identifier("n")} FROM ${i.identifier(e)} GROUP BY ${r}, ${o} HAVING COUNT(*) > 1`)).map(a=>({count:Number(a.n),id:String(a.id),token:String(a.token)}))},an=(n,t,{count:e,id:r,token:o})=>{const s=i`${$(t,S)} = ${r} AND ${$(t,g)} = ${o}`;switch(n.name){case"mysql":return i`DELETE FROM ${i.identifier(t)} WHERE ${s} LIMIT ${i.raw(String(e-1))}`;case"postgres":return i`DELETE FROM ${i.identifier(t)} WHERE ctid = ANY (ARRAY(SELECT ctid FROM ${i.identifier(t)} WHERE ${s} OFFSET 1))`;default:return i`DELETE FROM ${i.identifier(t)} WHERE rowid IN (SELECT rowid FROM ${i.identifier(t)} WHERE ${s} LIMIT -1 OFFSET 1)`}},fn=async(n,t,e,r)=>{const{companion:o,definition:s,index:a,tableName:f}=e;for(const d of r)await T(n,t,an(t,o,d));const u=[...new Set(r.map(d=>d.id))],c=await J(n,t,f,u);for(const d of u){const w=c.get(d),E=w===void 0?void 0:V(s,w);if(w!==void 0&&E){const l=[...Tt(q(E,a),I(a.language))];await O(n,t,Nt(t,o,d,l,{row:w,table:f}));continue}await T(n,t,v(o,d,_t(t,f,d,w)))}},un=async(n,t,e)=>t.name!=="mysql"?"":(await h(n,t,i`SELECT COLUMN_NAME AS ${i.identifier("name")}, COLLATION_NAME AS ${i.identifier("collation")} FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ${e} AND COLUMN_NAME IN (${g}, ${S})`)).filter(o=>!String(o.collation).endsWith("_bin")).map(o=>` Its ${String(o.name)} column uses ${String(o.collation)}, which treats values differing only in case or accents as one; convert it with ALTER TABLE \`${e}\` MODIFY \`${String(o.name)}\` VARCHAR(768) COLLATE utf8mb4_0900_bin NOT NULL.`).join(""),cn=async(n,t,e)=>{if(t.name!=="mysql")return 0;const r=$(e,S),o=await h(n,t,i`SELECT COUNT(*) AS ${i.identifier("n")} FROM (SELECT 1 AS ${i.identifier("x")} FROM ${i.identifier(e)} GROUP BY LEFT(${r}, ${i.raw(String(z))}), ${$(e,g)} HAVING COUNT(DISTINCT ${r}) > 1) AS ${i.identifier("shared")}`);return Number(o[0]?.n??0)},dn=async(n,t,e)=>{const r=Z(t,e),o=await L(n,t,e,r.name);if(o==="valid")return"valid";o==="invalid"&&await M(n,t,e,r.name);try{return await X(n,t,r),"built"}catch(s){const a=await L(n,t,e,r.name);if(a==="valid")return"valid";if(a==="invalid"&&await M(n,t,e,r.name),!t.isUniqueViolation(s))throw s;return"refused"}},$t=3,$n=async(n,t,e)=>{const{companion:r}=e,o=await un(n,t,r);let s="";o!==""&&console.warn(`[@lunora/sql-store] search companion "${r}" folds case and accents in its unique key.${o}`);for(let a=0;a<$t;a+=1){const f=await dn(n,t,r);if(f==="valid")return await M(n,t,r,Y(r)),!0;if(f==="refused"){const u=await sn(n,t,r);if(u.length>0)await fn(n,t,e,u);else{const c=await cn(n,t,r);s=c>0?` ${String(c)} token(s) are held by document ids that agree on their first ${String(z)} characters, which the key cannot tell apart; shorten those ids.`:o}}}return await L(n,t,r,Z(t,r).name)==="valid"?(await M(n,t,r,Y(r)),!0):(console.warn(`[@lunora/sql-store] search companion "${r}" still has no unique key after ${String($t)} attempts: writes keep working, but two backfills racing over one row can index it twice.${s}`),!1)},wn=async(n,t,e,r)=>r==="step"?At:{uniqueKeyMissing:!await $n(n,t,e),unmappedSkipped:0},Sn=(n,t)=>t?i`${i.identifier(g)} LIKE ${`${n}%`}`:i`${i.identifier(g)} = ${n}`,Ot=(n,t)=>{const e=t.filters.map(r=>i`m.${rt(r.field)} = ${jt(r.value)}`);return n.softDeleteMode&&e.push(i`m.${rt(n.softDeleteMode.field)} IS NULL`),e},It=async(n,t,e,r,o,s,a)=>{const f=Ot(e,o);let u=i`SELECT m.* FROM (${a}) s JOIN ${i.identifier(r)} m ON m.${i.identifier("id")} = s.${i.identifier(S)}`;return f.length>0&&(u=i`${u} WHERE ${i.join(f,i` AND `)}`),u=i`${u} ORDER BY s.${i.identifier("__score__")} DESC, m.${i.identifier("_creationTime")} DESC, m.${i.identifier("id")} ASC LIMIT ${i.raw(String(s))}`,pt(e,await h(n,t,u))},En=async(n,t,e,r,o,s)=>{const a=b(o.query,I(o.definition.language));if(a.length===0)return[];const f=k(r,o.indexName),u=a.length-1,c=a.map((m,R)=>Sn(m,R===u)),d=i.join(c,i` OR `),w=c.map(m=>i`SUM(CASE WHEN ${m} THEN ${i.identifier(A)} ELSE 0 END)`),E=i.join(w.map(m=>i`${m} > 0`),i` AND `),l=i`SELECT ${i.identifier(S)}, ${i.join(w,i` + `)} AS ${i.identifier("__score__")} FROM ${i.identifier(f)} WHERE ${d} GROUP BY ${i.identifier(S)} HAVING ${E}`;return It(n,t,e,r,o,s,l)},ln=async(n,t,e,r,o,s)=>{const a=b(o.query,I(o.definition.language));if(a.length===0)return[];const f=k(r,o.indexName),u=`${f}__vocab`,c=a.length-1,d=a.map((l,m)=>{const R=Qt(l,m===c),_=R.exact?i`${i.identifier("term")} = ${R.lower}`:i`${i.identifier("term")} >= ${R.lower} AND ${i.identifier("term")} < ${R.upper}`;return i`SELECT ${i.identifier("doc")}, ${i.raw(String(m))} AS ${i.identifier("__term__")}, COUNT(*) AS ${i.identifier("__n__")} FROM ${i.identifier(u)} WHERE ${_} GROUP BY ${i.identifier("doc")}`}),w=a.map((l,m)=>i`SUM(CASE WHEN u.${i.identifier("__term__")} = ${i.raw(String(m))} THEN u.${i.identifier("__n__")} ELSE 0 END)`),E=i`SELECT f.${i.identifier(S)} AS ${i.identifier(S)}, ${i.join(w,i` + `)} AS ${i.identifier("__score__")} FROM (${Ht(d)}) u JOIN ${i.identifier(f)} f ON f.rowid = u.${i.identifier("doc")} GROUP BY f.${i.identifier(S)} HAVING ${i.join(w.map(l=>i`${l} > 0`),i` AND `)}`;return It(n,t,e,r,o,s,E)},mn=async(n,t,e,r,o,s)=>{const a=t.nativeTextSearch,f=b(o.query,I(o.definition.language));if(!a||f.length===0)return[];const u=k(r,o.indexName),c=[a.matches(u,f),...Ot(e,o)],d=i`SELECT m.* FROM ${i.identifier(u)} JOIN ${i.identifier(r)} m ON m.${i.identifier("id")} = ${i.identifier(u)}.${i.identifier(S)} WHERE ${i.join(c,i` AND `)} ORDER BY ${a.rank(u,f)} DESC, m.${i.identifier("_creationTime")} DESC, m.${i.identifier("id")} ASC LIMIT ${i.raw(String(s))}`;return pt(e,await h(n,t,d))},hn={ensureCompanion:async(n,t,e)=>{const{integer:r,key:o}=t.companionTypes;await T(n,t,i`CREATE TABLE IF NOT EXISTS ${i.identifier(e)} (${i.identifier(g)} ${i.raw(o)} NOT NULL, ${i.identifier(S)} ${i.raw(o)} NOT NULL, ${i.identifier(A)} ${i.raw(r)} NOT NULL)`),await X(n,t,{columns:K(t,S,191),name:`${e}__by_id`,table:e,unique:!1}),await on(n,t,e)},indexDocument:async(n,t,e,r,o,s)=>{const a=[...Tt(q(r,o),I(o.language))];await O(n,t,Nt(t,e,String(s.row.id),a,s))},migrate:wn,name:"inverted",purgeDocument:async(n,t,e,r,o)=>{await T(n,t,v(e,r,H(o,r)))},runSearch:En},pn={ensureCompanion:async(n,t,e)=>{await O(n,t,kt(e))},indexDocument:async(n,t,e,r,o,s)=>{await O(n,t,mt(e,String(s.row.id),q(r,o),{guard:D(t,s)}))},migrate:rn,name:"fts5",purgeDocument:async(n,t,e,r,o)=>{await O(n,t,ht(e,r,{guard:H(o,r)}))},runSearch:ln},gn={ensureCompanion:async(n,t,e)=>{const r=t.nativeTextSearch;if(r){await T(n,t,r.createCompanion(e,t.companionTypes.key));for(const o of r.createIndexes(e))await T(n,t,o)}},indexDocument:async(n,t,e,r,o,s)=>{const a=t.nativeTextSearch;a&&await T(n,t,a.indexDocument(e,String(s.row.id),q(r,o),D(t,s)))},name:"native",purgeDocument:async(n,t,e,r,o)=>{await T(n,t,v(e,r,H(o,r)))},runSearch:mn},C=(n,t)=>n.strategy==="native"&&t.nativeTextSearch!==void 0?gn:t.supportsFts5?pn:hn,P=(n,t)=>`${vt(n)}/${C(n,t).name}`,tt=function*(n){for(const[t,e]of Object.entries(n.tables)){const r=e.searchIndexes;if(!(e.shardMode!==void 0&&e.shardMode.kind!=="global"||!r))for(const o of r)yield[t,e,o]}},N=(n,t)=>k(n,t.name),Tn=async(n,t,e,r)=>{const o=N(e,r);return lt(await U(n,t,o),P(r,t)).finished?!0:Ft(n,t,o)},Fn=async(n,t,e,r,o,s)=>{if(!await Tn(n,t,r,o.definition))throw new p("SEARCH_INDEX_BUILDING",`search index "${o.indexName}" on table "${r}" is still backfilling and currently covers only part of the table — retry once it finishes${t.searchBackfillHint===void 0?"":`, or complete it now: ${t.searchBackfillHint}`}`);return C(o.definition,t).runSearch(n,t,e,r,o,s)},wt=n=>n.slice(n.lastIndexOf("/")+1),St=200,Et=5,yn=async(n,t,e,r)=>{const o=[...r.keys()],s=await h(n,t,i`SELECT * FROM ${i.identifier(e)} WHERE ${i.identifier("id")} >= ${o[0]} AND ${i.identifier("id")} <= ${o.at(-1)} ORDER BY ${i.identifier("id")} ASC`),a=new Map;for(const f of s){const u=f.id,c=r.get(u)?.row;c&&yt.some(d=>String(c[d])!==String(f[d]))&&a.set(u,{row:f})}return a},Q=async(n,t,e,r,o,s,a)=>{const f=C(s,t);let u=a;for(let c=0;u.size>0&&c<Et;c+=1){for(const{document:d,row:w}of u.values()){const E=d??V(e,w);E&&await f.indexDocument(n,t,o,E,s,{row:w,table:r})}u=await yn(n,t,r,u)}u.size>0&&console.warn(`[@lunora/sql-store] search index "${o}": ${String(u.size)} row(s) were rewritten on every one of ${String(Et)} re-check rounds and were left to their writers — each stays stale only if none of those writes changed its indexed text.`)},Ct=async(n,t,e,r,o)=>{const s=N(r,o),a=P(o,t),f=lt(await U(n,t,s),a);if(f.finished)return!0;if(f.wipe&&await F(n,t,s,void 0,!1,a),(await h(n,t,t.tableExists(r))).length===0)return await F(n,t,s,void 0,!0,a),!0;const c=await xt(n,t,r,f.cursor,St),d=c.findLast(l=>typeof l.id=="string")?.id,w=new Map;for(const l of c)typeof l.id=="string"&&w.set(l.id,{row:l});await Q(n,t,e,r,s,o,w);const E=c.length<St;return await F(n,t,s,d??f.cursor,E,a),E},Lt=async(n,t,e)=>{await Dt(n,e);for(const[r,,o]of tt(t)){const s=N(r,o),a=P(o,e),f=await U(n,e,s);f.profile!==void 0&&wt(f.profile)!==wt(a)&&(await T(n,e,i`DROP TABLE IF EXISTS ${i.identifier(s)}`),await T(n,e,i`DROP TABLE IF EXISTS ${i.identifier(Pt(s))}`),await qt(n,e,s)),await C(o,e).ensureCompanion(n,e,s)}},Rn=async(n,t,e,r,o)=>{const s=N(r,o);if((await U(n,t,s)).profile!==void 0)return;let f=0;(await h(n,t,t.tableExists(r))).length>0&&await Kt(n,t,e,r,()=>{f+=1},{limit:1}),await F(n,t,s,void 0,f===0,P(o,t))},Mt=async(n,t,e,r,o,s)=>await C(o,t).migrate?.(n,t,{companion:N(r,o),definition:e,index:o,tableName:r},s)??{uniqueKeyMissing:!1,unmappedSkipped:0},Un=async(n,t,e)=>{await Lt(n,t,e);for(const[r,o,s]of tt(t)){if(await Mt(n,e,o,r,s,"step"),s.staged){await Rn(n,e,o,r,s);continue}await Ct(n,e,o,r,s)}},bn=async(n,t,e)=>{const r={uniqueKeyMissing:[],unmappedSkipped:0};await Lt(n,t,e);for(const[o,s,a]of tt(t)){const f=await Mt(n,e,s,o,a,"drain");r.unmappedSkipped+=f.unmappedSkipped,f.uniqueKeyMissing&&r.uniqueKeyMissing.push(N(o,a));let u=!1;for(;!u;)u=await Ct(n,e,s,o,a)}return r},kn=n=>{const{dialect:t,exec:e,schema:r}=n;return async(o,s,a)=>{const f=r.tables[o],u=f?.searchIndexes;if(!(!f||!u||u.length===0))for(const c of u){if(tn(a?.previous,a?.document,c))continue;const d=N(o,c);if(a){await Q(e,t,f,o,d,c,new Map([[s,{document:a.document,row:{...a.written,id:s}}]]));continue}await C(c,t).purgeDocument(e,t,d,s,o);const E=(await J(e,t,o,[s])).get(s);E!==void 0&&await Q(e,t,f,o,d,c,new Map([[s,{row:E}]]))}}};export{Cn as A,vn as C,Dn as R,qn as T,Fn as a,Mn as b,kn as c,bn as d,Un as r,Ln as x};
|
|
1
|
+
import{LunoraError as h}from"@lunora/errors";import{D as I,c as vt,m as Dt,r as U,a as qt,l as lt,w as F,b as Ft}from"./ctx-db-search-state-DGbsWTp6.mjs";import{ftsUnmappedPage as Ut,groupUnmappedRows as bt,ftsWriteDocument as mt,ftsPurgeDocument as pt,ftsCompanionDdl as kt,unionAll as Ht,ftsRowidMapName as Pt}from"@lunora/shard-engine";import{sql as i}from"drizzle-orm";import{queryAll as p,OCC_VERSION_COLUMN as Bt,nullSafeEqualsSql as Wt,decodeRow as V,runInOrder as O,indexState as L,createIndexIfNotExists as X,dropIndexIfExists as M,queryRun as T,decodeRows as ht,columnRefSql as rt,serializeColumnValue as jt,readRowsPage as xt,forEachRowPaged as Kt}from"./decodeGlobalRow-VOl_TIbk.mjs";const Gt=n=>{const t=atob(n),e=Uint8Array.from(t,r=>r.codePointAt(0)??0);return new TextDecoder().decode(e)},Yt=()=>new h("BAD_REQUEST","invalid cursor"),ot=16,st=8,y=1024,b=(n,t)=>t.query(n),Qt=(n,t)=>{if(!t)return{exact:!0,lower:n,upper:n};const e=[...n].at(-1)??"",r=(e.codePointAt(0)??0)+1;if(r>=55296&&r<=57343||r>1114111)return{exact:!0,lower:n,upper:n};const o=n.slice(0,n.length-e.length);return{exact:!1,lower:n,upper:o+String.fromCodePoint(r)}},Cn=(n,t,e)=>{const r={eq:(o,s)=>{if(!n.definition.filterFields?.includes(o))throw new h("INTERNAL",`field "${o}" is not a filter field of search index "${n.indexName}" on table "${t}"`);if(n.filters.length>=st)throw new h("BAD_REQUEST",`search index "${n.indexName}" on table "${t}": at most ${String(st)} .eq() filters are supported per search query`);return n.filters.push({field:o,value:s}),r},search:(o,s)=>{const a=n;if(o!==a.definition.field)throw new h("INTERNAL",`search index "${a.indexName}" on table "${t}" indexes "${a.definition.field}", not "${o}"`);const f=b(s,e).length;if(f>ot)throw new h("BAD_REQUEST",`search index "${a.indexName}" on table "${t}": at most ${String(ot)} search terms are supported (got ${String(f)})`);return a.field=o,a.query=s,a.hasQuery=!0,r}};return r},Ln=n=>{if(n.length>y)throw new h("BAD_REQUEST",`more than ${String(y)} documents match this search — narrow it with filters or read it a page at a time with .paginate()`)},Mn=n=>Math.min(n.offset+n.numItems+1,y),Vt=n=>btoa(`search:${String(n)}`),Xt=n=>{let t;try{t=Gt(n)}catch{return}if(!t.startsWith("search:"))return;const e=Number(t.slice(7));return Number.isInteger(e)&&e>=0?e:void 0},vn=n=>{if(typeof n.endCursor=="string")throw new h("BAD_REQUEST","bounded (endCursor) pagination is not supported on search queries — relevance order has no stable range boundary");if(!Number.isFinite(n.numItems))throw new h("BAD_REQUEST",`search pagination needs a finite numItems, got ${String(n.numItems)}`);const t=Math.max(0,Math.floor(n.numItems)),e=n.cursor?Xt(n.cursor):0;if(e===void 0)throw Yt();if(e+t>=y)throw new h("BAD_REQUEST",`search pagination reaches the ${String(y)}-document limit (offset ${String(e)} + ${String(t)} requested) — a page must end below the cap so the probe row that answers \`hasMore\` still fits: retry with numItems ${String(Math.max(1,y-e-1))} or fewer, or narrow the query or the filters instead`);return{numItems:t,offset:e}},Dn=(n,t)=>{const e=t.offset+t.numItems,r=t.numItems>0&&n.length>e;return{continueCursor:r?Vt(e):null,isDone:!r,page:n.slice(t.offset,e)}},qn=n=>{if(n===void 0)return y+1;if(!Number.isFinite(n))return y;const t=Math.max(0,Math.floor(n));if(t>y)throw new h("BAD_REQUEST",`search returns at most ${String(y)} documents (asked for ${String(t)}) — narrow the query or paginate instead`);return t},Jt=(n,t)=>{if(!t.includes("."))return n[t];let e=n;for(const r of t.split(".")){if(e===null||typeof e!="object"||Array.isArray(e))return;e=e[r]}return e},k=(n,t)=>`${n}__fts_${t}`,S="__id__",g="__token__",A="__n__",zt=1e3,gt=(n,t)=>t.document(n),x=(n,t)=>Jt(n,t),Zt=n=>typeof n=="string"?n:n==null?"":typeof n=="number"||typeof n=="bigint"||typeof n=="boolean"?String(n):JSON.stringify(n)??"",tn=(n,t,e)=>n!==void 0&&t!==void 0&&x(n,e.field)===x(t,e.field),nn=(n,t)=>gt(Zt(x(n,t.field)),I(t.language)).slice(0,zt),q=(n,t)=>nn(n,t).join(" "),Tt=(n,t)=>{const e=new Map;for(const r of gt(n,t))e.set(r,(e.get(r)??0)+1);return e},yt=[Bt,"_creationTime"],at=50,$=(n,t)=>i`${i.identifier(n)}.${i.identifier(t)}`,v=(n,t,e)=>i`DELETE FROM ${i.identifier(n)} WHERE ${$(n,S)} = ${t}${e===void 0?i``:i` AND ${e}`}`,Rt=(n,{row:t,table:e})=>{const r=yt.map(o=>i` AND ${Wt(n.name,$(e,o),t[o]??null)}`);return i`SELECT 1 FROM ${i.identifier(e)} WHERE ${$(e,"id")} = ${t.id}${i.join(r)}${n.name==="postgres"?i` FOR SHARE`:i``}`},D=(n,t)=>i`EXISTS (${Rt(n,t)})`,H=(n,t)=>i`NOT EXISTS (SELECT 1 FROM ${i.identifier(n)} WHERE ${$(n,"id")} = ${t})`,_t=(n,t,e,r)=>r===void 0?H(t,e):D(n,{row:r,table:t}),J=async(n,t,e,r)=>{const o=new Map;for(let s=0;s<r.length;s+=at){const a=r.slice(s,s+at),f=await p(n,t,i`SELECT * FROM ${i.identifier(e)} WHERE ${$(e,"id")} IN (${i.join(a.map(u=>i`${u}`),i`, `)})`);for(const u of f)o.set(String(u.id),u)}return o},K=(n,t,e)=>n.name==="mysql"?i`${i.identifier(t)}(${i.raw(String(e))})`:n.textPatternOperatorClass===void 0?i`${i.identifier(t)}`:i`${i.identifier(t)} ${i.raw(n.textPatternOperatorClass)}`,z=512,Z=(n,t)=>({columns:i`${K(n,g,256)}, ${K(n,S,z)}`,concurrently:!0,name:`${t}__unique`,table:t,unique:!0}),W=n=>i`${n} COLLATE utf8mb4_0900_bin`,j=(n,t)=>{const e=JSON.stringify(t.map(([r,o])=>({o,t:r})));switch(n.name){case"mysql":return i`JSON_TABLE(${e}, '$[*]' COLUMNS (${i.identifier("t")} VARCHAR(256) PATH '$.t', ${i.identifier("o")} INT PATH '$.o')) AS ${i.identifier("j")}`;case"postgres":return i`json_to_recordset(${e}::json) AS ${i.identifier("j")}(${i.identifier("t")} text, ${i.identifier("o")} integer)`;default:return i`(SELECT json_extract(${$("e","value")}, '$.t') AS ${i.identifier("t")}, json_extract(${$("e","value")}, '$.o') AS ${i.identifier("o")} FROM json_each(${e}) AS ${i.identifier("e")}) AS ${i.identifier("j")}`}},Nt=(n,t,e,r,o)=>{const s=i.join([g,S,A].map(f=>i.identifier(f)),i`, `),a=f=>i`INSERT INTO ${i.identifier(t)} (${s}) SELECT ${$("j","t")}, ${e}, ${$("j","o")} FROM ${j(n,r)} WHERE ${f}`;switch(n.name){case"mysql":{const f=D(n,o),u=$(t,g),c=i.identifier("held");return[i`${a(i`${f} AND NOT EXISTS (SELECT 1 FROM ${i.identifier(t)} AS ${c} WHERE ${$("held",S)} = ${e} AND ${$("held",g)} = ${W($("j","t"))})`)} ON DUPLICATE KEY UPDATE ${$(t,A)} = ${$(t,A)}`,i`UPDATE ${i.identifier(t)} JOIN ${j(n,r)} ON ${u} = ${W($("j","t"))} SET ${$(t,A)} = ${$("j","o")} WHERE ${$(t,S)} = ${e} AND ${f}`,i`DELETE FROM ${i.identifier(t)} WHERE ${$(t,S)} = ${e} AND ${f} AND ${u} NOT IN (SELECT ${W($("j","t"))} FROM ${j(n,r)})`]}case"postgres":{const f=i.identifier("__source__"),u=i.identifier("__purged__"),c=i`EXISTS (SELECT 1 FROM ${f})`;return[i`WITH ${f} AS (${Rt(n,o)}), ${u} AS (${v(t,e,c)} RETURNING 1) ${a(i`${c} AND (SELECT COUNT(*) FROM ${u}) >= 0`)} ON CONFLICT DO NOTHING`]}default:{const f=D(n,o);return[v(t,e,f),i`${a(f)} ON CONFLICT DO NOTHING`]}}},At={uniqueKeyMissing:!1,unmappedSkipped:0},ft=new Set,ut=(n,t)=>{ft.has(n)||(ft.add(n),console.warn(`[@lunora/sql-store] ${t}`))},ct=100,dt=3,G=async(n,t,e,r=0)=>{const{companion:o,definition:s,index:a,tableName:f}=e,u={done:!0,last:r,left:0};if((await p(n,t,t.tableExists(f))).length===0)return u;const d=await p(n,t,Ut(o,ct,r));if(d.length===0)return u;const w=Math.max(...d.map(_=>Number(_.rowid))),E=bt(d),l=await J(n,t,f,[...E.keys()]),m=[];for(const[_,nt]of E){const B=l.get(_),et=B===void 0?void 0:V(s,B),it=_t(t,f,_,B);m.push(...et?mt(o,_,q(et,a),{guard:it,unmappedRowids:nt}):pt(o,_,{guard:it,unmappedRowids:nt}))}await O(n,t,m);const R=await p(n,t,i`SELECT COUNT(*) AS ${i.identifier("n")} FROM ${i.identifier(o)} WHERE ${$(o,"rowid")} > ${Math.max(0,r)} AND ${$(o,"rowid")} <= ${w}`);return{done:d.length<ct,last:w,left:Number(R[0]?.n??0)}},en=async(n,t,e)=>{let r=0,o=0;for(;;){let s=await G(n,t,e,r);for(let a=1;s.left>0&&a<dt;a+=1)s=await G(n,t,e,r);if(s.left>0&&(o+=s.left,console.warn(`[@lunora/sql-store] search migration of "${e.companion}": ${String(s.left)} row(s) lost every one of ${String(dt)} attempts to a concurrent write and were left for a later pass.`)),s.done)return o;r=s.last}},rn=async(n,t,e,r)=>r==="step"?(await G(n,t,e),At):{uniqueKeyMissing:!1,unmappedSkipped:await en(n,t,e)},Y=n=>`${n}__btree`,on=async(n,t,e)=>{const r=Z(t,e),o=await L(n,t,e,r.name);if(o!=="valid"){if(o==="absent"&&(await p(n,t,i`SELECT 1 AS ${i.identifier("present")} FROM ${i.identifier(e)} LIMIT 1`)).length===0)try{await X(n,t,{...r,concurrently:!1}),await M(n,t,e,Y(e));return}catch(a){if(await L(n,t,e,r.name).catch(()=>{})==="valid")return;ut(`${e}:empty`,`search companion "${e}": adding its unique key failed (${String(a)}); run backfillSqlSearchIndexes.`);return}ut(`${e}:missing`,`search companion "${e}" has no usable unique key, so two backfills racing over one row can index it twice. Run backfillSqlSearchIndexes, which adds it.`)}},sn=async(n,t,e)=>{const r=$(e,S),o=$(e,g);return(await p(n,t,i`SELECT ${r} AS ${i.identifier("id")}, ${o} AS ${i.identifier("token")}, COUNT(*) AS ${i.identifier("n")} FROM ${i.identifier(e)} GROUP BY ${r}, ${o} HAVING COUNT(*) > 1`)).map(a=>({count:Number(a.n),id:String(a.id),token:String(a.token)}))},an=(n,t,{count:e,id:r,token:o})=>{const s=i`${$(t,S)} = ${r} AND ${$(t,g)} = ${o}`;switch(n.name){case"mysql":return i`DELETE FROM ${i.identifier(t)} WHERE ${s} LIMIT ${i.raw(String(e-1))}`;case"postgres":return i`DELETE FROM ${i.identifier(t)} WHERE ctid = ANY (ARRAY(SELECT ctid FROM ${i.identifier(t)} WHERE ${s} OFFSET 1))`;default:return i`DELETE FROM ${i.identifier(t)} WHERE rowid IN (SELECT rowid FROM ${i.identifier(t)} WHERE ${s} LIMIT -1 OFFSET 1)`}},fn=async(n,t,e,r)=>{const{companion:o,definition:s,index:a,tableName:f}=e;for(const d of r)await T(n,t,an(t,o,d));const u=[...new Set(r.map(d=>d.id))],c=await J(n,t,f,u);for(const d of u){const w=c.get(d),E=w===void 0?void 0:V(s,w);if(w!==void 0&&E){const l=[...Tt(q(E,a),I(a.language))];await O(n,t,Nt(t,o,d,l,{row:w,table:f}));continue}await T(n,t,v(o,d,_t(t,f,d,w)))}},un=async(n,t,e)=>t.name!=="mysql"?"":(await p(n,t,i`SELECT COLUMN_NAME AS ${i.identifier("name")}, COLLATION_NAME AS ${i.identifier("collation")} FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ${e} AND COLUMN_NAME IN (${g}, ${S})`)).filter(o=>!String(o.collation).endsWith("_bin")).map(o=>` Its ${String(o.name)} column uses ${String(o.collation)}, which treats values differing only in case or accents as one; convert it with ALTER TABLE \`${e}\` MODIFY \`${String(o.name)}\` VARCHAR(768) COLLATE utf8mb4_0900_bin NOT NULL.`).join(""),cn=async(n,t,e)=>{if(t.name!=="mysql")return 0;const r=$(e,S),o=await p(n,t,i`SELECT COUNT(*) AS ${i.identifier("n")} FROM (SELECT 1 AS ${i.identifier("x")} FROM ${i.identifier(e)} GROUP BY LEFT(${r}, ${i.raw(String(z))}), ${$(e,g)} HAVING COUNT(DISTINCT ${r}) > 1) AS ${i.identifier("shared")}`);return Number(o[0]?.n??0)},dn=async(n,t,e)=>{const r=Z(t,e),o=await L(n,t,e,r.name);if(o==="valid")return"valid";o==="invalid"&&await M(n,t,e,r.name);try{return await X(n,t,r),"built"}catch(s){const a=await L(n,t,e,r.name);if(a==="valid")return"valid";if(a==="invalid"&&await M(n,t,e,r.name),!t.isUniqueViolation(s))throw s;return"refused"}},$t=3,$n=async(n,t,e)=>{const{companion:r}=e,o=await un(n,t,r);let s="";o!==""&&console.warn(`[@lunora/sql-store] search companion "${r}" folds case and accents in its unique key.${o}`);for(let a=0;a<$t;a+=1){const f=await dn(n,t,r);if(f==="valid")return await M(n,t,r,Y(r)),!0;if(f==="refused"){const u=await sn(n,t,r);if(u.length>0)await fn(n,t,e,u);else{const c=await cn(n,t,r);s=c>0?` ${String(c)} token(s) are held by document ids that agree on their first ${String(z)} characters, which the key cannot tell apart; shorten those ids.`:o}}}return await L(n,t,r,Z(t,r).name)==="valid"?(await M(n,t,r,Y(r)),!0):(console.warn(`[@lunora/sql-store] search companion "${r}" still has no unique key after ${String($t)} attempts: writes keep working, but two backfills racing over one row can index it twice.${s}`),!1)},wn=async(n,t,e,r)=>r==="step"?At:{uniqueKeyMissing:!await $n(n,t,e),unmappedSkipped:0},Sn=(n,t)=>t?i`${i.identifier(g)} LIKE ${`${n}%`}`:i`${i.identifier(g)} = ${n}`,Ot=(n,t)=>{const e=t.filters.map(r=>i`m.${rt(r.field)} = ${jt(r.value)}`);return n.softDeleteMode&&e.push(i`m.${rt(n.softDeleteMode.field)} IS NULL`),t.scope!==void 0&&e.push(i`(${t.scope})`),e},It=async(n,t,e,r,o,s,a)=>{const f=Ot(e,o);let u=i`SELECT m.* FROM (${a}) s JOIN ${i.identifier(r)} m ON m.${i.identifier("id")} = s.${i.identifier(S)}`;return f.length>0&&(u=i`${u} WHERE ${i.join(f,i` AND `)}`),u=i`${u} ORDER BY s.${i.identifier("__score__")} DESC, m.${i.identifier("_creationTime")} DESC, m.${i.identifier("id")} ASC LIMIT ${i.raw(String(s))}`,ht(e,await p(n,t,u))},En=async(n,t,e,r,o,s)=>{const a=b(o.query,I(o.definition.language));if(a.length===0)return[];const f=k(r,o.indexName),u=a.length-1,c=a.map((m,R)=>Sn(m,R===u)),d=i.join(c,i` OR `),w=c.map(m=>i`SUM(CASE WHEN ${m} THEN ${i.identifier(A)} ELSE 0 END)`),E=i.join(w.map(m=>i`${m} > 0`),i` AND `),l=i`SELECT ${i.identifier(S)}, ${i.join(w,i` + `)} AS ${i.identifier("__score__")} FROM ${i.identifier(f)} WHERE ${d} GROUP BY ${i.identifier(S)} HAVING ${E}`;return It(n,t,e,r,o,s,l)},ln=async(n,t,e,r,o,s)=>{const a=b(o.query,I(o.definition.language));if(a.length===0)return[];const f=k(r,o.indexName),u=`${f}__vocab`,c=a.length-1,d=a.map((l,m)=>{const R=Qt(l,m===c),_=R.exact?i`${i.identifier("term")} = ${R.lower}`:i`${i.identifier("term")} >= ${R.lower} AND ${i.identifier("term")} < ${R.upper}`;return i`SELECT ${i.identifier("doc")}, ${i.raw(String(m))} AS ${i.identifier("__term__")}, COUNT(*) AS ${i.identifier("__n__")} FROM ${i.identifier(u)} WHERE ${_} GROUP BY ${i.identifier("doc")}`}),w=a.map((l,m)=>i`SUM(CASE WHEN u.${i.identifier("__term__")} = ${i.raw(String(m))} THEN u.${i.identifier("__n__")} ELSE 0 END)`),E=i`SELECT f.${i.identifier(S)} AS ${i.identifier(S)}, ${i.join(w,i` + `)} AS ${i.identifier("__score__")} FROM (${Ht(d)}) u JOIN ${i.identifier(f)} f ON f.rowid = u.${i.identifier("doc")} GROUP BY f.${i.identifier(S)} HAVING ${i.join(w.map(l=>i`${l} > 0`),i` AND `)}`;return It(n,t,e,r,o,s,E)},mn=async(n,t,e,r,o,s)=>{const a=t.nativeTextSearch,f=b(o.query,I(o.definition.language));if(!a||f.length===0)return[];const u=k(r,o.indexName),c=[a.matches(u,f),...Ot(e,o)],d=i`SELECT m.* FROM ${i.identifier(u)} JOIN ${i.identifier(r)} m ON m.${i.identifier("id")} = ${i.identifier(u)}.${i.identifier(S)} WHERE ${i.join(c,i` AND `)} ORDER BY ${a.rank(u,f)} DESC, m.${i.identifier("_creationTime")} DESC, m.${i.identifier("id")} ASC LIMIT ${i.raw(String(s))}`;return ht(e,await p(n,t,d))},pn={ensureCompanion:async(n,t,e)=>{const{integer:r,key:o}=t.companionTypes;await T(n,t,i`CREATE TABLE IF NOT EXISTS ${i.identifier(e)} (${i.identifier(g)} ${i.raw(o)} NOT NULL, ${i.identifier(S)} ${i.raw(o)} NOT NULL, ${i.identifier(A)} ${i.raw(r)} NOT NULL)`),await X(n,t,{columns:K(t,S,191),name:`${e}__by_id`,table:e,unique:!1}),await on(n,t,e)},indexDocument:async(n,t,e,r,o,s)=>{const a=[...Tt(q(r,o),I(o.language))];await O(n,t,Nt(t,e,String(s.row.id),a,s))},migrate:wn,name:"inverted",purgeDocument:async(n,t,e,r,o)=>{await T(n,t,v(e,r,H(o,r)))},runSearch:En},hn={ensureCompanion:async(n,t,e)=>{await O(n,t,kt(e))},indexDocument:async(n,t,e,r,o,s)=>{await O(n,t,mt(e,String(s.row.id),q(r,o),{guard:D(t,s)}))},migrate:rn,name:"fts5",purgeDocument:async(n,t,e,r,o)=>{await O(n,t,pt(e,r,{guard:H(o,r)}))},runSearch:ln},gn={ensureCompanion:async(n,t,e)=>{const r=t.nativeTextSearch;if(r){await T(n,t,r.createCompanion(e,t.companionTypes.key));for(const o of r.createIndexes(e))await T(n,t,o)}},indexDocument:async(n,t,e,r,o,s)=>{const a=t.nativeTextSearch;a&&await T(n,t,a.indexDocument(e,String(s.row.id),q(r,o),D(t,s)))},name:"native",purgeDocument:async(n,t,e,r,o)=>{await T(n,t,v(e,r,H(o,r)))},runSearch:mn},C=(n,t)=>n.strategy==="native"&&t.nativeTextSearch!==void 0?gn:t.supportsFts5?hn:pn,P=(n,t)=>`${vt(n)}/${C(n,t).name}`,tt=function*(n){for(const[t,e]of Object.entries(n.tables)){const r=e.searchIndexes;if(!(e.shardMode!==void 0&&e.shardMode.kind!=="global"||!r))for(const o of r)yield[t,e,o]}},N=(n,t)=>k(n,t.name),Tn=async(n,t,e,r)=>{const o=N(e,r);return lt(await U(n,t,o),P(r,t)).finished?!0:Ft(n,t,o)},Fn=async(n,t,e,r,o,s)=>{if(!await Tn(n,t,r,o.definition))throw new h("SEARCH_INDEX_BUILDING",`search index "${o.indexName}" on table "${r}" is still backfilling and currently covers only part of the table — retry once it finishes${t.searchBackfillHint===void 0?"":`, or complete it now: ${t.searchBackfillHint}`}`);return C(o.definition,t).runSearch(n,t,e,r,o,s)},wt=n=>n.slice(n.lastIndexOf("/")+1),St=200,Et=5,yn=async(n,t,e,r)=>{const o=[...r.keys()],s=await p(n,t,i`SELECT * FROM ${i.identifier(e)} WHERE ${i.identifier("id")} >= ${o[0]} AND ${i.identifier("id")} <= ${o.at(-1)} ORDER BY ${i.identifier("id")} ASC`),a=new Map;for(const f of s){const u=f.id,c=r.get(u)?.row;c&&yt.some(d=>String(c[d])!==String(f[d]))&&a.set(u,{row:f})}return a},Q=async(n,t,e,r,o,s,a)=>{const f=C(s,t);let u=a;for(let c=0;u.size>0&&c<Et;c+=1){for(const{document:d,row:w}of u.values()){const E=d??V(e,w);E&&await f.indexDocument(n,t,o,E,s,{row:w,table:r})}u=await yn(n,t,r,u)}u.size>0&&console.warn(`[@lunora/sql-store] search index "${o}": ${String(u.size)} row(s) were rewritten on every one of ${String(Et)} re-check rounds and were left to their writers — each stays stale only if none of those writes changed its indexed text.`)},Ct=async(n,t,e,r,o)=>{const s=N(r,o),a=P(o,t),f=lt(await U(n,t,s),a);if(f.finished)return!0;if(f.wipe&&await F(n,t,s,void 0,!1,a),(await p(n,t,t.tableExists(r))).length===0)return await F(n,t,s,void 0,!0,a),!0;const c=await xt(n,t,r,f.cursor,St),d=c.findLast(l=>typeof l.id=="string")?.id,w=new Map;for(const l of c)typeof l.id=="string"&&w.set(l.id,{row:l});await Q(n,t,e,r,s,o,w);const E=c.length<St;return await F(n,t,s,d??f.cursor,E,a),E},Lt=async(n,t,e)=>{await Dt(n,e);for(const[r,,o]of tt(t)){const s=N(r,o),a=P(o,e),f=await U(n,e,s);f.profile!==void 0&&wt(f.profile)!==wt(a)&&(await T(n,e,i`DROP TABLE IF EXISTS ${i.identifier(s)}`),await T(n,e,i`DROP TABLE IF EXISTS ${i.identifier(Pt(s))}`),await qt(n,e,s)),await C(o,e).ensureCompanion(n,e,s)}},Rn=async(n,t,e,r,o)=>{const s=N(r,o);if((await U(n,t,s)).profile!==void 0)return;let f=0;(await p(n,t,t.tableExists(r))).length>0&&await Kt(n,t,e,r,()=>{f+=1},{limit:1}),await F(n,t,s,void 0,f===0,P(o,t))},Mt=async(n,t,e,r,o,s)=>await C(o,t).migrate?.(n,t,{companion:N(r,o),definition:e,index:o,tableName:r},s)??{uniqueKeyMissing:!1,unmappedSkipped:0},Un=async(n,t,e)=>{await Lt(n,t,e);for(const[r,o,s]of tt(t)){if(await Mt(n,e,o,r,s,"step"),s.staged){await Rn(n,e,o,r,s);continue}await Ct(n,e,o,r,s)}},bn=async(n,t,e)=>{const r={uniqueKeyMissing:[],unmappedSkipped:0};await Lt(n,t,e);for(const[o,s,a]of tt(t)){const f=await Mt(n,e,s,o,a,"drain");r.unmappedSkipped+=f.unmappedSkipped,f.uniqueKeyMissing&&r.uniqueKeyMissing.push(N(o,a));let u=!1;for(;!u;)u=await Ct(n,e,s,o,a)}return r},kn=n=>{const{dialect:t,exec:e,schema:r}=n;return async(o,s,a)=>{const f=r.tables[o],u=f?.searchIndexes;if(!(!f||!u||u.length===0))for(const c of u){if(tn(a?.previous,a?.document,c))continue;const d=N(o,c);if(a){await Q(e,t,f,o,d,c,new Map([[s,{document:a.document,row:{...a.written,id:s}}]]));continue}await C(c,t).purgeDocument(e,t,d,s,o);const E=(await J(e,t,o,[s])).get(s);E!==void 0&&await Q(e,t,f,o,d,c,new Map([[s,{row:E}]]))}}};export{Cn as A,vn as C,Dn as R,qn as T,Fn as a,Mn as b,kn as c,bn as d,Un as r,ot as u,Ln as x};
|
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.140",
|
|
4
4
|
"description": "Internal dialect-parameterized SQL store core for Lunora .global() backends (D1, PlanetScale)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@lunora/errors": "1.0.0-alpha.41",
|
|
54
|
-
"@lunora/shard-engine": "1.0.0-alpha.
|
|
54
|
+
"@lunora/shard-engine": "1.0.0-alpha.86",
|
|
55
55
|
"drizzle-orm": "^0.45.2"
|
|
56
56
|
},
|
|
57
57
|
"engines": {
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import{LunoraError as v}from"@lunora/errors";import{m as xt,D as Pt}from"./ctx-db-search-state-DGbsWTp6.mjs";import{c as Ht,r as Kt,A as jt,C as Gt,R as Vt,a as Yt,T as Xt,x as zt,b as Qt}from"./ctx-db-search-Dxuv99A0.mjs";import{d as gi}from"./ctx-db-search-Dxuv99A0.mjs";import{literalInList as Jt,throwingScheduler as Zt,CDC_LOG_TABLE as P,cdcForkedError as en,cursorBelowRetainedFloor as tn,cdcTrimmedError as nn,assertNoExplicitUndefined as nt,hasTrigger as on,runRowValidators as De,assertFlatPredicate as it,mergeWhere as B,rankTableName as we,sortColumnName as ye,resolveRankPartition as ot,encodePartitionKey as Ae,resolveRankSeekTuple as rn,RANK_TIEBREAK as pe,CountRlsUnsupportedError as rt,stripReservedPatchFields as sn,normalizeIdStructurally as an,assertValidClientId as cn,aggregateSqlFunction as $e,softDeleteScope as _e,compileWhereSql as he,normalizeOrderKeys as dn,uniqueIndexFields as ln,equalityPinnedFields as un,buildSeekWhere as fn,decodeCursor as wn,resolveRelationPredicates as st,resolveWith as at,relationHooks as ct,applySelect as dt,encodeCursor as $n,NotFoundError as _n,applyOnDelete as hn,ConflictError as Le,normalizeCountArgument as En,selectIndexForCount as yn,encodeAggregateKey as Y,aggregateTableName as Q,selectIndexForAggregate as pn,readAggregateValue as Be,runTriggers as Rn,matchesRankStaticWhere as lt,rankPivotConditionSql as St,CURSOR_PREFIX as Sn,selectIndexForGroupBy as gn,tiebreakDirectionFor as vn,fanOutScalarCounts as Tn,matchesStaticWhere as Fe,foldAggregateTally as ut,coerceAggregateNumber as Oe,CDC_LOG_TABLE_SEQ_INDEX as mn,NotUniqueError as bn}from"@lunora/shard-engine";import{sql as e}from"drizzle-orm";import{d as gt,b as In,e as vt,n as Cn,c as An}from"./value-codec-DC_dHxUY.mjs";import{runSqlGlobalTableMigrations as Ln,runSqlAggregateMigrations as kn,runSqlRankMigrations as Mn}from"./runSqlAggregateMigrations-CmsPHIJb.mjs";import{queryAll as m,queryRun as M,createIndexIfNotExists as Nn,decodeRow as ee,serializeDocumentColumn as J,OCC_VERSION_COLUMN as Ee,columnRefSql as F,decodeRows as ft,tableColumns as Tt,nextRowVersion as wt,serializeColumnValue as We,forEachRowPaged as $t,BACKFILL_BATCH_SIZE as Ue,nullSafeEqualsSql as Ke,queryBatch as _t}from"./decodeGlobalRow-VOl_TIbk.mjs";import{decodeGlobalRow as Ti}from"./decodeGlobalRow-VOl_TIbk.mjs";const qn=(a,c)=>{if(a.size<c)return;const E=a.keys().next().value;E!==void 0&&a.delete(E)},ht=50,Dn=(a,c={})=>{const E=c.maxBatch??ht,r=Number.isInteger(E)&&E>0?E:ht,p=new Map;let w=!1;const d=async(g,T)=>{const b=[...T.keys()];for(let C=0;C<b.length;C+=r){const N=b.slice(C,C+r);try{const W=await a(g,N);for(const G of N){const V=W.get(G);for(const ie of T.get(G)??[])ie.resolve(V)}}catch(W){for(const G of N)for(const V of T.get(G)??[])V.reject(W)}}},L=()=>{w=!1;const g=[...p.entries()];p.clear();for(const[T,b]of g)d(T,b).catch(()=>{})};return{async load(g,T){return new Promise((b,C)=>{let N=p.get(g);N||(N=new Map,p.set(g,N));const W=N.get(T);W?W.push({reject:C,resolve:b}):N.set(T,[{reject:C,resolve:b}]),w||(w=!0,queueMicrotask(L))})}}},Bn=new Set(["_id","id"]),mt=(a,c)=>a.name!=="postgres"||c.nullable!==!0?"":c.direction==="desc"?" NULLS LAST":" NULLS FIRST",Fn=(a,c)=>{const E=a.map(r=>e`${F(r.field)} ${e.raw(`${r.direction==="desc"?"DESC":"ASC"}${mt(c,r)}`)}`);if(!a.some(r=>Bn.has(r.field))){const r=vn(a);E.push(e`${F("id")} ${e.raw(r==="desc"?"DESC":"ASC")}`)}return e.join(E,e`, `)},Et=50,On=(a,c,E)=>{const r={...c};for(const[p,w]of Tt(a)){if(w.serverDefault){r[p]=w.serverDefault({auth:E});continue}r[p]===void 0&&(w.defaultFn?r[p]=w.defaultFn():"defaultValue"in w&&(r[p]=w.defaultValue))}return r},yt=(a,c,E,r)=>{for(const[p,w]of Tt(a)){if(w.serverDefault){p in c&&(E[p]=w.serverDefault({auth:r}));continue}w.onUpdateFn&&!(p in c)&&(E[p]=w.onUpdateFn())}},Wn=128,Un=()=>{const a=new Map;return{delete:c=>{a.delete(c)},get:c=>{const E=a.get(c);if(E!==void 0)return a.delete(c),a.set(c,E),E},set:(c,E)=>{a.has(c)?a.delete(c):qn(a,Wn),a.set(c,E)}}},xn=async(a,c,E,r,p)=>{const w=p.get(r);if(w!==void 0)return w;const d=Object.entries(E.tables).filter(([,g])=>g.shardMode===void 0||g.shardMode.kind==="global").map(([g])=>g),L=await Promise.all(d.map(async g=>({found:(await m(a,c,e`SELECT 1 FROM ${e.identifier(g)} WHERE ${e.identifier("id")} = ${r} LIMIT 1`)).length>0,tableName:g})));for(const g of L)if(g.found)return p.set(r,g.tableName),g.tableName},te=a=>a==null?null:Number(a),Pn=(a,c,E)=>E.map(r=>{const p={};for(const w of c){const d=a.shape[w];p[w]=In(r[w]??null,d&&vt(d))}return{key:p,value:te(r.value)}}),Hn=(a,c,E)=>[...a.partitionBy??[],...a.sortBy.map(p=>p.field),...a.where?Object.keys(a.where):[]].every(p=>c[p]===E[p]),bt=a=>a.length===1?a[0]:e`(${e.join(a,e` AND `)})`,Z=a=>e.join(a.map(c=>e`${e.identifier(c)}`),e`, `),ne=a=>e.join(a.map(c=>e`${c}`),e`, `),Kn=(a,c,E,r,p)=>{const w=[];for(let d=0;d<E.length+1;d+=1){const L=E[d],g=c.sortBy[d],T=d<E.length&&L!==void 0&&g!==void 0,b=g?.direction==="desc"?"desc":"asc",C=T?St(L,r[L],b,!1):e`${e.identifier(pe)} < ${p}`;if(C===void 0)continue;const N=E.slice(0,d).map(W=>Ke(a,e`${e.identifier(W)}`,r[W]));N.push(C),w.push(bt(N))}return w.length>0?e.join(w,e` OR `):e`1 = 0`},jn=(a,c,E)=>{if(E.length!==c.length)return;const r=[];for(const[p,w]of c.entries()){const d=St(w.column,E[p],w.direction,!0);if(d===void 0)continue;const L=[];for(let g=0;g<p;g+=1){const T=c[g];T!==void 0&&L.push(Ke(a,e`${e.identifier(T.column)}`,E[g]))}L.push(d),r.push(bt(L))}return r.length===0?e`(1 = 0)`:e`(${e.join(r,e` OR `)})`},Gn=(a,c)=>{if(a.sortBy.length===0)throw new v("INTERNAL",`rankIndex "${a.name}" requires at least one "sortBy" column for stable pagination`);const E=[{column:"__partition__",direction:"asc",nullable:!1}];for(const[r,p]of a.sortBy.entries())E.push({column:c[r]??ye(r),direction:p.direction,nullable:!0});return E.push({column:pe,direction:"asc",nullable:!1}),E},Vn=async(a,c,E,r,p)=>{const d=[];for(let b=0;b<p.length;b+=50)d.push(p.slice(b,b+50));const L=await Promise.all(d.map(async b=>m(a,c,e`SELECT * FROM ${e.identifier(r)} WHERE ${e.identifier("id")} IN (${ne(b)})`))),g=new Map;for(const b of L)for(const C of b)g.set(C.id,C);const T=[];for(const b of p){const C=ee(E,g.get(b));C&&T.push(C)}return T},Yn=a=>{const c=JSON.stringify(a),E=new TextEncoder().encode(c);let r="";for(const p of E)r+=String.fromCodePoint(p);return Sn+btoa(r)},It=a=>{if(a===void 0)return!1;const c=vt(a);return c==="any"||c==="bigint"||c==="from"||c==="union"},Pe=(a,c,E)=>{if(It(a.shape[c]))throw new v("BAD_REQUEST",`${E}: "${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)`)},Xn=(a,c,E,r)=>{for(const p of E)Pe(a,p,`groupBy(${c}, { by: [..."${p}"] })`);r.field!==void 0&&Pe(a,r.field,`groupBy(${c}, { agg: { op: "${r.op}", field: "${r.field}" } })`)},He="__cdc_sweep",Ct="global",zn=6e4,Qn=1e4,Jn=3e4;let pt=0;const Zn=async(a,c)=>{const{autoincrementPrimaryKey:E,key:r,real:p,text:w}=c.companionTypes;await M(a,c,e`CREATE TABLE IF NOT EXISTS ${e.identifier(P)} (${e.identifier("seq")} ${e.raw(E)}, ${e.identifier("ts")} ${e.raw(p)} 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 Nn(a,c,{columns:e`${d===void 0?e`${e.identifier("table")}`:e`${e.identifier("table")}(${e.raw(String(d))})`}, ${e.identifier("seq")}`,name:mn,table:P,unique:!1}),await M(a,c,e`CREATE TABLE IF NOT EXISTS ${e.identifier(He)} (${e.identifier("id")} ${e.raw(r)} NOT NULL PRIMARY KEY, ${e.identifier("lease_until")} ${e.raw(p)} NOT NULL)`),await M(a,c,e`INSERT INTO ${e.identifier(He)} (${e.identifier("id")}, ${e.identifier("lease_until")}) VALUES (${Ct}, ${0}) ${c.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${e.identifier("id")} = ${e.identifier("id")}`:e`ON CONFLICT(${e.identifier("id")}) DO NOTHING`}`)},ei=async(a,c,E)=>{const r=e`UPDATE ${e.identifier(He)} SET ${e.identifier("lease_until")} = ${E+zn} WHERE ${e.identifier("id")} = ${Ct} AND ${e.identifier("lease_until")} <= ${E}`;if(c.supportsReturning)return(await m(a,c,e`${r} RETURNING ${e.identifier("id")}`)).length>0;const p=await M(a,c,r);return(c.affectedRows?c.affectedRows(p??{rowsAffected:0}):0)>0},ti=async(a,c,E,r)=>{if(!await ei(a,c,r))return;const p=r-E;await M(a,c,e`
|
|
2
|
-
DELETE FROM ${e.identifier(P)} WHERE ${e.identifier("seq")} IN (
|
|
3
|
-
SELECT ${e.identifier("seq")} FROM (
|
|
4
|
-
SELECT ${e.identifier("seq")} FROM ${e.identifier(P)} WHERE ${e.identifier("ts")} <= ${p} ORDER BY ${e.identifier("seq")} ASC LIMIT ${e.raw(String(Qn))}
|
|
5
|
-
) AS ${e.identifier("expired")}
|
|
6
|
-
)
|
|
7
|
-
`)},At=async(a,c)=>{const E=await m(a,c,e`SELECT MIN(${e.identifier("seq")}) AS ${e.identifier("seq")} FROM ${e.identifier(P)}`),r=Number(E[0]?.seq??Number.NaN);return Number.isFinite(r)?r:void 0},ni=async(a,c)=>{if(c.name!=="sqlite")return;const E=await m(a,c,e`SELECT ${e.identifier("seq")} AS ${e.identifier("seq")} FROM ${e.identifier("sqlite_sequence")} WHERE ${e.identifier("name")} = ${P}`),r=Number(E[0]?.seq??Number.NaN);return Number.isFinite(r)?r:0},ii=a=>JSON.stringify(Cn(a)?An(a):a),oi=async(a,c,E,r,p,w,d)=>{await M(a,d,e`INSERT INTO ${e.identifier(P)} (${e.identifier("ts")}, ${e.identifier("table")}, ${e.identifier("id")}, ${e.identifier("op")}, ${e.identifier("doc")}) VALUES (${c}, ${E}, ${r}, ${p}, ${w===void 0?null:ii(w)})`)},yi=async(a,c,E)=>{const r=c.sinceSeq??0,p=Math.max(1,Math.min(c.limit??1e3,1e4)),w=await At(a,E),d=await ni(a,E);if(d!==void 0&&r>d)throw en(d,r,"global");if(w!==void 0&&tn(w,r))throw nn(w,r,"global");const g=(await m(a,E,e`SELECT seq, ts, ${e.identifier("table")}, id, op, doc FROM ${e.identifier(P)} WHERE seq > ${r} ORDER BY seq ASC LIMIT ${e.raw(String(p))}`)).map(T=>{const{doc:b}=T,C={id:String(T.id),op:String(T.op),seq:Number(T.seq),table:String(T.table),ts:Number(T.ts)};return typeof b=="string"?{...C,doc:gt(JSON.parse(b))}:C});return{changes:g,cursor:g.at(-1)?.seq??r}},ri=async(a,c,E,r={})=>{if(r.cursorOnly){const g=await m(a,E,e`SELECT MAX(seq) AS seq FROM ${e.identifier(P)}`),T=Number(g[0]?.seq??c);return{cursor:Number.isFinite(T)?T:c,tables:[]}}const p=await m(a,E,e`SELECT ${e.identifier("table")} AS ${e.identifier("table")}, MAX(seq) AS seq FROM ${e.identifier(P)} WHERE seq > ${c} GROUP BY ${e.identifier("table")}`);let w=c;const d=[];for(const g of p){const T=Number(g.seq);Number.isFinite(T)&&T>w&&(w=T),d.push(String(g.table))}const L=r.retained===!0?await At(a,E):void 0;return{cursor:w,tables:d,...L===void 0?{}:{floor:L}}},si=/string or blob too big/iu,ai=/row is too big/iu,ci=(a,c)=>{const{code:E,errno:r}=c,p=c instanceof Error?c.message:"";switch(a.name){case"mysql":return r===1118||E==="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 ai.test(p)?"the 8 KB heap page a tuple must fit once its wide columns have been TOASTed out":void 0;default:return si.test(p)?"the storage engine's per-row ceiling (2 MB on D1)":void 0}},di=(a,c,E)=>{const r=ci(a,c);if(r!==void 0)throw new v("PAYLOAD_TOO_LARGE",`document is too large to store in "${E}": 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.`)},Rt=(a,c,E)=>{throw a.isUniqueViolation(c)?new Le(`unique constraint violation on "${E}"`,"unique"):(di(a,c,E),c)},xe=new WeakMap,pi=a=>{const{crossShardCounter:c,crossShardReader:E,exec:r,maxRelationKeys:p,schema:w}=a,{dialect:d}=a,L={fieldRef:F,...d.name==="mysql"?{containsExpr:(t,n)=>e`LOCATE(${n}, ${t}) > 0`}:{},...d.name==="postgres"?{containsExpr:(t,n)=>e`strpos(${t}, ${n}) > 0`}:{},...d.name==="sqlite"?{}:{inList:Jt}},g=new WeakMap,T=t=>{const n=g.get(t);if(n!==void 0)return n;const i={...L,serialize:(o,s)=>J(t,s,o)};return g.set(t,i),i},b=(t,n)=>Ke(d.name,t,n),C=t=>{const n=Z(t.columns),i=ne(t.values),o=u=>d.name==="mysql"?e`VALUES(${e.identifier(u)})`:e`excluded.${e.identifier(u)}`,s=u=>d.name==="postgres"?e`${e.identifier(t.table)}.${e.identifier(u)}`:e`${e.identifier(u)}`,l=e.join(Object.entries(t.set(o,s)).map(([u,y])=>e`${e.identifier(u)} = ${y}`),e`, `),h=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}) ${h}`},N=a.clock??(()=>Date.now()),W=a.idGenerator??(()=>crypto.randomUUID()),G=a.cdc??!1,V=typeof a.cdcRetentionMs=="number"&&Number.isFinite(a.cdcRetentionMs)&&a.cdcRetentionMs>0?a.cdcRetentionMs:void 0,ie=a.auth??{identity:null,userId:null},oe=async(t,n,i,o)=>{if(!G||(await oi(r,N(),t,n,i,o,d),V===void 0))return;const s=N();if(!(s-pt<Jn)){pt=s;try{await ti(r,d,V,s)}catch{}}},Lt=a.scheduler??Zt,ke=Un();let Re=0;const Me=a.provisionScope??{},U=async()=>{const t=xe.get(Me);if(t)return t;const n=(async()=>{await Ln(r,w,d),await kn(r,w,d),await Mn(r,w,d),await xt(r,d),await Kt(r,w,d),G&&await Zn(r,d)})().catch(i=>{throw xe.delete(Me),i});return xe.set(Me,n),n},re=async(t,n)=>{await U();const i=await xn(r,d,w,t,ke);if(!(n!==void 0&&i!==n))return i},se=new Map,je=async(t,n)=>{const i=Q(t,n);return(await m(r,d,d.tableExists(i))).length>0},Se=async(t,n)=>{const i=`${t}::${n.name}`,o=se.get(i);if(o!==void 0)return o;if(!await je(t,n.name))return se.set(i,!1),!1;const l=w.tables[t];if(!l)return se.set(i,!1),!1;const h=n.by??[],u=new Map;await $t(r,d,l,t,$=>{if(n.where&&!Fe($,n.where))return;const _=Y(h,$);ut(u,_,n,$)});const y=Q(t,n.name);await M(r,d,e`DELETE FROM ${e.identifier(y)}`);const f=[];for(const[$,_]of u)f.push(e`INSERT INTO ${e.identifier(y)} (${e.identifier("__key__")}, ${e.identifier("__value__")}, ${e.identifier("__count__")}) VALUES (${$}, ${_.value}, ${_.count})`);return await _t(r,d,f),se.set(i,!0),!0},kt=async(t,n,i,o)=>{const s=new Map;let l,h=!0;for(;h;){const u=l===void 0?o:[...o,e`${e.identifier("id")} > ${l}`],y=u.length>0?e` WHERE ${e.join(u,e` AND `)}`:e``,f=await m(r,d,e`SELECT ${e.identifier("id")}, ${F(i.field??"")} FROM ${e.identifier(n)}${y} ORDER BY ${e.identifier("id")} ASC LIMIT ${e.raw(String(Ue))}`);for(const $ of ft(t,f))ut(s,"",i,$);l=f.at(-1)?.id,h=f.length===Ue}return s.get("")?.value??null},Mt=async(t,n,i)=>{const o=n.field??"",s=[],l=w.tables[t],h=($,_)=>l?J(l,$,_):We(_);for(const $ of n.by??[]){const _=h($,i[$]??null);s.push(_===null?e`${F($)} IS NULL`:e`${F($)} = ${_}`)}for(const[$,_]of Object.entries(n.where??{})){const R=_!==null&&typeof _=="object"&&!Array.isArray(_)?_.eq:_,S=h($,R);s.push(S===null?e`${F($)} IS NULL`:e`${F($)} = ${S}`)}const u=s.length>0?e` WHERE ${e.join(s,e` AND `)}`:e``;if(l&&It(l.shape[o]))return kt(l,t,n,s);const y=$e(n.op),f=await m(r,d,e`SELECT ${e.raw(y)}(${F(o)}) AS ${e.identifier("value")} FROM ${e.identifier(t)}${u}`);return te(f[0]?.value)},Nt=async(t,n)=>{await M(r,d,e`DELETE FROM ${e.identifier(t)} WHERE ${e.identifier("__key__")} = ${n} AND ${e.identifier("__count__")} <= 0`)},qt=async(t,n,i,o)=>{const s=Q(t,n.name),{op:l}=n,h=n.field??"",u=i&&(!n.where||Fe(i,n.where))?i:void 0,y=o&&(!n.where||Fe(o,n.where))?o:void 0;if(!(!u&&!y)){if(l==="count"||l==="sum"||l==="avg"){const f=new Set;for(const[$,_]of[[u,-1],[y,1]]){if(!$)continue;const R=l==="count"?1:Oe($[h]);if(R===void 0)continue;const S=Y(n.by??[],$);f.add(S),await M(r,d,C({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(I,A)=>({__count__:e`${A("__count__")} + ${I("__count__")}`,__value__:l==="count"?e`${A("__value__")} + ${I("__value__")}`:e`COALESCE(${A("__value__")}, 0) + ${I("__value__")}`}),table:s,values:[S,_*R,_]}))}for(const $ of f)await Nt(s,$);return}if(u){const f=Y(n.by??[],u),$=Oe(u[h]),R=(await m(r,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(s)} WHERE ${e.identifier("__key__")} = ${f}`))[0],S=te(R?.value),I=(R?.count??0)-1;if(I<=0)await M(r,d,e`DELETE FROM ${e.identifier(s)} WHERE ${e.identifier("__key__")} = ${f}`);else if(R&&$!==void 0&&S!==null&&$===S){const A=await Mt(t,n,u);await M(r,d,e`UPDATE ${e.identifier(s)} SET ${e.identifier("__value__")} = ${A}, ${e.identifier("__count__")} = ${I} WHERE ${e.identifier("__key__")} = ${f}`)}else await M(r,d,e`UPDATE ${e.identifier(s)} SET ${e.identifier("__count__")} = ${e.identifier("__count__")} - 1 WHERE ${e.identifier("__key__")} = ${f}`)}if(y){const f=Y(n.by??[],y),$=Oe(y[h]);if($===void 0)await M(r,d,C({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(_,R)=>({__count__:e`${R("__count__")} + 1`}),table:s,values:[f,null,1]}));else{const _=l==="min"?"MIN":"MAX";await M(r,d,C({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(R,S)=>({__count__:e`${S("__count__")} + 1`,__value__:e`${e.raw(_)}(COALESCE(${S("__value__")}, ${R("__value__")}), ${R("__value__")})`}),table:s,values:[f,$,1]}))}}}},ge=async t=>{const n=w.tables[t]?.aggregateIndexes;if(!(!n||n.length===0))for(const i of n)await Se(t,i)},ae=async(t,n,i)=>{const o=w.tables[t]?.aggregateIndexes;if(!(!o||o.length===0))for(const s of o){const l=`${t}::${s.name}`;(se.get(l)??await je(t,s.name))&&await qt(t,s,n,i)}},ce=new Map,Ge=async(t,n)=>{const i=we(t,n);return(await m(r,d,d.tableExists(i))).length>0},Ne=async(t,n)=>{const i=`${t}::rank::${n.name}`,o=ce.get(i);if(o!==void 0)return o;if(!await Ge(t,n.name))return ce.set(i,!1),!1;const l=w.tables[t];if(!l)return ce.set(i,!1),!1;const h=we(t,n.name);await M(r,d,e`DELETE FROM ${e.identifier(h)}`);const u=n.sortBy.map((_,R)=>ye(R)),y=Z(["__id__","__partition__",...u]);let f=[];const $=async()=>{if(f.length===0)return;const _=f;f=[],await _t(r,d,_)};return await $t(r,d,l,t,async _=>{if(n.where&&!lt(_,n.where))return;const R=Ae(n.partitionBy??[],_),S=n.sortBy.map(I=>We(_[I.field]??null));f.push(e`INSERT INTO ${e.identifier(h)} (${y}) VALUES (${ne([_._id,R,...S])})`),f.length>=Ue&&await $()}),await $(),ce.set(i,!0),!0},ve=async t=>{const n=w.tables[t]?.rankIndexes;if(!(!n||n.length===0))for(const i of n)await Ne(t,i)},X=async(t,n,i,o)=>{const s=w.tables[t],l=s?.rankIndexes;if(!l||l.length===0)return;const h=s?.softDeleteMode?.field,u=h===void 0||o===void 0?void 0:o[h],y=u!=null;for(const f of l){if(i!==void 0&&o!==void 0&&Hn(f,i,o))continue;const $=`${t}::rank::${f.name}`;if(!(ce.get($)??await Ge(t,f.name)))continue;const S=we(t,f.name);if(i&&await M(r,d,e`DELETE FROM ${e.identifier(S)} WHERE ${e.identifier("__id__")} = ${n}`),o&&!y){if(f.where&&!lt(o,f.where))continue;const I=f.sortBy.map((D,j)=>ye(j)),A=Z(["__id__","__partition__",...I]),O=Ae(f.partitionBy??[],o),k=f.sortBy.map(D=>We(o[D.field]??null));await M(r,d,e`INSERT INTO ${e.identifier(S)} (${A}) VALUES (${ne([n,O,...k])})`)}}},de=Ht({dialect:d,exec:r,schema:w}),Ve=new Set;for(const[t,n]of Object.entries(w.tables))for(const i of Object.values(n.triggerMap??{}))Ve.add(`${t} ${i.timing} ${i.op}`);const H=(t,n,i)=>Ve.has(`${t} ${n} ${i}`);let Ye;const K=async(t,n,i)=>{if(Re+=1,Re>Et)throw Re-=1,new Le(`trigger recursion exceeded ${String(Et)} levels on "${i.table}" — check for a self-triggering write`,"trigger");try{await Rn({ctx:Ye,event:i,op:n,schema:w,tableName:i.table,timing:t})}finally{Re-=1}},Dt=async(t,n)=>{try{await M(r,d,n)}catch(i){Rt(d,i,t)}},Te=async(t,n)=>(await m(r,d,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} = ${n}`))[0],me=async(t,n,i,o)=>{if(o===void 0)return;const s=e`${e.identifier(Ee)}`,l=e.join([b(e`${e.identifier("id")}`,o.id),b(s,o[Ee]??null)],e` AND `),h=n==="UPDATE"?e`UPDATE ${e.identifier(t)} SET ${i}, ${s} = ${wt.sql(s)} WHERE ${l}`:e`DELETE FROM ${e.identifier(t)} WHERE ${l}`,u=()=>{throw new Le(`optimistic concurrency conflict on "${t}" — the row changed during this mutation; refetch and retry`,"occ")};try{if(d.supportsReturning)(await m(r,d,e`${h} RETURNING ${e.identifier("id")}`)).length===0&&u();else{const y=await M(r,d,h);(d.affectedRows?d.affectedRows(y??{rowsAffected:0}):0)===0&&u()}}catch(y){Rt(d,y,t)}},qe=(t,n=t?._creationTime)=>({_creationTime:n,[Ee]:wt.value(t?.[Ee])}),Bt=(t,n,i,o)=>{const s=Object.keys(t.shape);return{columns:["id","_creationTime",...s],values:[n,i,...s.map(l=>J(t,l,o[l]??null))]}},Ft=async(t,n,i,o)=>{const s=gn(n,i.op,i.field,o.by,o.where);if(!s||!await Se(t,s.index))return;const h=Q(t,s.index.name),u=Object.keys(s.partial);if(u.length===(s.index.by??[]).length&&u.length>0){const f=Y(s.index.by??[],s.partial),$=await m(r,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(h)} WHERE ${e.identifier("__key__")} = ${f}`);if($.length===0)return[];const _=$[0];return[{key:{...s.partial},value:Be(i.op,{count:_.count,value:te(_.value)})}]}return u.length>0?void 0:(await m(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(h)}`)).map(f=>{const $=f;return{key:gt(JSON.parse($.key)),value:Be(i.op,{count:$.count,value:te($.value)})}})},Xe=t=>{const n=w.tables[t]?.shardMode?.kind;return n==="shardBy"||n==="root"},ze=t=>{throw new v("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`)},Ot=(t,n)=>{if(n?.relationMask!==void 0&&n.with!==void 0&&Object.keys(n.with).length>0)throw new v("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 o of Object.keys(w.tables)){const s=n.relationBaseWhere(o);s!==void 0&&(i[o]=s)}return{orderBy:n?.orderBy,relationPolicies:i,where:B(n?.baseWhere,n?.where),with:n?.with}},Qe=(t,n)=>Xe(t)?E?E(t,Ot(t,n)):ze(t):x.findMany(t,n),be=(t,n,i)=>st(t,{fetcher:Qe,maxRelationKeys:p,relationBaseWhere:i,schema:w,tableName:n}),Wt=Dn(async(t,n)=>{const i=await m(r,d,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} IN (${ne(n)})`),o=new Map;for(const s of i){const{id:l}=s;typeof l=="string"&&o.set(l,s)}return o}),x={async cdcChangedTables(t,n){if(G)try{return await U(),await ri(r,t,d,{...n,retained:V!==void 0})}catch{return}},async aggregate(t,n){const i=w.tables[t];if(!i)throw new v("INTERNAL",`unknown table: ${t}`);if(await U(),$e(n.op),n.op==="count")return x.count(t,{baseWhere:n.baseWhere,relationBaseWhere:n.relationBaseWhere,restrictsCounts:n.restrictsCounts,where:n.where});if(!n.field)throw new v("INTERNAL",`aggregate(${t}, { op: "${n.op}" }): "field" is required for non-count reducers`);const o=_e(i.softDeleteMode,void 0),s=B(B(n.baseWhere,n.where),o),l=await be(s,t,n.relationBaseWhere),h=l!==s;if(i.aggregateIndexes&&!n.baseWhere&&!h&&!o){const R=pn(i.aggregateIndexes,n.op,n.field,n.where);if(R&&await Se(t,R.index)){const I=Y(R.index.by??[],R.key),A=Q(t,R.index.name),k=(await m(r,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(A)} WHERE ${e.identifier("__key__")} = ${I}`))[0];return Be(n.op,k===void 0?void 0:{count:k.count,value:te(k.value)})}}Pe(i,n.field,`aggregate(${t}, { op: "${n.op}", field: "${n.field}" })`);const u=he(l,T(i)),y=e.raw($e(n.op)),f=e`SELECT ${y}(${F(n.field)}) AS ${e.identifier("value")} FROM ${e.identifier(t)}`,_=(await m(r,d,u?e`${f} WHERE ${u}`:f))[0]?.value;return _==null?null:Number(_)},async count(t,n){const i=w.tables[t];if(!i)throw new v("INTERNAL",`unknown table: ${t}`);await U();const o=En(n);if(o.restrictsCounts)throw new rt(t);const s=_e(i.softDeleteMode,void 0),l=B(B(o.baseWhere,o.where),s),h=await be(l,t,o.relationBaseWhere),u=h!==l;if(i.aggregateIndexes&&!o.baseWhere&&!u&&!s){const _=yn(i.aggregateIndexes,o.where);if(_&&await Se(t,_.index)){const S=Y(_.index.by??[],_.key),I=Q(t,_.index.name),A=await m(r,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")} FROM ${e.identifier(I)} WHERE ${e.identifier("__key__")} = ${S}`);return Number(A[0]?.value??0)}}const y=he(h,T(i)),f=e`SELECT COUNT(*) AS ${e.identifier("count")} FROM ${e.identifier(t)}`,$=await m(r,d,y?e`${f} WHERE ${y}`:f);return Number($[0]?.count??0)},async delete(t,n,i){const o=await re(t,n);if(!o)return;const s=w.tables[o];if(!s)return;const l=await Te(o,t),h=ee(s,l),u=i?.hard===!0,y=!u&&s.softDeleteMode?s.softDeleteMode.field:void 0;if(!(y&&(!h||h[y]!==null&&h[y]!==void 0))){if(H(o,"before","delete")&&await K("before","delete",{id:t,op:"delete",previous:h??void 0,table:o}),await hn({deletedId:t,deletedReference:f=>h?.[f],findHolders:async(f,$,_)=>{if(w.tables[f]?.shardMode?.kind==="shardBy")throw new v("INTERNAL",`cross-backend cascade from global '${o}' into shardBy '${f}' is not supported — would require Query Coordinator fan-out across shards`);return(await x.findMany(f,{includeDeleted:u,where:{[$]:_}})).page},onCascade:(f,$)=>x.delete($,void 0,i),onRestrict:f=>{throw new Le(f,"restrict")},onSetNull:(f,$,_)=>x.patch($,{[_]:null}),schema:w,tableName:o}),await ge(o),await ve(o),y&&h){const f={...h,[y]:N()},$=e.join(Object.keys(s.shape).map(_=>e`${e.identifier(_)} = ${J(s,_,f[_]??null)}`),e`, `);await me(o,"UPDATE",$,l),await ae(o,h,f),await X(o,t,h,void 0),await de(o,t,{document:f,previous:h,written:qe(l)}),await oe(o,t,"update",f),H(o,"after","delete")&&await K("after","delete",{id:t,op:"delete",previous:h,table:o});return}await me(o,"DELETE",void 0,l),ke.delete(t),await ae(o,h??void 0,void 0),await X(o,t,h??void 0,void 0),await de(o,t,void 0),await oe(o,t,"delete"),H(o,"after","delete")&&await K("after","delete",{id:t,op:"delete",previous:h??void 0,table:o})}},async findFirst(t,n={}){return(await x.findMany(t,{...n,limit:1})).page[0]??null},async findFirstOrThrow(t,n={}){const i=await x.findFirst(t,n);if(i===null)throw new _n(`findFirstOrThrow: no "${t}" document matched`);return i},async findMany(t,n={}){const i=w.tables[t];if(!i)throw new v("INTERNAL",`unknown table: ${t}`);await U();const o=dn(n.orderBy,i.shape,{pinned:un(n.where),uniqueBy:ln(i.indexes,i.shape)}),s=n.cursor?fn(o,wn(n.cursor)):void 0,l=Qe,h=async(k,D,j,Ie)=>{if(Xe(k))return c?Tn(c,k,D,j,Ie):ze(k);const le=w.tables[k];if(!le)throw new v("INTERNAL",`unknown table: ${k}`);const Ce=_e(le.softDeleteMode,void 0),z={[D]:{in:j}},q=B(B(z,Ie),Ce),ue=await be(q,k,void 0),Je=he(ue,T(le)),Ze=F(D);let fe=e`SELECT ${Ze} AS __fk__, COUNT(*) AS ${e.identifier("count")} FROM ${e.identifier(k)}`;Je&&(fe=e`${fe} WHERE ${Je}`),fe=e`${fe} GROUP BY ${Ze}`;const Ut=await m(r,d,fe),et=new Map;for(const tt of Ut)et.set(tt.__fk__,Number(tt.count??0));return et};let u=B(n.baseWhere,n.where);u=B(u,_e(i.softDeleteMode,n.includeDeleted)),u=await st(u,{fetcher:l,maxRelationKeys:p,relationBaseWhere:n.relationBaseWhere,schema:w,tableName:t}),s&&(u=u?{AND:[u,s]}:s);const y=he(u,T(i)),f=Fn(o,d);let $=e`SELECT * FROM ${e.identifier(t)}`;y&&($=e`${$} WHERE ${y}`),$=e`${$} ORDER BY ${f}`;const _=typeof n.limit=="number"?Math.max(0,Math.floor(n.limit)):void 0;_!==void 0&&($=e`${$} LIMIT ${e.raw(String(_+1))}`);const R=await m(r,d,$),S=ft(i,R);if(_===void 0)return n.with&&await at({fetcher:l,groupedCounter:h,parents:S,...ct(n),schema:w,tableName:t,with:n.with}),{continueCursor:null,isDone:!0,page:dt(S,n.select,n.with)};const I=S.length>_,A=I?S.slice(0,_):S,O=A.at(-1);return n.with&&await at({fetcher:l,groupedCounter:h,parents:A,...ct(n),schema:w,tableName:t,with:n.with}),{continueCursor:I&&O?$n(O,o):null,isDone:!I,page:dt(A,n.select,n.with)}},async get(t,n){const i=await re(t,n);if(!i)return null;const o=w.tables[i];return o?ee(o,await Wt.load(i,t)):null},async groupBy(t,n){const i=w.tables[t];if(!i)throw new v("INTERNAL",`unknown table: ${t}`);await U();const o=n.agg??{op:"count"};if($e(o.op),o.op!=="count"&&!o.field)throw new v("INTERNAL",`groupBy(${t}, { agg: { op: "${o.op}" } }): "field" is required for non-count reducers`);const s=_e(i.softDeleteMode,void 0),l=B(B(n.baseWhere,n.where),s),h=await be(l,t,n.relationBaseWhere),u=h!==l;if(i.aggregateIndexes&&!n.baseWhere&&!u&&!s){const S=await Ft(t,i.aggregateIndexes,o,n);if(S!==void 0)return S}Xn(i,t,n.by,o);const y=he(h,T(i)),f=n.by.map(S=>e`${F(S)} AS ${e.identifier(S)}`);if(o.op==="count")f.push(e`COUNT(*) AS ${e.identifier("value")}`);else{if(!o.field)throw new v("INTERNAL",`groupBy(${t}, { agg: { op: "${o.op}" } }): "field" is required for non-count reducers`);f.push(e`${e.raw($e(o.op))}(${F(o.field)}) AS ${e.identifier("value")}`)}const $=e.join(n.by.map(S=>F(S)),e`, `);let _=e`SELECT ${e.join(f,e`, `)} FROM ${e.identifier(t)}`;y&&(_=e`${_} WHERE ${y}`),_=e`${_} GROUP BY ${$}`;const R=await m(r,d,_);return Pn(i,n.by,R)},async insert(t,n,i){const o=w.tables[t];if(!o)throw new v("INTERNAL",`unknown table: ${t}`);await U();const s=On(o,n,ie);De(o,s);let l,h=!0;i?.clientId!==void 0?(cn(i.clientId),l=i.clientId):i?.allowExplicitId&&typeof s._id=="string"?l=s._id:(l=W(),h=!1);const u=i?.allowExplicitId&&typeof s._creationTime=="number"?s._creationTime:N(),y={...s,_creationTime:u,_id:l};H(t,"before","insert")&&await K("before","insert",{doc:{...y},id:l,op:"insert",table:t}),await ge(t),await ve(t);const{columns:f,values:$}=Bt(o,l,u,s);return await Dt(t,e`INSERT INTO ${e.identifier(t)} (${Z(f)}) VALUES (${ne($)})`),h&&ke.set(l,t),await ae(t,void 0,y),await X(t,l,void 0,y),await de(t,l,{document:y,written:{_creationTime:u,[Ee]:null}}),await oe(t,l,"insert",y),H(t,"after","insert")&&await K("after","insert",{doc:y,id:l,op:"insert",table:t}),l},normalizeId(t,n){return an(w,t,n)},async patch(t,n,i){nt("patch",n);const o=await re(t,i);if(!o)throw new v("INTERNAL",`document not found: ${t}`);const s=w.tables[o];if(!s)throw new v("INTERNAL",`document not found: ${t}`);const l=await Te(o,t),h=ee(s,l);if(!h)throw new v("INTERNAL",`document not found: ${t}`);const u={...h,...sn(n),_id:t};yt(s,n,u,ie),De(s,u,!0),H(o,"before","update")&&await K("before","update",{doc:{...u},id:t,op:"update",previous:h,table:o}),await ge(o),await ve(o);const y=Object.keys(s.shape),f=e.join(y.map($=>e`${e.identifier($)} = ${J(s,$,u[$]??null)}`),e`, `);await me(o,"UPDATE",f,l),await ae(o,h,u),await X(o,t,h,u),await de(o,t,{document:u,previous:h,written:qe(l)}),await oe(o,t,"update",u),H(o,"after","update")&&await K("after","update",{doc:u,id:t,op:"update",previous:h,table:o})},async restore(t,n){const i=await re(t,n);if(!i)throw new v("INTERNAL",`document not found: ${t}`);const o=w.tables[i],s=o?.softDeleteMode?.field;if(!o||!s)throw new v("INTERNAL",`ctx.db.restore: table "${i}" is not a .softDelete() table`);const l=await Te(i,t),h=l?.[s]!==null&&l?.[s]!==void 0;if(await x.patch(t,{[s]:null},n),h){const u=ee(o,l);u!==null&&await X(i,t,void 0,{...u,[s]:null})}},query(t){const n=w.tables[t];if(!n)throw new v("INTERNAL",`unknown table: ${t}`);const i="the legacy query()/withIndex() reader is not available on the D1 (global) backend; use findMany",o=[],s=u=>o.every(y=>y(u)),l=async(u,y)=>{await U();const f=o.length>0,$=await Yt(r,d,n,t,u,Xt(f?void 0:y));if(!f)return y===void 0&&zt($),$;const _=$.filter(R=>s(R));return y===void 0?_:_.slice(0,y)},h=u=>{const y={async*[Symbol.asyncIterator](){if(!u)throw new v("INTERNAL",i);yield*await l(u,void 0)},async collect(){if(!u)throw new v("INTERNAL",i);return l(u,void 0)},async collectWithScores(){throw new v("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(f){return o.push(f),y},async first(){if(!u)throw new v("INTERNAL",i);return(await l(u,1))[0]??null},order(){return y},async paginate(f){if(!u)throw new v("INTERNAL",i);const $=Gt(f);return Vt(await l(u,Qt($)),$)},async take(f){if(!u)throw new v("INTERNAL",i);return l(u,f)},async unique(){if(!u)throw new v("INTERNAL",i);const f=await l(u,2);if(f.length>1)throw new bn(`unique() on table "${t}" matched ${String(f.length)} documents; expected at most one`);return f[0]??null},withGeoIndex(){throw new v("INTERNAL",`geo indexes are not supported on \`.global()\` tables (table "${t}") — geospatial queries run only on sharded tables`)},withIndex(){throw new v("INTERNAL",i)},withSearchIndex(f,$){const _=(n.searchIndexes??[]).find(S=>S.name===f);if(!_)throw new v("INTERNAL",`unknown search index "${f}" on table "${t}"`);const R={definition:_,field:_.field,filters:[],hasQuery:!1,indexName:f,query:""};if($(jt(R,t,Pt(_.language))),!R.hasQuery)throw new v("INTERNAL",`search index "${f}" on table "${t}" requires a .search(field, query) call`);return h(R)}};return y};return h(void 0)},async rank(t,n,i){const o=w.tables[t];if(!o)throw new v("INTERNAL",`unknown table: ${t}`);const s=o.rankIndexes?.find(D=>D.name===n);if(!s)throw new v("INTERNAL",`unknown rankIndex "${n}" on table "${t}"`);if(i.restrictsCounts)throw new rt(t);if(await U(),!await Ne(t,s))return null;const h=typeof i.row=="string"?i.row:i.row._id;if(!h)return null;const u=we(t,s.name),y=s.sortBy.map((D,j)=>ye(j)),f=Z(["__partition__",...y]),_=(await m(r,d,e`SELECT ${f} FROM ${e.identifier(u)} WHERE ${e.identifier("__id__")} = ${h}`))[0];if(!_)return null;const R=_.__partition__,S=B(i.baseWhere,i.where);it(S,w,t,"rank");const I=ot(s,S);if(I&&Ae(s.partitionBy??[],I)!==R)return null;const A=Kn(d.name,s,y,_,h),O=await m(r,d,e`SELECT COUNT(*) AS c FROM ${e.identifier(u)} WHERE ${e.identifier("__partition__")} = ${R} AND (${A})`),k=await m(r,d,e`SELECT COUNT(*) AS c FROM ${e.identifier(u)} WHERE ${e.identifier("__partition__")} = ${R}`);return{position:Number(O[0]?.c??0)+1,total:Number(k[0]?.c??0)}},async rankPage(t,n,i={}){it(B(i.baseWhere,i.where),w,t,"rankPage");const o=w.tables[t];if(!o)throw new v("INTERNAL",`unknown table: ${t}`);const s=o.rankIndexes?.find(q=>q.name===n);if(!s)throw new v("INTERNAL",`unknown rankIndex "${n}" on table "${t}"`);if(await U(),!await Ne(t,s))return{continueCursor:null,isDone:!0,page:[]};const h=we(t,s.name),u=s.sortBy.map((q,ue)=>ye(ue)),y=Math.max(1,Math.min(1e3,Math.floor(i.take??100))),f=B(i.baseWhere,i.where),$=ot(s,f),_=Gn(s,u),R=e.join(_.map(q=>e`${e.identifier(q.column)} ${e.raw(`${q.direction==="desc"?"DESC":"ASC"}${mt(d,{direction:q.direction,nullable:q.nullable})}`)}`),e`, `),S=[];typeof i.partitionKey=="string"?S.push(e`${e.identifier("__partition__")} = ${i.partitionKey}`):$&&S.push(e`${e.identifier("__partition__")} = ${Ae(s.partitionBy??[],$)}`);const I=rn(i);if(I!==void 0){const q=jn(d.name,_,I);q!==void 0&&S.push(q)}const A=Z([pe,"__partition__",...u]);let O=e`SELECT ${A} FROM ${e.identifier(h)}`;S.length>0&&(O=e`${O} WHERE ${e.join(S,e` AND `)}`),O=e`${O} ORDER BY ${R} LIMIT ${e.raw(String(y+1))}`;const k=await m(r,d,O),D=k.length>y,j=D?k.slice(0,y):k,Ie=j.map(q=>q[pe]),le=await Vn(r,d,o,t,Ie);let Ce=null;const z=j.at(-1);if(D&&z!==void 0){const q=[z.__partition__,...u.map(ue=>z[ue]),z[pe]];Ce=Yn(q)}return{continueCursor:Ce,isDone:!D,page:le}},async replace(t,n,i,o){nt("replace",n);const s=await re(t,i);if(!s)throw new v("INTERNAL",`document not found: ${t}`);const l=w.tables[s];if(!l)throw new v("INTERNAL",`document not found: ${t}`);const h=await Te(s,t);if(h===void 0)throw new v("INTERNAL",`document not found: ${t}`);const y=on(w,s,"update")||(l.aggregateIndexes??[]).length>0||(l.rankIndexes??[]).length>0?ee(l,h)??void 0:void 0,f=o?.allowExplicitId&&typeof n._creationTime=="number"?n._creationTime:void 0,$=typeof h._creationTime=="number"?h._creationTime:void 0,_=f??$??N(),R={...n,_creationTime:_,_id:t};yt(l,n,R,ie),De(l,R),H(s,"before","update")&&await K("before","update",{doc:{...R},id:t,op:"update",previous:y,table:s}),await ge(s),await ve(s);const S=Object.keys(l.shape),I=e.join([e`${e.identifier("_creationTime")} = ${_}`,...S.map(A=>e`${e.identifier(A)} = ${J(l,A,R[A]??null)}`)],e`, `);await me(s,"UPDATE",I,h),await ae(s,y,R),await X(s,t,y,R),await de(s,t,{document:R,previous:y,written:qe(h,_)}),await oe(s,t,"update",R),H(s,"after","update")&&await K("after","update",{doc:R,id:t,op:"update",previous:y,table:s})}};return Ye={db:x,scheduler:Lt},x};export{gi as backfillSqlSearchIndexes,pi as createSqlCtxDb,Ti as decodeGlobalRow,ri as readSqlCdcChangedTables,yi as readSqlCdcChanges,At as readSqlCdcFloor,Zn as runSqlCdcMigration,Kt as runSqlSearchMigrations,ti as sweepSqlCdcRetention};
|