semanticdb-v2 2.0.0-alpha.1 → 2.0.0-alpha.2

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.
Files changed (48) hide show
  1. package/README.md +2 -0
  2. package/dist/calendar/constants.d.ts +2 -2
  3. package/dist/calendar/constants.js +1 -1
  4. package/dist/compatibility/logicform.js +1 -1
  5. package/dist/compiler/compile-cte.js +1 -1
  6. package/dist/compiler/compile-plan.js +1 -1
  7. package/dist/compiler/shorten-sql.d.ts +1 -1
  8. package/dist/compiler/shorten-sql.js +3 -2
  9. package/dist/errors.d.ts +2 -0
  10. package/dist/errors.js +1 -1
  11. package/dist/executor/compatibility.js +1 -1
  12. package/dist/executor/components.js +1 -1
  13. package/dist/executor/execute-query.js +1 -1
  14. package/dist/executor/execute.js +1 -1
  15. package/dist/executor/fetch-entities.d.ts +2 -1
  16. package/dist/executor/fetch-entities.js +1 -1
  17. package/dist/executor/result-hydrator/entity-loader.js +1 -1
  18. package/dist/executor/result-hydrator/populate-entities.js +1 -1
  19. package/dist/executor/row-identity.d.ts +2 -1
  20. package/dist/executor/row-identity.js +1 -1
  21. package/dist/executor/types.d.ts +4 -0
  22. package/dist/normalizer/preds.js +1 -1
  23. package/dist/normalizer/query.js +1 -1
  24. package/dist/operators/builtins/average-per-period.js +1 -1
  25. package/dist/operators/builtins/period-difference.js +1 -1
  26. package/dist/operators/builtins/period-growth.js +1 -1
  27. package/dist/operators/builtins/sql.js +1 -1
  28. package/dist/physical/clickhouse-provider.d.ts +1 -0
  29. package/dist/physical/clickhouse-provider.js +1 -1
  30. package/dist/physical/mysql-compatible-sql.js +1 -1
  31. package/dist/physical/mysql-protocol-provider.d.ts +1 -0
  32. package/dist/physical/mysql-protocol-provider.js +1 -1
  33. package/dist/physical/oracle-provider.d.ts +1 -0
  34. package/dist/physical/oracle-provider.js +1 -1
  35. package/dist/physical/postgresql-provider.d.ts +1 -0
  36. package/dist/physical/postgresql-provider.js +1 -1
  37. package/dist/physical/provider.d.ts +1 -0
  38. package/dist/physical/snowflake-provider.d.ts +1 -0
  39. package/dist/physical/snowflake-provider.js +1 -1
  40. package/dist/planner/create-logical-plan.js +1 -1
  41. package/dist/planner/select-schema-mapping.js +1 -1
  42. package/dist/planner/types.d.ts +5 -0
  43. package/dist/plugins/hierarchy/operator.js +1 -1
  44. package/dist/schema/resolve-property-path.js +1 -1
  45. package/dist/semantic-types/builtins/entity.js +1 -1
  46. package/dist/semantic-types/resolve-schema.js +1 -1
  47. package/dist/semantic-types/types.d.ts +2 -1
  48. package/package.json +1 -1
package/README.md CHANGED
@@ -108,6 +108,8 @@ SQL string for diagnostics:
108
108
  ```ts
109
109
  interface ExecutedSql {
110
110
  type: 'semantic-resolution' | 'computation';
111
+ explain: string;
112
+ logicform: LogicformType;
111
113
  sql: string;
112
114
  parameters: readonly unknown[];
113
115
  displaySql: string;
@@ -2,6 +2,6 @@
2
2
  export declare const CALENDAR_DATES_TABLE = "semanticdb_calendar_dates";
3
3
  export declare const CALENDAR_RELATIONS_TABLE = "semanticdb_calendar_relations";
4
4
  export declare const CALENDAR_SYSTEM_SCHEMA_IDS: {
5
- readonly dates: "__semanticdb_calendar_dates";
6
- readonly relations: "__semanticdb_calendar_relations";
5
+ readonly dates: "__sdb_calendar_dates";
6
+ readonly relations: "__sdb_calendar_relations";
7
7
  };
@@ -1 +1 @@
1
- const _="semanticdb_calendar_dates",a="semanticdb_calendar_relations",e={dates:"__semanticdb_calendar_dates",relations:"__semanticdb_calendar_relations"};export{_ as CALENDAR_DATES_TABLE,a as CALENDAR_RELATIONS_TABLE,e as CALENDAR_SYSTEM_SCHEMA_IDS};
1
+ const _="semanticdb_calendar_dates",a="semanticdb_calendar_relations",e={dates:"__sdb_calendar_dates",relations:"__sdb_calendar_relations"};export{_ as CALENDAR_DATES_TABLE,a as CALENDAR_RELATIONS_TABLE,e as CALENDAR_SYSTEM_SCHEMA_IDS};
@@ -1 +1 @@
1
- import{SemanticDBError as m}from"../errors.js";const p=r=>!a(r)||!Array.isArray(r.properties)?[]:r.properties.filter(a),$=(r,n)=>r.id===n||r._id===n||r.name===n,h=(r,n,e,o=new Set)=>{if(r===n)return[];if(!o.has(r)){o.add(r);for(const t of p(e[r])){if(typeof t.ref!="string"||e[t.ref]===void 0)continue;const f=h(t.ref,n,e,o);if(f)return[String(t.name??t.id),...f]}o.delete(r)}},w=(r,n,e)=>{if(p(e[r]).some(d=>$(d,n)))return n;const t=n.split("_");for(let d=1;d<t.length;d+=1){const c=t.slice(d).join("_");if(p(e[r]).some(i=>$(i,c)))return c}const f=t.at(-1)??n;for(const[d,c]of Object.entries(e)){if(!p(c).some(u=>$(u,f)))continue;const i=h(r,d,e);if(i)return[...i,f].join("_")}},O=r=>Object.keys(r).filter(n=>n!=="$and"&&n!=="$or"),a=r=>typeof r=="object"&&r!==null&&!Array.isArray(r),b=r=>{if(!a(r))return r;if(a(r.$offset)){const n=Object.entries(r.$offset),e=n[0];if(n.length===1&&e&&Number.isInteger(e[1]))return{granularity:e[0],offset:e[1]}}return r},L=(r,n)=>{const o=r&&({$sply:"$periodShift",$splm:"$periodShift",$splw:"$periodShift",$yoy:"$periodGrowth",$mom:"$periodGrowth",$wow:"$periodGrowth",$yoyd:"$periodDifference",$momd:"$periodDifference",$dailyavg:"$averagePerPeriod",$weeklyavg:"$averagePerPeriod",$monthlyavg:"$averagePerPeriod",$startOfPeriod:"$periodBoundary",$endOfPeriod:"$periodBoundary",$dateLevel:"$dateBucket",$uniq:"$countDistinct"}[r]??r),t={...n};return r==="$sply"||r==="$yoy"||r==="$yoyd"?t.time??={granularity:"year",offset:-1}:r==="$splm"||r==="$mom"||r==="$momd"?t.time??={granularity:"month",offset:-1}:r==="$splw"||r==="$wow"?t.time??={granularity:"week",offset:-1}:r==="$dailyavg"?t.granularity??="day":r==="$weeklyavg"?t.granularity??="week":r==="$monthlyavg"?t.granularity??="month":r==="$startOfPeriod"?t.boundary??="start":r==="$endOfPeriod"&&(t.boundary??="end"),{...o?{operator:o}:{},args:t}},y=r=>{if(Array.isArray(r))return r.map(y);if(!a(r))return r;const n=r,e={};for(const[t,f]of Object.entries(n))t==="skip"||t==="_snapshot"||t==="_snapshots"||(t==="$offset"?e[t]=b(f):e[t]=y(f));if(typeof n.version=="string"&&/^1\./.test(n.version)&&(e.version="2.0"),Array.isArray(n.groupby)){const t=n.groupby;e.groupby=t.map(f=>{if(!a(f))return f;const{_id:d,level:c,...i}=f,u={...i,...i.pred===void 0&&typeof d=="string"?{pred:d}:{}};return typeof c=="string"&&(u.operator="$dateBucket",u.args={...a(u.args)?u.args:{},granularity:c}),u})}const o=n.skip;if(typeof o=="number"&&Number.isInteger(o)&&o>=0){const t=n.limit;o===0?delete e.skip:typeof t=="number"&&Number.isInteger(t)&&t>0&&o%t===0?e.page=o/t+1:e.skip=o}else o!==void 0&&(e.skip=y(o));if(n.operator&&typeof n.operator=="string"){const t=L(n.operator,a(e.args)?e.args:{});t.operator&&(e.operator=t.operator),e.args=t.args}return n.params!==void 0&&e.args===void 0&&(e.args=Array.isArray(n.params)?{values:y(n.params)}:y(n.params)),n.inputs!==void 0&&e.components===void 0&&Array.isArray(n.inputs)&&(e.components=n.inputs.map(y),a(e.args)&&delete e.args.inputs),n.pred===void 0&&typeof n.operator=="string"&&n.operator==="$sql"&&typeof n.sql=="string"&&(e.args={...a(e.args)?e.args:{},sql:n.sql},delete e.sql),n.$options!==void 0&&e.$regex!==void 0&&(e.args={...a(e.args)?e.args:{},options:n.$options},delete e.$options),n.query&&a(n.query)&&(e.query=k(n.query)),e},k=r=>{const n=y(r);for(const[e,o]of Object.entries(n))n[e]=b(o);return n},v=r=>{const n=a(r)?r:void 0,e=y(r);if(!a(e))return e;if(n&&typeof n.operator=="string"&&n.preds===void 0){const o={};for(const d of["version","schema","query","groupby","having","sort","limit","page","skip","output","representation","currency"])e[d]!==void 0&&(o[d]=e[d]);const t=new Set(["version","schema","query","groupby","having","sort","limit","page","skip","output","representation","currency"]),f=Object.fromEntries(Object.entries(e).filter(([d])=>!t.has(d)));return o.preds=[f],v(o)}return(e.version===void 0||typeof e.version=="string"&&/^1\./.test(e.version))&&(e.version="2.0"),e},_=(r,n)=>{const e=r;if(Array.isArray(e.logicforms)||!Array.isArray(e.preds))return r;const o=e.preds,t=typeof e.schema=="string"?e.schema:void 0;if(!t)return r;const f=new Set([t]);for(const i of o)a(i)&&typeof i.schema=="string"&&f.add(i.schema);if(f.size<2)return r;if(!n)throw new Error("Cannot migrate cross-Schema LogicForm without Schema metadata for field alignment");const d=[...f].map(i=>{const u={...e,schema:i,preds:[]};if(u.preds=o.filter(s=>a(s)?(s.schema??t)===i:i===t).map(s=>{if(!a(s))return s;const{schema:g,...l}=s;return l}),Array.isArray(e.groupby)&&(u.groupby=e.groupby.map(s=>{if(!a(s)||typeof s.pred!="string")return s;const g=w(i,s.pred,n);if(!g)throw new Error(`Cannot align legacy groupby field ${s.pred} in Schema ${i}`);return{...s,pred:g}})),a(e.query)){u.query={};for(const s of O(e.query)){if(!Object.entries(n).find(([,A])=>p(A).some(q=>$(q,s)))?.[0])throw new Error(`Cannot align legacy query field ${s} to a Schema`);const l=w(i,s,n);if(!l)throw new Error(`Cannot align legacy query field ${s} in Schema ${i}`);u.query[l]=e.query[s]}for(const s of["$and","$or"])e.query[s]!==void 0&&(u.query[s]=e.query[s])}return u}),c={version:e.version,logicforms:d};for(const i of["output","representation","currency","sort"])e[i]!==void 0&&(c[i]=e[i]);return c},S=r=>{const n=(e,o)=>{if(Array.isArray(e)){e.forEach((t,f)=>n(t,`${o}[${f}]`));return}if(a(e))for(const[t,f]of Object.entries(e)){if(["is_snapshot","is_observation","_snapshot","_snapshots","$options","params","inputs"].includes(t))throw new m("LEGACY_LOGICFORM_NOT_ALLOWED",`Legacy LogicForm field is not supported in v2: ${o}.${t}`);if(t==="_id"&&(o.endsWith(".groupby")||o.includes(".groupby[")))throw new m("LEGACY_LOGICFORM_NOT_ALLOWED",`Legacy LogicForm field is not supported in v2: ${o}.${t}`);n(f,`${o}.${t}`)}};n(r,"$")};export{S as assertNoLegacyLogicformFields,v as normalizeLegacyLogicform,_ as splitLegacySchemas};
1
+ import{SemanticDBError as m}from"../errors.js";const O=new Set(["$sply","$splm","$splq","$splw","$yoy","$mom","$qoq","$wow","$yoyd","$momd","$qoqd","$wowd"]),y=r=>!a(r)||!Array.isArray(r.properties)?[]:r.properties.filter(a),l=(r,o)=>r.id===o||r._id===o||r.name===o,w=(r,o,e,n=new Set)=>{if(r===o)return[];if(!n.has(r)){n.add(r);for(const t of y(e[r])){if(typeof t.ref!="string"||e[t.ref]===void 0)continue;const i=w(t.ref,o,e,n);if(i)return[String(t.name??t.id),...i]}n.delete(r)}},q=(r,o,e)=>{if(y(e[r]).some(d=>l(d,o)))return o;const t=o.split("_");for(let d=1;d<t.length;d+=1){const u=t.slice(d).join("_");if(y(e[r]).some(f=>l(f,u)))return u}const i=t.at(-1)??o;for(const[d,u]of Object.entries(e)){if(!y(u).some(c=>l(c,i)))continue;const f=w(r,d,e);if(f)return[...f,i].join("_")}},L=r=>Object.keys(r).filter(o=>o!=="$and"&&o!=="$or"),a=r=>typeof r=="object"&&r!==null&&!Array.isArray(r),h=r=>{if(!a(r))return r;if(a(r.$offset)){const o=Object.entries(r.$offset),e=o[0];if(o.length===1&&e&&Number.isInteger(e[1]))return{granularity:e[0],offset:e[1]}}return r},k=(r,o)=>{const n=r&&({$sply:"$periodShift",$splm:"$periodShift",$splq:"$periodShift",$splw:"$periodShift",$yoy:"$periodGrowth",$mom:"$periodGrowth",$qoq:"$periodGrowth",$wow:"$periodGrowth",$yoyd:"$periodDifference",$momd:"$periodDifference",$qoqd:"$periodDifference",$wowd:"$periodDifference",$dailyavg:"$averagePerPeriod",$weeklyavg:"$averagePerPeriod",$monthlyavg:"$averagePerPeriod",$startOfPeriod:"$periodBoundary",$endOfPeriod:"$periodBoundary",$dateLevel:"$dateBucket",$uniq:"$countDistinct"}[r]??r),t={...o};return r==="$sply"||r==="$yoy"||r==="$yoyd"?t.time??={granularity:"year",offset:-1}:r==="$splm"||r==="$mom"||r==="$momd"?t.time??={granularity:"month",offset:-1}:r==="$splq"||r==="$qoq"||r==="$qoqd"?t.time??={granularity:"quarter",offset:-1}:r==="$splw"||r==="$wow"||r==="$wowd"?t.time??={granularity:"week",offset:-1}:r==="$dailyavg"?t.granularity??="day":r==="$weeklyavg"?t.granularity??="week":r==="$monthlyavg"?t.granularity??="month":r==="$startOfPeriod"?t.boundary??="start":r==="$endOfPeriod"&&(t.boundary??="end"),{...n?{operator:n}:{},args:t}},p=r=>{if(Array.isArray(r))return r.map(p);if(!a(r))return r;const o=h(r);if(o!==r)return o;const e=r,n={};for(const[i,d]of Object.entries(e))i==="skip"||i==="_snapshot"||i==="_snapshots"||(n[i]=p(d));if(typeof e.version=="string"&&/^1\./.test(e.version)&&(n.version="2.0"),Array.isArray(e.groupby)){const i=e.groupby;n.groupby=i.map(d=>{if(!a(d))return d;const{_id:u,level:f,...c}=d,s={...c,...c.pred===void 0&&typeof u=="string"?{pred:u}:{}};return typeof f=="string"&&(s.operator="$dateBucket",s.args={...a(s.args)?s.args:{},granularity:f}),s})}const t=e.skip;if(typeof t=="number"&&Number.isInteger(t)&&t>=0){const i=e.limit;t===0?delete n.skip:typeof i=="number"&&Number.isInteger(i)&&i>0&&t%i===0?n.page=t/i+1:n.skip=t}else t!==void 0&&(n.skip=p(t));if(e.operator&&typeof e.operator=="string"){const i=k(e.operator,a(n.args)?n.args:{});i.operator&&(n.operator=i.operator),n.args=i.args,O.has(e.operator)&&typeof n.pred=="string"&&(n.pred={operator:"$sum",pred:n.pred})}return e.params!==void 0&&n.args===void 0&&(n.args=Array.isArray(e.params)?{values:p(e.params)}:p(e.params)),e.inputs!==void 0&&n.components===void 0&&Array.isArray(e.inputs)&&(n.components=e.inputs.map(p),a(n.args)&&delete n.args.inputs),e.pred===void 0&&typeof e.operator=="string"&&e.operator==="$sql"&&typeof e.sql=="string"&&(n.args={...a(n.args)?n.args:{},sql:e.sql},delete n.sql),e.$options!==void 0&&n.$regex!==void 0&&(n.args={...a(n.args)?n.args:{},options:e.$options},delete n.$options),e.query&&a(e.query)&&(n.query=S(e.query)),n},S=r=>{const o=p(r);for(const[e,n]of Object.entries(o))o[e]=h(n);return o},b=r=>{const o=a(r)?r:void 0,e=p(r);if(!a(e))return e;if(o&&typeof o.operator=="string"&&o.preds===void 0){const n={};for(const d of["version","schema","query","groupby","having","sort","limit","page","skip","output","representation","currency"])e[d]!==void 0&&(n[d]=e[d]);const t=new Set(["version","schema","query","groupby","having","sort","limit","page","skip","output","representation","currency"]),i=Object.fromEntries(Object.entries(e).filter(([d])=>!t.has(d)));return n.preds=[i],b(n)}return(e.version===void 0||typeof e.version=="string"&&/^1\./.test(e.version))&&(e.version="2.0"),e},_=(r,o)=>{const e=r;if(Array.isArray(e.logicforms)||!Array.isArray(e.preds))return r;const n=e.preds,t=typeof e.schema=="string"?e.schema:void 0;if(!t)return r;const i=new Set([t]);for(const f of n)a(f)&&typeof f.schema=="string"&&i.add(f.schema);if(i.size<2)return r;if(!o)throw new Error("Cannot migrate cross-Schema LogicForm without Schema metadata for field alignment");const d=[...i].map(f=>{const c={...e,schema:f,preds:[]};if(c.preds=n.filter(s=>a(s)?(s.schema??t)===f:f===t).map(s=>{if(!a(s))return s;const{schema:$,...g}=s;return g}),Array.isArray(e.groupby)&&(c.groupby=e.groupby.map(s=>{if(!a(s)||typeof s.pred!="string")return s;const $=q(f,s.pred,o);if(!$)throw new Error(`Cannot align legacy groupby field ${s.pred} in Schema ${f}`);return{...s,pred:$}})),a(e.query)){c.query={};for(const s of L(e.query)){if(!Object.entries(o).find(([,v])=>y(v).some(A=>l(A,s)))?.[0])throw new Error(`Cannot align legacy query field ${s} to a Schema`);const g=q(f,s,o);if(!g)throw new Error(`Cannot align legacy query field ${s} in Schema ${f}`);c.query[g]=e.query[s]}for(const s of["$and","$or"])e.query[s]!==void 0&&(c.query[s]=e.query[s])}return c}),u={version:e.version,logicforms:d};for(const f of["output","representation","currency","sort"])e[f]!==void 0&&(u[f]=e[f]);return u},j=r=>{const o=(e,n)=>{if(Array.isArray(e)){e.forEach((t,i)=>o(t,`${n}[${i}]`));return}if(a(e))for(const[t,i]of Object.entries(e)){if(["is_snapshot","is_observation","_snapshot","_snapshots","$options","params","inputs"].includes(t))throw new m("LEGACY_LOGICFORM_NOT_ALLOWED",`Legacy LogicForm field is not supported in v2: ${n}.${t}`);if(t==="_id"&&(n.endsWith(".groupby")||n.includes(".groupby[")))throw new m("LEGACY_LOGICFORM_NOT_ALLOWED",`Legacy LogicForm field is not supported in v2: ${n}.${t}`);o(i,`${n}.${t}`)}};o(r,"$")};export{j as assertNoLegacyLogicformFields,b as normalizeLegacyLogicform,_ as splitLegacySchemas};
@@ -1 +1 @@
1
- import{compilePlan as d}from"./compile-plan.js";const w=o=>`__semanticdb_cte_${o}`,h=o=>{const i=o.projections[o.groupBy.length];if(!i)throw new Error("CTE component must produce one metric projection");return i.name},y=(o,i)=>o?.find(c=>c.name===i),T=(o,i,c,e,g)=>{const s=[];let l=0;for(const t of i){const n=d(t.plan,e,g,l);l+=n.parameters.length,s.push({input:t,sql:n,alias:w(t.name)})}const j=s.map(({sql:t,alias:n})=>`${e.quoteIdentifier(n)} AS (${t.sql})`).join(", "),m=s[0];if(!m)throw new Error("CTE calculation requires a current component");const a=e.quoteIdentifier(m.alias),p=c.groupBy.map(({name:t})=>t),E=p.map(t=>`${a}.${e.quoteIdentifier(t)} AS ${e.quoteIdentifier(t)}`);let $="";for(const t of s.slice(1)){const n=e.quoteIdentifier(t.alias),u=(t.input.joinKeys??p).map(r=>{const f=y(t.input.groupKeyShifts,r),N=f?e.dateShift(`${n}.${e.quoteIdentifier(r)}`,f.granularity,f.offset):`${n}.${e.quoteIdentifier(r)}`;return`${a}.${e.quoteIdentifier(r)} = ${N}`});$+=` LEFT JOIN ${n} ON ${u.length>0?u.join(" AND "):"1 = 1"}`}const A=Object.fromEntries(s.map(({input:t,alias:n})=>[t.name,`${e.quoteIdentifier(n)}.${e.quoteIdentifier(h(t.plan))}`])),S=o.toSQL({values:A,quoteIdentifier:t=>e.quoteIdentifier(t)}),q=o.outputName??c.projections[c.groupBy.length]?.name;if(!q)throw new Error("CTE output projection is missing");const C=[...E,...o.exposeComponents?s.map(({input:t,alias:n})=>`${`${e.quoteIdentifier(n)}.${e.quoteIdentifier(h(t.plan))}`} AS ${e.quoteIdentifier(t.name)}`):[],`${S} AS ${e.quoteIdentifier(q)}`].join(", ");let I=`WITH ${j} SELECT ${C} FROM ${a}`;return I+=$,{sql:I,parameters:s.flatMap(({sql:t})=>t.parameters)}};export{T as compileCteCalculation};
1
+ import{compilePlan as y}from"./compile-plan.js";const C=o=>`__sdb_cte_${o}`,g=o=>{const i=o.projections[o.groupBy.length];if(!i)throw new Error("CTE component must produce one metric projection");return i.name},N=(o,i)=>o?.find(r=>r.name===i),T=(o,i,r,e,h)=>{const s=[];let l=0;for(const t of i){const n=y(t.plan,e,h,l);l+=n.parameters.length,s.push({input:t,sql:n,alias:C(t.name)})}const j=s.map(({sql:t,alias:n})=>`${e.quoteIdentifier(n)} AS (${t.sql})`).join(", "),m=s[0];if(!m)throw new Error("CTE calculation requires a current component");const u=e.quoteIdentifier(m.alias),$=r.groupBy.map(({name:t})=>t),d=$.map(t=>`${u}.${e.quoteIdentifier(t)} AS ${e.quoteIdentifier(t)}`);let p="";for(const t of s.slice(1)){const n=e.quoteIdentifier(t.alias),a=(t.input.joinKeys??$).map(c=>{const f=N(t.input.groupKeyShifts,c),w=f?e.dateShift(`${n}.${e.quoteIdentifier(c)}`,f.granularity,f.offset):`${n}.${e.quoteIdentifier(c)}`;return`${u}.${e.quoteIdentifier(c)} = ${w}`});p+=` LEFT JOIN ${n} ON ${a.length>0?a.join(" AND "):"1 = 1"}`}const A=Object.fromEntries(s.map(({input:t,alias:n})=>[t.name,`${e.quoteIdentifier(n)}.${e.quoteIdentifier(g(t.plan))}`])),E=o.toSQL({values:A,quoteIdentifier:t=>e.quoteIdentifier(t)}),q=o.outputName??r.projections[r.groupBy.length]?.name;if(!q)throw new Error("CTE output projection is missing");const S=[...d,...r.rowIdentity.kind==="group"?[`${u}.${e.quoteIdentifier("_id")} AS ${e.quoteIdentifier("_id")}`]:[],...o.exposeComponents?s.map(({input:t,alias:n})=>`${`${e.quoteIdentifier(n)}.${e.quoteIdentifier(g(t.plan))}`} AS ${e.quoteIdentifier(t.name)}`):[],`${E} AS ${e.quoteIdentifier(q)}`].join(", ");let I=`WITH ${j} SELECT ${S} FROM ${u}`;return I+=p,{sql:I,parameters:s.flatMap(({sql:t})=>t.parameters)}};export{T as compileCteCalculation};
@@ -1 +1 @@
1
- import{SemanticDBError as N}from"../errors.js";import{createOperatorRegistry as X}from"../operators/index.js";import{isLogicalOrdinalQuery as z,isLogicalRecursiveQuery as K,isLogicalQuerySubquery as B}from"../planner/types.js";import{bindTrustedSqlExpression as Z}from"../operators/sql-expression.js";class x{provider;parameterOffset;values=[];constructor(e,n=0){this.provider=e,this.parameterOffset=n}add(e){const n=this.parameterOffset+this.values.length;return this.values.push(e),this.provider.parameter(n,e)}get nextParameterOffset(){return this.parameterOffset+this.values.length}append(e){this.values.push(...e)}}const v=(t,e)=>{const n=e.split(".");if(n.some(r=>r.length===0))throw new N("INVALID_SOURCE",`Invalid physical source: ${e}`);return n.map(r=>t.quoteIdentifier(r)).join(".")},C=(t,e,n)=>n===void 0?v(t,e):`(${n.trim().replace(/;+\s*$/,"")})`,tt=t=>/^(select|with)\b/i.test(t.trim()),P=(t,e,n,r,i)=>{const a=Z(t,[],String,`Property ${e} udf.sql`);if(!a.includes("$TS"))return a;if(!r)throw new N("PROPERTY_UDF_TIMESTAMP_REQUIRED",`Property ${e} udf.sql uses $TS but its Schema has no timestamp Property`,{property:e});const $=(i?`${n.quoteIdentifier(i)}.`:"")+n.quoteIdentifier(r);return a.replace(/\$TS/g,$)},y=(t,e,n,r=!1,i,a,$,o)=>{const u=(c,m)=>r&&m?`${e.quoteIdentifier(m)}.${e.quoteIdentifier(c)}`:e.quoteIdentifier(c);switch(t.kind){case"array-element":if(!o)throw new N("ARRAY_EXPANSION_UNAVAILABLE","Array expansion compiler is unavailable");return o(t.expansionPath);case"column":return u(t.column,t.qualifier);case"property-sql":return P(t.sql,t.property,e,t.timestampColumn,r?t.qualifier:void 0);case"window":{if(l(t.input,n))throw new N("UNSUPPORTED_MIXED_OPERATOR","Window operators cannot consume a JavaScript operator");const c=t.partitionBy.length>0?`PARTITION BY ${t.partitionBy.map(f=>y(f,e,n,r,i,a,$,o)).join(", ")} `:"",m=y(t.input,e,n,r,i,a,$,o);return`${t.function==="rank"?"RANK":"ROW_NUMBER"}() OVER (${c}ORDER BY ${m} ${t.direction})`}case"cumulative":{if(l(t.input,n))throw new N("UNSUPPORTED_MIXED_OPERATOR","Cumulative expressions cannot consume a JavaScript operator");const c=y(t.input,e,n,r,i,a,$,o),m=t.partitionBy.length>0?`PARTITION BY ${t.partitionBy.map(f=>y(f,e,n,r,i,a,$,o)).join(", ")} `:"",A=t.orderBy.map(f=>`${y(f.expression,e,n,r,i,a,$,o)} ${f.direction}`).join(", ");return`${e.aggregateFunction(t.function,c)} OVER (${m}ORDER BY ${A} ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)`}case"ordinal-encode":{if(!a)throw new N("SQL_BINDER_UNAVAILABLE","Ordinal encoding requires a value binder");return`CASE ${t.comparable.values.map((m,A)=>`WHEN ${y(t.input,e,n,r,i,a,$,o)} = ${a(m)} THEN ${t.comparable.direction*A}`).join(" ")} END`}case"ordinal-decode":{if(!a)throw new N("SQL_BINDER_UNAVAILABLE","Ordinal decoding requires a value binder");return`CASE ${t.comparable.values.map((m,A)=>`WHEN ${y(t.input,e,n,r,i,a,$,o)} = ${t.comparable.direction*A} THEN ${a(m)}`).join(" ")} END`}case"operator":{const c=n.get(t.operator);if(c.kind==="component")throw new N("COMPONENT_OPERATOR_REQUIRES_EXECUTOR",`${t.operator} must be compiled through the executor component pipeline`);if(c.toSQL&&(t.input&&l(t.input,n)||t.components?.some(f=>l(f,n))))throw new N("UNSUPPORTED_MIXED_OPERATOR",`${t.operator} cannot consume a JavaScript operator in SQL`);let m=t.input?y(t.input,e,n,r,i,a,$,o):t.column?u(t.column,t.qualifier):void 0;const A=t.components?.map(f=>y(f,e,n,r,i,a,$,o));if(t.query){if(c.kind!=="aggregate")throw new N("OPERATOR_QUERY_REQUIRES_AGGREGATION",`Pred query is only supported by aggregation operators: ${t.operator}`);if(!i)throw new N("INVALID_OPERATOR_QUERY","Operator query compiler is unavailable");m=`CASE WHEN ${i(t.query)} THEN ${m??"1"} END`}if(c.requiresProperty!==!1&&!m)throw new N("PRED_PROPERTY_REQUIRED",`${t.operator} requires a column`);return c.toSQL?c.toSQL({sql:{aggregateFunction:e.aggregateFunction.bind(e),quoteIdentifier:e.quoteIdentifier.bind(e),concat:e.concat.bind(e),substring:e.substring.bind(e),dateBucket:e.dateBucket.bind(e),value:f=>{if(!a)throw new N("SQL_BINDER_UNAVAILABLE","SQL value binder is unavailable");return a(f)},condition:(f,_)=>{if(!$)throw new N("SQL_CONDITION_UNAVAILABLE","SQL condition compiler is unavailable");return $(f,_)}},...m?{expression:m,column:m}:{},...A?{expressions:A}:{},args:t.args}):m??"NULL"}}},l=(t,e)=>t.kind==="window"?l(t.input,e)||t.partitionBy.some(n=>l(n,e)):t.kind==="cumulative"?l(t.input,e)||t.partitionBy.some(n=>l(n,e))||t.orderBy.some(n=>l(n.expression,e)):t.kind==="ordinal-encode"||t.kind==="ordinal-decode"?l(t.input,e):t.kind==="operator"&&(!e.get(t.operator).toSQL&&!!e.get(t.operator).evaluate||(t.input?l(t.input,e):!1)||(t.components?.some(n=>l(n,e))??!1)),et=(t,e)=>t.sourceSql?P(t.sourceSql,t.sourceColumn,e,t.sourceTimestampColumn,t.sourceAlias):`${e.quoteIdentifier(t.sourceAlias)}.${e.quoteIdentifier(t.sourceColumn)}`,H=t=>typeof t=="object"&&t!==null&&!Array.isArray(t)&&Object.keys(t).some(e=>e.startsWith("$")),nt=(t,e,n)=>{if(!n.supportsRecursiveCte)throw new N("RECURSIVE_QUERY_UNSUPPORTED",`Provider ${n.kind} does not support recursive queries`);if(t.roots.length===0)return"(SELECT NULL WHERE 1 = 0)";const r=n.quoteIdentifier("__recursive_tree__"),i=n.quoteIdentifier("__recursive_id__"),a=n.quoteIdentifier("__recursive_node__"),$=n.quoteIdentifier("__recursive_child__"),o=n.quoteIdentifier(t.idColumn),u=n.quoteIdentifier(t.parentColumn),c=C(n,t.source,t.sourceSql),m=()=>t.roots.map(w=>e.add(w)).join(", "),A=(w,T=!1)=>t.roots.length===1?`${w} ${T?"<>":"="} ${m()}`:`${w} ${T?"NOT IN":"IN"} (${m()})`,f=`${a}.${o}`,_=t.includeSelf?A(f):`${A(`${a}.${u}`)} AND ${A(f,!0)}`;return`(WITH RECURSIVE ${r}(${i}) AS ( SELECT ${f} FROM ${n.tableAlias(c,"__recursive_node__")} WHERE ${_} UNION SELECT ${$}.${o} FROM ${n.tableAlias(c,"__recursive_child__")} JOIN ${r} ON ${$}.${u} = ${r}.${i}) SELECT DISTINCT ${i} FROM ${r})`},D=(t,e,n,r,i,a,$)=>{if(e==="$recursive"){if(!K(n))throw new N("INVALID_RECURSIVE_QUERY","$recursive query was not planned");return`${t} IN ${nt(n,r,i)}`}if(e==="$eq"&&n===null)return`${t} IS NULL`;if(e==="$ne"&&n===null)return`${t} IS NOT NULL`;const u={$eq:"=",$ne:"<>",$gt:">",$gte:">=",$lt:"<",$lte:"<="}[e];if(u){const c=B(n)?`(${a(n)})`:r.add(n);return`${t} ${u} ${c}`}if(e==="$in"||e==="$nin"){const c=e==="$in"?"IN":"NOT IN";if(B(n))return`${t} ${c} (${a(n)})`;if(!Array.isArray(n))throw new N("INVALID_QUERY_VALUE",`${e} requires an array or query LogicForm`);if(n.length===0)return e==="$in"?"0 = 1":"1 = 1";const m=n.filter(B);if(m.length>0){if(m.length!==n.length)throw new N("MIXED_SUBQUERY_VALUES",`${e} cannot mix query LogicForms and literal values`);return`${t} ${c} (${m.map(a).join(" UNION ALL ")})`}const A=n.map(f=>r.add(f)).join(", ");return`${t} ${c} (${A})`}if(B(n))throw new N("UNSUPPORTED_SUBQUERY_OPERATOR",`Query LogicForm is not supported by ${e}`);if(e==="$exists")return`${t} IS ${n?"NOT ":""}NULL`;if(e==="$contains")return`${t} LIKE ${r.add(`%${String(n)}%`)}`;if(e==="$regex"){if(typeof n!="string"||$!==void 0&&typeof $!="string")throw new N("INVALID_QUERY_VALUE","$regex requires a string pattern and args.options must be a string");return i.regex(t,r.add(n),$)}throw new N("UNSUPPORTED_QUERY_OPERATOR",`Unsupported query operator: ${e}`)},b=(t,e,n,r,i)=>{if(z(e)){const u=()=>`CASE ${e.comparable.values.map((f,_)=>`WHEN ${t} = ${n.add(f)} THEN ${e.comparable.direction*_}`).join(" ")} END`,c=new Set(["$gt","$gte","$lt","$lte"]),m=A=>typeof A!="object"||A===null||Array.isArray(A)?b(t,A,n,r,i):Object.entries(A).filter(([f])=>f!=="args").map(([f,_])=>(f==="$and"||f==="$or")&&Array.isArray(_)?`(${_.map(m).join(f==="$and"?" AND ":" OR ")})`:c.has(f)?D(u(),f,e.comparable.direction*e.comparable.values.indexOf(String(_)),n,r,i):D(t,f,_,n,r,i)).join(" AND ");return m(e.condition)}if(Array.isArray(e))return D(t,"$in",e,n,r,i);if(!H(e))return D(t,"$eq",e,n,r,i);const a=[],$=e.args;if($!==void 0){if(typeof $!="object"||$===null||Array.isArray($))throw new N("INVALID_QUERY_VALUE","Query args.options must be a string");const u=$.options;if(u!==void 0&&typeof u!="string")throw new N("INVALID_QUERY_VALUE","Query args.options must be a string")}const o=$?.options;for(const[u,c]of Object.entries(e))if(u==="$and"||u==="$or"){if(!Array.isArray(c))throw new N("INVALID_QUERY_VALUE",`${u} requires an array`);const m=c.map(A=>b(t,A,n,r,i));a.push(`(${m.join(u==="$and"?" AND ":" OR ")})`)}else u!=="args"&&a.push(D(t,u,c,n,r,i,u==="$regex"?o:void 0));if(a.length===0)throw new N("EMPTY_QUERY_CONDITION","Query condition contains no operators");return a.join(" AND ")},W=(t,e,n,r)=>{if(!r.arrayContains)throw new N("ARRAY_CONDITION_UNSUPPORTED",`Provider ${r.kind} does not support array conditions`);const i=r.arrayContains.bind(r),a=o=>i(t,n.add(o));if(Array.isArray(e))return`(${e.map(a).join(" OR ")})`;if(!H(e))return a(e);const $=[];for(const[o,u]of Object.entries(e))if(o==="$and"||o==="$or"){if(!Array.isArray(u))throw new N("INVALID_QUERY_VALUE",`${o} requires an array`);const c=u.map(m=>W(t,m,n,r));$.push(`(${c.join(o==="$and"?" AND ":" OR ")})`)}else if(o==="$eq")$.push(a(u));else if(o==="$ne")$.push(`NOT (${a(u)})`);else if(o==="$in"||o==="$nin"){if(!Array.isArray(u))throw new N("INVALID_QUERY_VALUE",`${o} requires an array`);const c=u.map(a),m=c.length===0?o==="$in"?"0 = 1":"1 = 1":c.join(o==="$in"?" OR ":" AND NOT ");$.push(o==="$nin"&&c.length>0?`NOT (${c.join(" OR ")})`:`(${m})`)}else if(o==="$exists")$.push(`${t} IS ${u?"NOT ":""}NULL`);else if(o!=="args")throw new N("UNSUPPORTED_ARRAY_QUERY_OPERATOR",`Unsupported array query operator: ${o}`);if($.length===0)throw new N("EMPTY_QUERY_CONDITION","Query condition contains no operators");return $.join(" AND ")},Q=(t,e,n,r,i,a)=>{const $=[];for(const[o,u]of Object.entries(t)){if(o==="$and"||o==="$or"){if(!Array.isArray(u))throw new N("INVALID_QUERY_VALUE",`${o} requires an array`);const m=u.map(A=>{if(typeof A!="object"||A===null||Array.isArray(A))throw new N("INVALID_QUERY_VALUE",`${o} items must be query objects`);return Q(A,e,n,r,i,a)});$.push(`(${m.join(o==="$and"?" AND ":" OR ")})`);continue}const c=e(o);$.push(n(o)?W(c,u,r,i):b(c,u,r,i,a))}return $.join(" AND ")},Y=(t,e,n,r,i)=>t.orderBy.map(a=>{const $=e.quoteIdentifier(a.name),o=`${r?`${r}.`:""}${$}`,u=a.comparable,c=!r&&i&&a.expression?i(a.expression):o;return`${u?`CASE ${u.values.map((A,f)=>`WHEN ${c} = ${n.add(A)} THEN ${u.direction*f}`).join(" ")} END`:o} ${a.direction}`}).join(", "),M=(t,e)=>{let n=t;for(;e.has(n);)n=`_${n}`;return n},st=(t,e,n,r,i)=>{const a=new Set(e.projections.map(({name:h})=>h)),$="__semanticdb_page_source",o="__semanticdb_page",u=r.quoteIdentifier($),c=r.quoteIdentifier(o),m=M("__semanticdb_row_number",a),A=M("__semanticdb_total_rows",a),f=r.quoteIdentifier(m),_=r.quoteIdentifier(A),w=Y(e,r,i,u),T=w?`ORDER BY ${w}`:"",d=`CEIL(${_} * ${n.ratio})`,U=n.page-1,O=n.page,s=[...e.projections.map(({name:h})=>h),...e.rowIdentity.kind==="property"?[e.rowIdentity.internalName]:[]].map(h=>`${c}.${r.quoteIdentifier(h)}`).join(", "),E=Y(e,r,i,c),I=`SELECT ${u}.*, ROW_NUMBER() OVER (${T}) AS ${f}, COUNT(*) OVER () AS ${_} FROM ${r.tableAlias(`(${t})`,$)}`;let S=`SELECT ${s} FROM ${r.tableAlias(`(${I})`,o)} WHERE ${c}.${f} > ${U} * ${d} AND ${c}.${f} <= ${O} * ${d}`;return E&&(S+=` ORDER BY ${E}`),S},V=(t,e,n,r)=>{const i=e.groupLimit;if(!i)return t;const a="__semanticdb_limit_by_source",$="__semanticdb_limit_by_ranked",o=n.quoteIdentifier(a),u=n.quoteIdentifier($),c=new Set(e.projections.map(({name:O})=>O)),m=M("__semanticdb_limit_by_row_number",c),A=n.quoteIdentifier(m),f=i.partitionBy.map(O=>`${o}.${n.quoteIdentifier(O)}`).join(", "),_=i.orderBy.map(({name:O,direction:R,comparable:s})=>{const E=`${o}.${n.quoteIdentifier(O)}`;return`${s?`CASE ${s.values.map((S,h)=>`WHEN ${E} = ${r.add(S)} THEN ${s.direction*h}`).join(" ")} END`:E} ${R}`}).join(", "),T=[...e.projections.map(({name:O})=>O),...e.rowIdentity.kind==="property"?[e.rowIdentity.internalName]:[]].map(O=>`${u}.${n.quoteIdentifier(O)}`).join(", "),d=`PARTITION BY ${f} ORDER BY ${_}`,U=`SELECT ${o}.*, ROW_NUMBER() OVER (${d}) AS ${A} FROM ${n.tableAlias(`(${t})`,a)}`;return`SELECT ${T} FROM ${n.tableAlias(`(${U})`,$)} WHERE ${u}.${A} <= ${i.count}`},k=(t,e,n=X(),r=0)=>{const i=new x(e,r);if(t.composition){const s=t.projections.map(({name:g})=>g),E=t.groupBy.map(({name:g})=>g),I=t.composition.map((g,j)=>{const F=k(g,e,n,i.nextParameterOffset);i.append(F.parameters);const p=`__composition_child_${j}`,G=e.quoteIdentifier(p),J=new Set(g.projections.map(({name:L})=>L));return`SELECT ${s.map(L=>J.has(L)?`${G}.${e.quoteIdentifier(L)} AS ${e.quoteIdentifier(L)}`:`NULL AS ${e.quoteIdentifier(L)}`).join(", ")} FROM ${e.tableAlias(`(${F.sql})`,p)}`}),S="__composition_union",h=e.quoteIdentifier(S);let q=`SELECT ${s.map(g=>{const j=`${h}.${e.quoteIdentifier(g)}`;return E.includes(g)?`${j} AS ${e.quoteIdentifier(g)}`:`MAX(${j}) AS ${e.quoteIdentifier(g)}`}).join(", ")} FROM ${e.tableAlias(`(${I.join(" UNION ALL ")})`,S)}`;return E.length>0&&(q+=` GROUP BY ${E.map(g=>`${h}.${e.quoteIdentifier(g)}`).join(", ")}`),t.groupLimit&&(q=V(q,t,e,i)),t.orderBy.length>0&&(q+=` ORDER BY ${Y(t,e,i)}`),t.pagination?.kind==="rows"&&(q=e.paginate(q,t.pagination.pageSize,0)),{sql:q,parameters:i.values}}const a=s=>{if(s.plan.projections.some(({expression:I})=>l(I,n)))throw new N("JAVASCRIPT_OPERATOR_IN_SUBQUERY","A query LogicForm cannot use a JavaScript-only operator");const E=k(s.plan.rowIdentity.kind==="property"?{...s.plan,rowIdentity:{kind:"generated"}}:s.plan,e,n,i.nextParameterOffset);return i.append(E.parameters),E.sql},$=(t.joins?.length??0)>0,o=(t.arrayExpansions?.length??0)>0,u=$||o||t.sourcePlan!==void 0||t.sourceSql!==void 0,c=new Map,m=(t.arrayExpansions??[]).map(s=>{if(!e.expandArray)throw new N("ARRAY_EXPANSION_UNSUPPORTED",`Provider ${e.kind} does not support array expansion`);const E=e.expandArray(et(s,e),s.alias,"value");return c.set(s.path,E.valueExpression),E}),A=s=>{const E=t.fieldReferences?.[s];if(E?.sql)return P(E.sql,E.property,e,E.timestampColumn,u?E.qualifier:void 0);if(E&&u)return`${e.quoteIdentifier(E.qualifier)}.${e.quoteIdentifier(E.column)}`;const I=t.fields[s];if(!I)throw new N("PROPERTY_NOT_FOUND",`Query property not found: ${s}`);return e.quoteIdentifier(I)},f=s=>y(s,e,n,u,E=>Q(E,A,I=>t.fieldReferences?.[I]?.isArray===!0,i,e,a),E=>i.add(E),(E,I)=>b(E,I,i,e,a),E=>{const I=c.get(E);if(!I)throw new N("ARRAY_EXPANSION_NOT_FOUND",`Array expansion not found: ${E}`);return I}),_=t.projections.map(s=>`${f(s.expression)} AS ${e.quoteIdentifier(s.name)}`),w=t.rowIdentity.kind==="property"?`${u&&t.sourceAlias?`${e.quoteIdentifier(t.sourceAlias)}.`:""}${e.quoteIdentifier(t.rowIdentity.column)} AS ${e.quoteIdentifier(t.rowIdentity.internalName)}`:void 0,T=[..._,...w?[w]:[]].join(", "),d=!t.sourcePlan&&t.sourceSql!==void 0&&tt(t.sourceSql)?{name:e.quoteIdentifier(t.schemaId),sql:t.sourceSql.trim().replace(/;+\s*$/,"")}:void 0,U=t.sourcePlan?(()=>{const s=k(t.sourcePlan,e,n,i.nextParameterOffset);return i.append(s.parameters),`(${s.sql})`})():d?d.name:C(e,t.source,t.sourceSql),O=u&&t.sourceAlias?e.tableAlias(U,t.sourceAlias):U;let R=`SELECT ${T} FROM ${O}`;for(const s of m)R+=` ${s.joinSql}`;for(const s of t.joins??[]){R+=` LEFT JOIN ${e.tableAlias(C(e,s.targetSource,s.targetSourceSql),s.targetAlias)}`;const E=s.sourceArrayExpansionPath?c.get(s.sourceArrayExpansionPath):s.sourceSql?P(s.sourceSql,s.sourceColumn,e,s.sourceTimestampColumn,s.sourceAlias):`${e.quoteIdentifier(s.sourceAlias)}.${e.quoteIdentifier(s.sourceColumn)}`,I=s.targetSql?P(s.targetSql,s.targetColumn,e,s.targetTimestampColumn,s.targetAlias):`${e.quoteIdentifier(s.targetAlias)}.${e.quoteIdentifier(s.targetColumn)}`;if(!E)throw new N("ARRAY_EXPANSION_NOT_FOUND",`Array expansion not found: ${s.path}`);if(R+=` ON ${E} = ${I}`,s.sourceTimestampColumn&&s.targetTimestampColumn&&s.sourceTimestampGranularity&&s.targetTimestampGranularity){const S=`${e.quoteIdentifier(s.sourceAlias)}.${e.quoteIdentifier(s.sourceTimestampColumn)}`,h=`${e.quoteIdentifier(s.targetAlias)}.${e.quoteIdentifier(s.targetTimestampColumn)}`;R+=` AND ${e.dateBucket(S,s.sourceTimestampGranularity)} = ${e.dateBucket(h,s.targetTimestampGranularity)}`}}if(t.query&&Object.keys(t.query).length>0&&(R+=` WHERE ${Q(t.query,A,s=>t.fieldReferences?.[s]?.isArray===!0,i,e,a)}`),t.groupBy.length>0&&(R+=` GROUP BY ${t.groupBy.map(s=>f(s.expression)).join(", ")}`),t.having&&Object.keys(t.having).length>0){const s=E=>{const I=t.projections.find(S=>S.name===E);if(!I)throw new N("INVALID_HAVING",`Having field is not an output: ${E}`);return e.supportsHavingAlias===!1?f(I.expression):e.quoteIdentifier(E)};R+=` HAVING ${Q(t.having,s,()=>!1,i,e,a)}`}if(t.groupLimit&&(R=V(R,t,e,i)),t.pagination?.kind==="ratio")R=st(R,t,t.pagination,e,i);else if(t.orderBy.length>0&&(R+=` ORDER BY ${Y(t,e,i,void 0,f)}`),t.pagination?.kind==="rows"){const s=(t.pagination.page-1)*t.pagination.pageSize;R=e.paginate(R,t.pagination.pageSize,s)}return d&&(R=`WITH ${d.name} AS (${d.sql}) ${R}`),{sql:R,parameters:i.values}};export{k as compilePlan};
1
+ import{SemanticDBError as A}from"../errors.js";import{createOperatorRegistry as K}from"../operators/index.js";import{isLogicalOrdinalQuery as Z,isLogicalRecursiveQuery as x,isLogicalQuerySubquery as b}from"../planner/types.js";import{bindTrustedSqlExpression as v}from"../operators/sql-expression.js";class tt{provider;parameterOffset;values=[];constructor(e,n=0){this.provider=e,this.parameterOffset=n}add(e){const n=this.parameterOffset+this.values.length;return this.values.push(e),this.provider.parameter(n,e)}get nextParameterOffset(){return this.parameterOffset+this.values.length}append(e){this.values.push(...e)}}const et=(t,e)=>{const n=e.split(".");if(n.some(o=>o.length===0))throw new A("INVALID_SOURCE",`Invalid physical source: ${e}`);return n.map(o=>t.quoteIdentifier(o)).join(".")},M=(t,e,n)=>n===void 0?et(t,e):`(${n.trim().replace(/;+\s*$/,"")})`,D=(t,e,n,o,i)=>{const r=v(t,[],String,`Property ${e} udf.sql`);if(!r.includes("$TS"))return r;if(!o)throw new A("PROPERTY_UDF_TIMESTAMP_REQUIRED",`Property ${e} udf.sql uses $TS but its evaluation context has no timestamp Property`,{property:e});const $=(i?`${n.quoteIdentifier(i)}.`:"")+n.quoteIdentifier(o);return r.replace(/\$TS/g,$)},l=(t,e,n,o=!1,i,r,$,a)=>{const u=(c,I)=>o&&I?`${e.quoteIdentifier(I)}.${e.quoteIdentifier(c)}`:e.quoteIdentifier(c);switch(t.kind){case"array-element":if(!a)throw new A("ARRAY_EXPANSION_UNAVAILABLE","Array expansion compiler is unavailable");return a(t.expansionPath);case"column":return u(t.column,t.qualifier);case"property-sql":return D(t.sql,t.property,e,t.timestampColumn,o?t.timestampQualifier??t.qualifier:void 0);case"window":{if(w(t.input,n))throw new A("UNSUPPORTED_MIXED_OPERATOR","Window operators cannot consume a JavaScript operator");const c=t.partitionBy.length>0?`PARTITION BY ${t.partitionBy.map(E=>l(E,e,n,o,i,r,$,a)).join(", ")} `:"",I=l(t.input,e,n,o,i,r,$,a);return`${t.function==="rank"?"RANK":"ROW_NUMBER"}() OVER (${c}ORDER BY ${I} ${t.direction})`}case"cumulative":{if(w(t.input,n))throw new A("UNSUPPORTED_MIXED_OPERATOR","Cumulative expressions cannot consume a JavaScript operator");const c=l(t.input,e,n,o,i,r,$,a),I=t.partitionBy.length>0?`PARTITION BY ${t.partitionBy.map(E=>l(E,e,n,o,i,r,$,a)).join(", ")} `:"",m=t.orderBy.map(E=>`${l(E.expression,e,n,o,i,r,$,a)} ${E.direction}`).join(", ");return`${e.aggregateFunction(t.function,c)} OVER (${I}ORDER BY ${m} ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)`}case"ordinal-encode":{if(!r)throw new A("SQL_BINDER_UNAVAILABLE","Ordinal encoding requires a value binder");return`CASE ${t.comparable.values.map((I,m)=>`WHEN ${l(t.input,e,n,o,i,r,$,a)} = ${r(I)} THEN ${t.comparable.direction*m}`).join(" ")} END`}case"ordinal-decode":{if(!r)throw new A("SQL_BINDER_UNAVAILABLE","Ordinal decoding requires a value binder");return`CASE ${t.comparable.values.map((I,m)=>`WHEN ${l(t.input,e,n,o,i,r,$,a)} = ${t.comparable.direction*m} THEN ${r(I)}`).join(" ")} END`}case"operator":{const c=n.get(t.operator);if(c.kind==="component")throw new A("COMPONENT_OPERATOR_REQUIRES_EXECUTOR",`${t.operator} must be compiled through the executor component pipeline`);if(c.toSQL&&(t.input&&w(t.input,n)||t.components?.some(E=>w(E,n))))throw new A("UNSUPPORTED_MIXED_OPERATOR",`${t.operator} cannot consume a JavaScript operator in SQL`);let I=t.input?l(t.input,e,n,o,i,r,$,a):t.column?u(t.column,t.qualifier):void 0;const m=t.components?.map(E=>l(E,e,n,o,i,r,$,a));if(t.query){if(c.kind!=="aggregate")throw new A("OPERATOR_QUERY_REQUIRES_AGGREGATION",`Pred query is only supported by aggregation operators: ${t.operator}`);if(!i)throw new A("INVALID_OPERATOR_QUERY","Operator query compiler is unavailable");I=`CASE WHEN ${i(t.query)} THEN ${I??"1"} END`}if(c.requiresProperty!==!1&&!I)throw new A("PRED_PROPERTY_REQUIRED",`${t.operator} requires a column`);return c.toSQL?c.toSQL({sql:{aggregateFunction:e.aggregateFunction.bind(e),quoteIdentifier:e.quoteIdentifier.bind(e),concat:e.concat.bind(e),substring:e.substring.bind(e),dateBucket:e.dateBucket.bind(e),value:E=>{if(!r)throw new A("SQL_BINDER_UNAVAILABLE","SQL value binder is unavailable");return r(E)},condition:(E,R)=>{if(!$)throw new A("SQL_CONDITION_UNAVAILABLE","SQL condition compiler is unavailable");return $(E,R)}},...I?{expression:I,column:I}:{},...m?{expressions:m}:{},args:t.args}):I??"NULL"}}},w=(t,e)=>t.kind==="window"?w(t.input,e)||t.partitionBy.some(n=>w(n,e)):t.kind==="cumulative"?w(t.input,e)||t.partitionBy.some(n=>w(n,e))||t.orderBy.some(n=>w(n.expression,e)):t.kind==="ordinal-encode"||t.kind==="ordinal-decode"?w(t.input,e):t.kind==="operator"&&(!e.get(t.operator).toSQL&&!!e.get(t.operator).evaluate||(t.input?w(t.input,e):!1)||(t.components?.some(n=>w(n,e))??!1)),nt=(t,e)=>t.sourceSql?D(t.sourceSql,t.sourceColumn,e,t.sourceTimestampColumn,t.sourceAlias):`${e.quoteIdentifier(t.sourceAlias)}.${e.quoteIdentifier(t.sourceColumn)}`,G=t=>typeof t=="object"&&t!==null&&!Array.isArray(t)&&Object.keys(t).some(e=>e.startsWith("$")),st=(t,e,n)=>{if(!n.supportsRecursiveCte)throw new A("RECURSIVE_QUERY_UNSUPPORTED",`Provider ${n.kind} does not support recursive queries`);if(t.roots.length===0)return"(SELECT NULL WHERE 1 = 0)";const o=n.quoteIdentifier("__recursive_tree__"),i=n.quoteIdentifier("__recursive_id__"),r=n.quoteIdentifier("__recursive_node__"),$=n.quoteIdentifier("__recursive_child__"),a=n.quoteIdentifier(t.idColumn),u=n.quoteIdentifier(t.parentColumn),c=M(n,t.source,t.sourceSql),I=()=>t.roots.map(y=>e.add(y)).join(", "),m=(y,h=!1)=>t.roots.length===1?`${y} ${h?"<>":"="} ${I()}`:`${y} ${h?"NOT IN":"IN"} (${I()})`,E=`${r}.${a}`,R=t.includeSelf?m(E):`${m(`${r}.${u}`)} AND ${m(E,!0)}`;return`(WITH RECURSIVE ${o}(${i}) AS ( SELECT ${E} FROM ${n.tableAlias(c,"__recursive_node__")} WHERE ${R} UNION SELECT ${$}.${a} FROM ${n.tableAlias(c,"__recursive_child__")} JOIN ${o} ON ${$}.${u} = ${o}.${i}) SELECT DISTINCT ${i} FROM ${o})`},j=(t,e,n,o,i,r,$)=>{if(e==="$recursive"){if(!x(n))throw new A("INVALID_RECURSIVE_QUERY","$recursive query was not planned");return`${t} IN ${st(n,o,i)}`}if(e==="$eq"&&n===null)return`${t} IS NULL`;if(e==="$ne"&&n===null)return`${t} IS NOT NULL`;const u={$eq:"=",$ne:"<>",$gt:">",$gte:">=",$lt:"<",$lte:"<="}[e];if(u){const c=b(n)?`(${r(n)})`:o.add(n);return`${t} ${u} ${c}`}if(e==="$in"||e==="$nin"){const c=e==="$in"?"IN":"NOT IN";if(b(n))return`${t} ${c} (${r(n)})`;if(!Array.isArray(n))throw new A("INVALID_QUERY_VALUE",`${e} requires an array or query LogicForm`);if(n.length===0)return e==="$in"?"0 = 1":"1 = 1";const I=n.filter(b);if(I.length>0){if(I.length!==n.length)throw new A("MIXED_SUBQUERY_VALUES",`${e} cannot mix query LogicForms and literal values`);return`${t} ${c} (${I.map(r).join(" UNION ALL ")})`}const m=n.map(E=>o.add(E)).join(", ");return`${t} ${c} (${m})`}if(b(n))throw new A("UNSUPPORTED_SUBQUERY_OPERATOR",`Query LogicForm is not supported by ${e}`);if(e==="$exists")return`${t} IS ${n?"NOT ":""}NULL`;if(e==="$contains")return`${t} LIKE ${o.add(`%${String(n)}%`)}`;if(e==="$regex"){if(typeof n!="string"||$!==void 0&&typeof $!="string")throw new A("INVALID_QUERY_VALUE","$regex requires a string pattern and args.options must be a string");return i.regex(t,o.add(n),$)}throw new A("UNSUPPORTED_QUERY_OPERATOR",`Unsupported query operator: ${e}`)},Q=(t,e,n,o,i)=>{if(Z(e)){const u=()=>`CASE ${e.comparable.values.map((E,R)=>`WHEN ${t} = ${n.add(E)} THEN ${e.comparable.direction*R}`).join(" ")} END`,c=new Set(["$gt","$gte","$lt","$lte"]),I=m=>typeof m!="object"||m===null||Array.isArray(m)?Q(t,m,n,o,i):Object.entries(m).filter(([E])=>E!=="args").map(([E,R])=>(E==="$and"||E==="$or")&&Array.isArray(R)?`(${R.map(I).join(E==="$and"?" AND ":" OR ")})`:c.has(E)?j(u(),E,e.comparable.direction*e.comparable.values.indexOf(String(R)),n,o,i):j(t,E,R,n,o,i)).join(" AND ");return I(e.condition)}if(Array.isArray(e))return j(t,"$in",e,n,o,i);if(!G(e))return j(t,"$eq",e,n,o,i);const r=[],$=e.args;if($!==void 0){if(typeof $!="object"||$===null||Array.isArray($))throw new A("INVALID_QUERY_VALUE","Query args.options must be a string");const u=$.options;if(u!==void 0&&typeof u!="string")throw new A("INVALID_QUERY_VALUE","Query args.options must be a string")}const a=$?.options;for(const[u,c]of Object.entries(e))if(u==="$and"||u==="$or"){if(!Array.isArray(c))throw new A("INVALID_QUERY_VALUE",`${u} requires an array`);const I=c.map(m=>Q(t,m,n,o,i));r.push(`(${I.join(u==="$and"?" AND ":" OR ")})`)}else u!=="args"&&r.push(j(t,u,c,n,o,i,u==="$regex"?a:void 0));if(r.length===0)throw new A("EMPTY_QUERY_CONDITION","Query condition contains no operators");return r.join(" AND ")},V=(t,e,n,o)=>{if(!o.arrayContains)throw new A("ARRAY_CONDITION_UNSUPPORTED",`Provider ${o.kind} does not support array conditions`);const i=o.arrayContains.bind(o),r=a=>i(t,n.add(a));if(Array.isArray(e))return`(${e.map(r).join(" OR ")})`;if(!G(e))return r(e);const $=[];for(const[a,u]of Object.entries(e))if(a==="$and"||a==="$or"){if(!Array.isArray(u))throw new A("INVALID_QUERY_VALUE",`${a} requires an array`);const c=u.map(I=>V(t,I,n,o));$.push(`(${c.join(a==="$and"?" AND ":" OR ")})`)}else if(a==="$eq")$.push(r(u));else if(a==="$ne")$.push(`NOT (${r(u)})`);else if(a==="$in"||a==="$nin"){if(!Array.isArray(u))throw new A("INVALID_QUERY_VALUE",`${a} requires an array`);const c=u.map(r),I=c.length===0?a==="$in"?"0 = 1":"1 = 1":c.join(a==="$in"?" OR ":" AND NOT ");$.push(a==="$nin"&&c.length>0?`NOT (${c.join(" OR ")})`:`(${I})`)}else if(a==="$exists")$.push(`${t} IS ${u?"NOT ":""}NULL`);else if(a!=="args")throw new A("UNSUPPORTED_ARRAY_QUERY_OPERATOR",`Unsupported array query operator: ${a}`);if($.length===0)throw new A("EMPTY_QUERY_CONDITION","Query condition contains no operators");return $.join(" AND ")},Y=(t,e,n,o,i,r)=>{const $=[];for(const[a,u]of Object.entries(t)){if(a==="$and"||a==="$or"){if(!Array.isArray(u))throw new A("INVALID_QUERY_VALUE",`${a} requires an array`);const I=u.map(m=>{if(typeof m!="object"||m===null||Array.isArray(m))throw new A("INVALID_QUERY_VALUE",`${a} items must be query objects`);return Y(m,e,n,o,i,r)});$.push(`(${I.join(a==="$and"?" AND ":" OR ")})`);continue}const c=e(a);$.push(n(a)?V(c,u,o,i):Q(c,u,o,i,r))}return $.join(" AND ")},k=(t,e,n,o,i)=>t.orderBy.map(r=>{const $=e.quoteIdentifier(r.name),a=`${o?`${o}.`:""}${$}`,u=r.comparable,c=!o&&i&&r.expression?i(r.expression):a;return`${u?`CASE ${u.values.map((m,E)=>`WHEN ${c} = ${n.add(m)} THEN ${u.direction*E}`).join(" ")} END`:a} ${r.direction}`}).join(", "),F=(t,e)=>{let n=t;for(;e.has(n);)n=`_${n}`;return n},p=(t,e,n,o)=>e.concat(t.flatMap((i,r)=>[...r>0?[`'${n.replaceAll("'","''")}'`]:[],`CASE WHEN ${i} IS NULL THEN '${o.replaceAll("'","''")}' ELSE ${e.stringify(i)} END`])),it=(t,e,n,o,i)=>{const r=new Set(e.projections.map(({name:d})=>d)),$="__sdb_page_source",a="__sdb_page",u=o.quoteIdentifier($),c=o.quoteIdentifier(a),I=F("__sdb_row_number",r),m=F("__sdb_total_rows",r),E=o.quoteIdentifier(I),R=o.quoteIdentifier(m),y=k(e,o,i,u),h=y?`ORDER BY ${y}`:"",T=`CEIL(${R} * ${n.ratio})`,P=n.page-1,N=n.page,s=[...e.projections.map(({name:d})=>d),...e.rowIdentity.kind==="property"?[e.rowIdentity.internalName]:[],...e.rowIdentity.kind==="group"?["_id"]:[]].map(d=>`${c}.${o.quoteIdentifier(d)}`).join(", "),f=k(e,o,i,c),_=`SELECT ${u}.*, ROW_NUMBER() OVER (${h}) AS ${E}, COUNT(*) OVER () AS ${R} FROM ${o.tableAlias(`(${t})`,$)}`;let O=`SELECT ${s} FROM ${o.tableAlias(`(${_})`,a)} WHERE ${c}.${E} > ${P} * ${T} AND ${c}.${E} <= ${N} * ${T}`;return f&&(O+=` ORDER BY ${f}`),O},J=(t,e,n,o)=>{const i=e.groupLimit;if(!i)return t;const r="__sdb_limit_by_source",$="__sdb_limit_by_ranked",a=n.quoteIdentifier(r),u=n.quoteIdentifier($),c=new Set(e.projections.map(({name:N})=>N)),I=F("__sdb_limit_by_row_number",c),m=n.quoteIdentifier(I),E=i.partitionBy.map(N=>`${a}.${n.quoteIdentifier(N)}`).join(", "),R=i.orderBy.map(({name:N,direction:U,comparable:s})=>{const f=`${a}.${n.quoteIdentifier(N)}`;return`${s?`CASE ${s.values.map((O,d)=>`WHEN ${f} = ${o.add(O)} THEN ${s.direction*d}`).join(" ")} END`:f} ${U}`}).join(", "),h=[...e.projections.map(({name:N})=>N),...e.rowIdentity.kind==="property"?[e.rowIdentity.internalName]:[],...e.rowIdentity.kind==="group"?["_id"]:[]].map(N=>`${u}.${n.quoteIdentifier(N)}`).join(", "),T=`PARTITION BY ${E} ORDER BY ${R}`,P=`SELECT ${a}.*, ROW_NUMBER() OVER (${T}) AS ${m} FROM ${n.tableAlias(`(${t})`,r)}`;return`SELECT ${h} FROM ${n.tableAlias(`(${P})`,$)} WHERE ${u}.${m} <= ${i.count}`},C=(t,e,n=K(),o=0)=>{const i=new tt(e,o);if(t.composition){const s=t.projections.map(({name:g})=>g),f=t.groupBy.map(({name:g})=>g),_=t.composition.map((g,S)=>{const B=C(g,e,n,i.nextParameterOffset);i.append(B.parameters);const W=`__composition_child_${S}`,X=e.quoteIdentifier(W),z=new Set(g.projections.map(({name:q})=>q));return`SELECT ${s.map(q=>z.has(q)?`${X}.${e.quoteIdentifier(q)} AS ${e.quoteIdentifier(q)}`:`NULL AS ${e.quoteIdentifier(q)}`).join(", ")} FROM ${e.tableAlias(`(${B.sql})`,W)}`}),O="__composition_union",d=e.quoteIdentifier(O),H=s.map(g=>{const S=`${d}.${e.quoteIdentifier(g)}`;return f.includes(g)?`${S} AS ${e.quoteIdentifier(g)}`:`MAX(${S}) AS ${e.quoteIdentifier(g)}`});if(t.rowIdentity.kind==="group"){const g=t.rowIdentity.names.map(S=>`${d}.${e.quoteIdentifier(S)}`);H.push(`${p(g,e,t.rowIdentity.separator,t.rowIdentity.nullLabel)} AS ${e.quoteIdentifier("_id")}`)}let L=`SELECT ${H.join(", ")} FROM ${e.tableAlias(`(${_.join(" UNION ALL ")})`,O)}`;if(f.length>0){const g=f.map(B=>`${d}.${e.quoteIdentifier(B)}`),S=t.rowIdentity.kind==="group"?[p(g,e,t.rowIdentity.separator,t.rowIdentity.nullLabel)]:[];L+=` GROUP BY ${[...g,...S].join(", ")}`}return t.groupLimit&&(L=J(L,t,e,i)),t.orderBy.length>0&&(L+=` ORDER BY ${k(t,e,i)}`),t.pagination?.kind==="rows"&&(L=e.paginate(L,t.pagination.pageSize,0)),{sql:L,parameters:i.values}}const r=s=>{if(s.plan.projections.some(({expression:_})=>w(_,n)))throw new A("JAVASCRIPT_OPERATOR_IN_SUBQUERY","A query LogicForm cannot use a JavaScript-only operator");const f=C(s.plan.rowIdentity.kind==="property"||s.plan.rowIdentity.kind==="group"?{...s.plan,rowIdentity:{kind:"generated"}}:s.plan,e,n,i.nextParameterOffset);return i.append(f.parameters),f.sql},$=(t.joins?.length??0)>0,a=(t.arrayExpansions?.length??0)>0,u=$||a||t.sourcePlan!==void 0||t.sourceSql!==void 0,c=new Map,I=(t.arrayExpansions??[]).map(s=>{if(!e.expandArray)throw new A("ARRAY_EXPANSION_UNSUPPORTED",`Provider ${e.kind} does not support array expansion`);const f=e.expandArray(nt(s,e),s.alias,"value");return c.set(s.path,f.valueExpression),f}),m=s=>{const f=t.fieldReferences?.[s];if(f?.sql)return D(f.sql,f.property,e,f.timestampColumn,u?f.timestampQualifier??f.qualifier:void 0);if(f&&u)return`${e.quoteIdentifier(f.qualifier)}.${e.quoteIdentifier(f.column)}`;const _=t.fields[s];if(!_)throw new A("PROPERTY_NOT_FOUND",`Query property not found: ${s}`);return e.quoteIdentifier(_)},E=s=>l(s,e,n,u,f=>Y(f,m,_=>t.fieldReferences?.[_]?.isArray===!0,i,e,r),f=>i.add(f),(f,_)=>Q(f,_,i,e,r),f=>{const _=c.get(f);if(!_)throw new A("ARRAY_EXPANSION_NOT_FOUND",`Array expansion not found: ${f}`);return _}),R=t.projections.map(s=>`${E(s.expression)} AS ${e.quoteIdentifier(s.name)}`),y=t.rowIdentity.kind==="property"?`${u&&t.sourceAlias?`${e.quoteIdentifier(t.sourceAlias)}.`:""}${e.quoteIdentifier(t.rowIdentity.column)} AS ${e.quoteIdentifier(t.rowIdentity.internalName)}`:t.rowIdentity.kind==="group"?(()=>{const s=t.rowIdentity.names.map(f=>{const _=t.groupBy.find(O=>O.name===f)??t.projections.find(O=>O.name===f);if(!_)throw new A("GROUP_IDENTITY_OUTPUT_NOT_FOUND",`Group identity output not found: ${f}`);return E(_.expression)});return`${p(s,e,t.rowIdentity.separator,t.rowIdentity.nullLabel)} AS ${e.quoteIdentifier("_id")}`})():void 0,h=[...R,...y?[y]:[]].join(", "),T=t.sourcePlan?(()=>{const s=C(t.sourcePlan,e,n,i.nextParameterOffset);return i.append(s.parameters),`(${s.sql})`})():M(e,t.source,t.sourceSql),P=u&&t.sourceAlias?e.tableAlias(T,t.sourceAlias):T;let N=`SELECT ${h} FROM ${P}`;for(const s of I)N+=` ${s.joinSql}`;for(const s of t.joins??[]){N+=` LEFT JOIN ${e.tableAlias(M(e,s.targetSource,s.targetSourceSql),s.targetAlias)}`;const f=s.sourceArrayExpansionPath?c.get(s.sourceArrayExpansionPath):s.sourceSql?D(s.sourceSql,s.sourceColumn,e,s.sourceTimestampColumn,s.sourceAlias):`${e.quoteIdentifier(s.sourceAlias)}.${e.quoteIdentifier(s.sourceColumn)}`,_=s.targetSql?D(s.targetSql,s.targetColumn,e,s.targetTimestampColumn,s.targetAlias):`${e.quoteIdentifier(s.targetAlias)}.${e.quoteIdentifier(s.targetColumn)}`;if(!f)throw new A("ARRAY_EXPANSION_NOT_FOUND",`Array expansion not found: ${s.path}`);if(N+=` ON ${f} = ${_}`,s.sourceTimestampColumn&&s.targetTimestampColumn&&s.sourceTimestampGranularity&&s.targetTimestampGranularity){const O=`${e.quoteIdentifier(s.sourceAlias)}.${e.quoteIdentifier(s.sourceTimestampColumn)}`,d=`${e.quoteIdentifier(s.targetAlias)}.${e.quoteIdentifier(s.targetTimestampColumn)}`;N+=` AND ${e.dateBucket(O,s.sourceTimestampGranularity)} = ${e.dateBucket(d,s.targetTimestampGranularity)}`}}const U=[];if(t.query&&Object.keys(t.query).length>0&&U.push(Y(t.query,m,s=>t.fieldReferences?.[s]?.isArray===!0,i,e,r)),U.length>0&&(N+=` WHERE ${U.join(" AND ")}`),t.groupBy.length>0){const s=t.groupBy.map(_=>E(_.expression)),f=t.rowIdentity.kind==="group"?[p(s,e,t.rowIdentity.separator,t.rowIdentity.nullLabel)]:[];N+=` GROUP BY ${[...s,...f].join(", ")}`}if(t.having&&Object.keys(t.having).length>0){const s=f=>{const _=t.projections.find(O=>O.name===f);if(!_)throw new A("INVALID_HAVING",`Having field is not an output: ${f}`);return e.supportsHavingAlias===!1?E(_.expression):e.quoteIdentifier(f)};N+=` HAVING ${Y(t.having,s,()=>!1,i,e,r)}`}if(t.groupLimit&&(N=J(N,t,e,i)),t.pagination?.kind==="ratio")N=it(N,t,t.pagination,e,i);else if(t.orderBy.length>0&&(N+=` ORDER BY ${k(t,e,i,void 0,E)}`),t.pagination?.kind==="rows"){const s=(t.pagination.page-1)*t.pagination.pageSize;N=e.paginate(N,t.pagination.pageSize,s)}return{sql:N,parameters:i.values}};export{C as compilePlan};
@@ -1,2 +1,2 @@
1
- /** Conservatively remove unused aliased columns from nested SELECT projections. */
1
+ /** Conservatively simplify nested SELECT projections and pass-through views. */
2
2
  export declare const shortenSQL: (sql: string) => string;
@@ -1,2 +1,3 @@
1
- const h=e=>{if(typeof e!="string"||!/\bSELECT\b/i.test(e))return e;try{const c=d(e),i=[];for(const t of c){const s=$(e,t.selectEnd,t.fromStart);if(!s||s.length<2||s.some(u=>u.trim()==="*"))continue;const n=g(e.slice(t.fromStart,t.close),t,c),l=S(e,t,c),r=s.filter(u=>{const p=E(u);if(!p)return!0;const a=new RegExp(`(^|[^\\w$])${x(p)}([^\\w$]|$)`,"i");return a.test(n)||a.test(l)});if(r.length===s.length||r.length===0)continue;const f=e.slice(t.selectEnd,t.fromStart);i.push({start:t.selectEnd,end:t.fromStart,value:f.match(/^\s*/)[0]+r.map(u=>u.trim()).join(", ")+f.match(/\s*$/)[0]})}i.sort((t,s)=>s.start-t.start);let o=e;for(const t of i)o=o.slice(0,t.start)+t.value+o.slice(t.end);return o}catch{return e}},g=(e,c,i)=>m(e,i.filter(o=>o.open>c.open&&o.close<c.close).map(o=>({start:o.open-c.fromStart,end:o.close-c.fromStart+1}))),S=(e,c,i)=>{const o=i.filter(n=>n.open<c.open&&n.close>c.close).sort((n,l)=>n.close-n.open-(l.close-l.open))[0],t=o?o.open+1:0,s=o?o.close:e.length;return m(e.slice(t,s),i.filter(n=>n.open>=t&&n.close<s).map(n=>({start:n.open-t,end:n.close-t+1})))},m=(e,c)=>{if(!c.length)return e;const i=e.split("");for(const o of c)for(let t=Math.max(0,o.start);t<Math.min(i.length,o.end);t+=1)i[t]=" ";return i.join("")},d=e=>{const c=[],i=[];let o="",t="";for(let s=0;s<e.length;s+=1){const n=e[s],l=e[s+1];if(t==="--"){n===`
2
- `&&(t="");continue}if(t==="/*"){n==="*"&&l==="/"&&(t="",s+=1);continue}if(o){n===o&&e[s-1]!=="\\"&&(o="");continue}if(n==="-"&&l==="-"){t="--",s+=1;continue}if(n==="/"&&l==="*"){t="/*",s+=1;continue}if(n==="`"||n==='"'||n==="'"){o=n;continue}if(n==="("){i.push({open:s});continue}if(n!==")")continue;const r=i.pop();if(!r)continue;const f=/\bSELECT\b/i.exec(e.slice(r.open+1,s));if(!f)continue;const u=r.open+1+f.index,p=/\bFROM\b/i.exec(e.slice(u+6,s));p&&c.push({open:r.open,close:s,selectEnd:u+6,fromStart:u+6+p.index})}return c},$=(e,c,i)=>{const o=e.slice(c,i),t=[];let s=0,n=0,l="";for(let r=0;r<o.length;r+=1){const f=o[r];if(l){f===l&&o[r-1]!=="\\"&&(l="");continue}if(f==="`"||f==='"'||f==="'"){l=f;continue}f==="("?n+=1:f===")"?n-=1:f===","&&n===0&&(t.push(o.slice(s,r)),s=r+1)}return l||n!==0?null:(t.push(o.slice(s)),t)},E=e=>e.trim().match(/\bAS\s+(`[^`]+`|"[^"]+"|[A-Za-z_][\w$]*)\s*$/i)?.[1]?.replace(/^`|`$/g,"").replace(/^"|"$/g,"")??null,x=e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");export{h as shortenSQL};
1
+ const L=e=>{if(typeof e!="string"||!/\bSELECT\b/i.test(e))return e;try{let o=e;const f=R(e).length*2+2;for(let i=0;i<f;i+=1){const s=N(o);if(s===o)return o;o=s}return o}catch{return e}},N=e=>{const o=R(e),f=O(e,o);if(f!==e)return f;const i=[];for(const n of o){const t=x(e,n.selectEnd,n.fromStart),r=t?.length===1&&t[0]?.trim()==="*"?Q(e.slice(n.fromStart+4,n.close)):null;if(r){i.push({start:n.open,end:n.close+1,value:r});continue}if(!t||t.some(h=>h.trim()==="*"))continue;const c=B(I(e.slice(n.fromStart,n.close),n,o)),a=j(e,n);let l=t.filter(h=>{const $=w(h);if(!$)return!0;const m=new RegExp(`(^|[^\\w$])${C($)}([^\\w$]|$)`,"i");return m.test(c)||m.test(a)}),p=!0;for(;p;){p=!1;const h=b(l.join(" ")),$=t.filter(m=>{if(l.includes(m))return!1;const E=w(m);return E?new RegExp(`(^|[^\\w$])${C(E)}([^\\w$]|$)`,"i").test(h):!1});$.length>0&&(l=t.filter(m=>l.includes(m)||$.includes(m)),p=!0)}if(l.length===t.length)continue;const u=e.slice(n.selectEnd,n.fromStart),d=l.length===0&&z(e,n)&&!/(^|[^\w$])\*([^\w$]|$)/.test(a)&&!/^\s*(?:DISTINCT|ALL|TOP|SQL_)\b/i.test(u)&&!/\b(?:UNION|INTERSECT|EXCEPT)\b/i.test(c);l.length===0&&!d||i.push({start:n.selectEnd,end:n.fromStart,value:u.match(/^\s*/)[0]+(d?"1":l.map(h=>h.trim()).join(", "))+u.match(/\s*$/)[0]})}i.sort((n,t)=>t.start-n.start);let s=e;for(const n of i)s=s.slice(0,n.start)+n.value+s.slice(n.end);return s},O=(e,o)=>{const f=/^\s*SELECT\b/i.exec(e);if(!f)return e;const i=f.index+f[0].length,s=S(e,"FROM",i);if(s===-1||!x(e,i,s)?.every(F))return e;const t=o.find(u=>u.open>s&&e.slice(s+4,u.open).trim().length===0);if(!t||!/^\s*1\s*$/.test(e.slice(t.selectEnd,t.fromStart)))return e;const r=o.filter(u=>u.open>t.fromStart&&u.close<t.close).sort((u,d)=>d.close-d.open-(u.close-u.open))[0];if(!r||e.slice(t.fromStart+4,r.open).trim().length>0)return e;const c=e.slice(r.close+1,t.close);if(/\b(?:GROUP|HAVING|ORDER|LIMIT|OFFSET|UNION|INTERSECT|EXCEPT)\b/i.test(c))return e;const a=U.exec(c)?.[1],l=v.exec(e.slice(t.close+1));if(!a||!l||T(a)!==T(l[1]))return e;const p=/;(\s*)$/.exec(e)?.[0]??"";return e.slice(0,t.open)+e.slice(r.open,t.close)+p},I=(e,o,f)=>k(e,f.filter(i=>i.open>o.open&&i.close<o.close).map(i=>({start:i.open-o.fromStart,end:i.close-o.fromStart+1}))),j=(e,o)=>{const f=e.slice(0,o.open)+" ".repeat(o.close-o.open+1)+e.slice(o.close+1);return b(f)},k=(e,o)=>{if(!o.length)return e;const f=e.split("");for(const i of o)for(let s=Math.max(0,i.start);s<Math.min(f.length,i.end);s+=1)f[s]=" ";return f.join("")},S=(e,o,f)=>{let i=0,s=0,n="",t="";for(let r=f;r<e.length;r+=1){const c=e[r],a=e[r+1];if(t==="--"){c===`
2
+ `&&(t="");continue}if(t==="/*"){c==="*"&&a==="/"&&(t="",r+=1);continue}if(n){c===n&&e[r-1]!=="\\"&&(n="");continue}if(c==="-"&&a==="-"){t="--",r+=1;continue}if(c==="/"&&a==="*"){t="/*",r+=1;continue}if(c==="`"||c==='"'||c==="'"){n=c;continue}if(c==="("){i+=1;continue}if(c===")"){i-=1;continue}if(c==="["){s+=1;continue}if(c==="]"){s-=1;continue}if(i===0&&s===0&&new RegExp(`^${o}\\b`,"i").test(e.slice(r)))return r}return-1},R=e=>{const o=[],f=[];let i="",s="";for(let n=0;n<e.length;n+=1){const t=e[n],r=e[n+1];if(s==="--"){t===`
3
+ `&&(s="");continue}if(s==="/*"){t==="*"&&r==="/"&&(s="",n+=1);continue}if(i){t===i&&e[n-1]!=="\\"&&(i="");continue}if(t==="-"&&r==="-"){s="--",n+=1;continue}if(t==="/"&&r==="*"){s="/*",n+=1;continue}if(t==="`"||t==='"'||t==="'"){i=t;continue}if(t==="("){f.push({open:n});continue}if(t!==")")continue;const c=f.pop();if(!c)continue;const a=e.slice(c.open+1,n),l=/^\s*SELECT\b/i.exec(a);if(!l)continue;const p=c.open+1+l.index,u=S(e,"FROM",p+6);u===-1||u>=n||o.push({open:c.open,close:n,selectEnd:p+6,fromStart:u})}return o},x=(e,o,f)=>{const i=e.slice(o,f),s=[];let n=0,t=0,r=0,c="";for(let a=0;a<i.length;a+=1){const l=i[a];if(c){l===c&&i[a-1]!=="\\"&&(c="");continue}if(l==="`"||l==='"'||l==="'"){c=l;continue}l==="("?t+=1:l===")"?t-=1:l==="["?r+=1:l==="]"?r-=1:l===","&&t===0&&r===0&&(s.push(i.slice(n,a)),n=a+1)}return c||t!==0||r!==0?null:(s.push(i.slice(n)),s)},w=e=>e.trim().match(/\bAS\s+(`[^`]+`|"[^"]+"|[A-Za-z_][\w$]*)\s*$/i)?.[1]?.replace(/^`|`$/g,"").replace(/^"|"$/g,"")??null,g='(?:`(?:``|[^`])+`|"(?:""|[^"])+"|[A-Za-z_][\\w$]*)',P=new RegExp(`\\s+AS\\s+${g}\\s*$`,"i"),y=new RegExp(`\\bAS\\s+${g}`,"gi"),D=new RegExp(`(?:${g}\\s*\\.\\s*)+${g}`,"g"),M=new RegExp(`^${g}(?:\\s*\\.\\s*${g})*$`),U=new RegExp(`^\\s*(?:AS\\s+)?(${g})\\s+WHERE\\b`,"i"),v=new RegExp(`^\\s*(?:AS\\s+)?(${g})\\s*;?\\s*$`,"i"),T=e=>e.replace(/^`|`$/g,"").replace(/^"|"$/g,"").toLowerCase(),F=e=>{const o=e.replace(P,"").trim();return/^(?:NULL|TRUE|FALSE|[-+]?\d+(?:\.\d+)?|'(?:''|[^'])*')$/i.test(o)||/^COUNT\s*\(\s*(?:\*|1)\s*\)$/i.test(o)},A=(e,o)=>e.replace(o,f=>" ".repeat(f.length)),b=e=>A(e,y),B=e=>A(e,D),z=(e,o)=>/\b(?:FROM|JOIN)\s*$/i.test(e.slice(0,o.open)),Q=e=>{const o=e.trim();return M.test(o)?o:null},C=e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");export{L as shortenSQL};
package/dist/errors.d.ts CHANGED
@@ -2,4 +2,6 @@ export declare class SemanticDBError extends Error {
2
2
  readonly code: string;
3
3
  readonly details?: Readonly<Record<string, unknown>> | undefined;
4
4
  constructor(code: string, message: string, details?: Readonly<Record<string, unknown>> | undefined);
5
+ /** Return the user-facing message for an execution locale. */
6
+ localizedMessage(locale?: string): string;
5
7
  }
package/dist/errors.js CHANGED
@@ -1 +1 @@
1
- class s extends Error{code;details;constructor(r,e,t){super(e),this.code=r,this.details=t,this.name="SemanticDBError"}}export{s as SemanticDBError};
1
+ class i extends Error{code;details;constructor(s,t,e){super(t),this.code=s,this.details=e,this.name="SemanticDBError"}localizedMessage(s="zh-CN"){if(this.code==="PROPERTY_NOT_FOUND"&&s.toLowerCase().startsWith("en")){const t=this.details?.schemaId,e=this.details?.schemaName,o=this.details?.property,r=this.details?.propertyName;if(typeof t=="string"&&typeof e=="string"&&typeof o=="string"&&typeof r=="string")return`Property not found in Schema: ${e}(${t}): ${r}`}return this.message}}export{i as SemanticDBError};
@@ -1 +1 @@
1
- import{resolvePlugins as E}from"../plugins/index.js";const p=e=>{if(Array.isArray(e))return e.map(p).filter(t=>t!==void 0);if(typeof e!="object"||e===null||Object.prototype.toString.call(e)!=="[object Object]")return e;const o=Object.entries(e),n=o.filter(([,t])=>t!==null).map(([t,a])=>[t,p(a)]).filter(([,t])=>t!==void 0);if(!(o.length>0&&n.length===0))return Object.fromEntries(n)},F=e=>{if(e.comparable!=null)return e.comparable;if(e.is_comparable!==!0||e.isArray===!0||e.type!=="string"&&e.type!=="category"||!Array.isArray(e.constraints?.enum))return;const n=e.constraints.enum.map(t=>Array.isArray(t)?t[0]:t);if(!(n.length===0||n.some(t=>typeof t!="string")))return{values:[...new Set(n)],direction:e.comparable_order===-1?-1:1}},K=e=>{if(typeof e!="object"||e===null||Array.isArray(e))return e;const o=e;if(o.sql!=null)return Object.keys(o).every(n=>n==="sql")?e:{sql:o.sql}},v=(e,o=[],n=!0)=>{const t=E(o),a={},h=new Set(t.flatMap(d=>[...d.schemaMetadata?.keys??[],...d.legacySchemaKeys??[]]));for(const[d,_]of Object.entries(e)){const s=p(_),l=s,j=(Array.isArray(l.properties)?l.properties:[]).some(i=>i.is_observation===!0)||l.is_observation===!0,A=l.is_snapshot===!0,f=typeof s.id=="string"&&s.id.length>0?s.id:n&&typeof s._id=="string"&&s._id.length>0?s._id:s.id,{properties:O}=s,S=Object.fromEntries(Object.entries(s).filter(([i])=>(n?!["_id","_sid","sid","is_observation","is_snapshot"].includes(i):i!=="properties")&&i!=="properties"&&!h.has(i))),m=O.map(i=>{const r=i,y=typeof r.id=="string"&&r.id.length>0?r.id:n&&typeof r._id=="string"&&r._id.length>0?r._id:r.id,{udf:P,...b}=r,u=F(r),g=K(P),M=u!==void 0&&r.comparable==null?"category":r.type==="object"?"entity":r.type;return{...n?Object.fromEntries(Object.entries(b).filter(([q])=>!["_id","_sid","sid","is_observation","is_comparable","comparable_order"].includes(q))):b,type:M,...y!==void 0?{id:y}:{},...g!==void 0?{udf:g}:{},...u!==void 0?{comparable:u}:{}}});let c={};for(const i of t)for(const r of i.schemaMetadata?.keys??[])s[r]!==void 0&&(c={...c,[r]:s[r]});for(const i of t)c={...c,...i.normalizeLegacySchema?.({source:s,properties:m})};const z={...S,...f!==void 0?{id:f}:{},properties:m,...n&&(A||j)?{temporalSemantics:"snapshot"}:{},...c};a[d]=z}return a};export{v as normalizeSchemas};
1
+ import{resolvePlugins as E}from"../plugins/index.js";const u=e=>{if(Array.isArray(e))return e.map(u).filter(t=>t!==void 0);if(typeof e!="object"||e===null||Object.prototype.toString.call(e)!=="[object Object]")return e;const o=Object.entries(e),n=o.filter(([,t])=>t!==null).map(([t,a])=>[t,u(a)]).filter(([,t])=>t!==void 0);if(!(o.length>0&&n.length===0))return Object.fromEntries(n)},F=e=>{if(e.comparable!=null)return e.comparable;if(e.is_comparable!==!0||e.isArray===!0||e.type!=="string"&&e.type!=="category"||!Array.isArray(e.constraints?.enum))return;const n=e.constraints.enum.map(t=>Array.isArray(t)?t[0]:t);if(!(n.length===0||n.some(t=>typeof t!="string")))return{values:[...new Set(n)],direction:e.comparable_order===-1?-1:1}},K=e=>{if(typeof e!="object"||e===null||Array.isArray(e))return e;const o=e;if(o.sql!=null)return Object.keys(o).every(n=>n==="sql")?e:{sql:o.sql}},v=(e,o=[],n=!0)=>{const t=E(o),a={},_=new Set(t.flatMap(d=>[...d.schemaMetadata?.keys??[],...d.legacySchemaKeys??[]]));for(const[d,h]of Object.entries(e)){const s=u(h),l=s,j=(Array.isArray(l.properties)?l.properties:[]).some(i=>i.is_observation===!0)||l.is_observation===!0,A=l.is_snapshot===!0,f=typeof s.id=="string"&&s.id.length>0?s.id:n&&typeof s._id=="string"&&s._id.length>0?s._id:s.id,{properties:O}=s,S=Object.fromEntries(Object.entries(s).filter(([i])=>(n?!["_id","_sid","sid","is_observation","is_snapshot"].includes(i):i!=="properties")&&i!=="properties"&&!_.has(i))),m=O.map(i=>{const r=i,y=typeof r.id=="string"&&r.id.length>0?r.id:n&&typeof r._id=="string"&&r._id.length>0?r._id:r.id,{udf:P,...b}=r,p=F(r),g=K(P),M=p!==void 0&&r.comparable==null?"category":r.type==="object"?"entity":r.type;return{...n?Object.fromEntries(Object.entries(b).filter(([q])=>!["_id","_sid","sid","is_observation","is_comparable","comparable_order"].includes(q))):b,type:M,...r.primal_type==="object"?{primal_type:"entity"}:{},...y!==void 0?{id:y}:{},...g!==void 0?{udf:g}:{},...p!==void 0?{comparable:p}:{}}});let c={};for(const i of t)for(const r of i.schemaMetadata?.keys??[])s[r]!==void 0&&(c={...c,[r]:s[r]});for(const i of t)c={...c,...i.normalizeLegacySchema?.({source:s,properties:m})};const z={...S,...f!==void 0?{id:f}:{},properties:m,...n&&(A||j)?{temporalSemantics:"snapshot"}:{},...c};a[d]=z}return a};export{v as normalizeSchemas};
@@ -1 +1 @@
1
- import{SemanticDBError as l}from"../errors.js";import{clone as P}from"../utils/clone.js";import{groupRowIdentity as b}from"./row-identity.js";const R=r=>{const e=P(r);return delete e.having,delete e.sort,delete e.limit,delete e.page,e},m=(r,e)=>r.operator&&e.get(r.operator).kind==="component"?!0:typeof r.pred=="object"&&m(r.pred,e)||(r.components?.some(i=>m(i,e))??!1),j=(r,e,i,o,n,t)=>{const s=r.preds??[],c=s.map((u,p)=>({pred:u,index:p,definition:u.operator?o.get(u.operator):void 0})).filter(({definition:u})=>u?.kind==="component");if(c.length===0)return;for(const u of s)if((!u.operator||o.get(u.operator).kind!=="component")&&(typeof u.pred=="object"&&m(u.pred,o)||(u.components?.some(p=>m(p,o))??!1)))throw new l("UNSUPPORTED_COMPONENT_NESTING","A component operator must currently be the top-level projection operator");const a=[],d=s.filter((u,p)=>!c.some(y=>y.index===p));if(d.length>0){const u=R(r);u.preds=d,a.push({kind:"query",logicform:u})}const g=r.groupby?.length??0;for(const{pred:u,index:p,definition:y}of c){const A=y?.createComponent;if(!A)continue;const w=e.projections[g+p]?.name;if(!w)throw new l("INVALID_COMPONENT_PLAN","Component output projection is missing");const C=t?.preds?.[p],h=A({logicform:r,pred:C??u,outputName:w,schema:i,resolveSchema:n,...t?{sourceLogicform:t}:{}});if(h.kind==="query"&&(h.logicform.preds??[]).some(E=>m(E,o)))throw new l("UNSUPPORTED_COMPONENT_NESTING","Nested component operators are not supported yet");a.push(h)}return a},f=r=>r instanceof Date?r.getTime():r,v=(r,e,i,o)=>{const n=f(r),t=f(i);switch(e){case"$eq":return n===t;case"$ne":return n!==t;case"$gt":return n!=null&&t!==void 0&&t!==null&&n>t;case"$gte":return n!=null&&t!==void 0&&t!==null&&n>=t;case"$lt":return n!=null&&t!==void 0&&t!==null&&n<t;case"$lte":return n!=null&&t!==void 0&&t!==null&&n<=t;case"$in":return Array.isArray(i)&&i.some(s=>f(s)===n);case"$nin":return Array.isArray(i)&&i.every(s=>f(s)!==n);case"$exists":return i?r!=null:r==null;case"$contains":return String(r).includes(String(i));case"$regex":return typeof i=="string"&&new RegExp(i,o).test(String(r));default:throw new l("UNSUPPORTED_QUERY_OPERATOR",`Unsupported having operator: ${e}`)}},S=(r,e)=>{if(!e||typeof e!="object"||Array.isArray(e))return f(r)===f(e);const i=Object.entries(e),o=e.args,n=o&&typeof o=="object"&&!Array.isArray(o)?o.options:void 0;return i.every(([t,s])=>{if(t==="args")return!0;if(t==="$and"||t==="$or"){if(!Array.isArray(s))return!1;const c=s.map(a=>S(r,a));return t==="$and"?c.every(Boolean):c.some(Boolean)}return v(r,t,s,typeof n=="string"?n:void 0)})},$=(r,e)=>Object.entries(e).every(([i,o])=>{if(i==="$and"||i==="$or"){if(!Array.isArray(o))return!1;const n=o.map(t=>$(r,t));return i==="$and"?n.every(Boolean):n.some(Boolean)}return S(r[i],o)}),N=(r,e)=>e.length===0?"0":b(r,e),O=(r,e)=>{const i=new Map;for(const o of r)for(const n of o){const t=N(n,e);i.set(t,{...i.get(t)??{},...n})}return[...i.values()]},B=(r,e)=>{const i=e.groupBy.map(({name:n})=>n);let o=O(r,i);if(e.having&&(o=o.filter(n=>$(n,e.having))),e.groupLimit){const n=e.groupLimit,t=new Map;for(const s of o){const c=N(s,n.partitionBy);t.set(c,[...t.get(c)??[],s])}o=[...t.values()].flatMap(s=>s.sort((c,a)=>{for(const d of n.orderBy){const g=f(c[d.name]),u=f(a[d.name]);if(g!==u)return g==null?d.direction==="ASC"?-1:1:u==null?d.direction==="ASC"?1:-1:(g<u?-1:1)*(d.direction==="ASC"?1:-1)}return 0}).slice(0,n.count))}if(e.orderBy.length>0&&o.sort((n,t)=>{for(const s of e.orderBy){const c=f(n[s.name]),a=f(t[s.name]);if(c!==a)return c==null?s.direction==="ASC"?-1:1:a==null?s.direction==="ASC"?1:-1:(c<a?-1:1)*(s.direction==="ASC"?1:-1)}return 0}),e.pagination?.kind==="rows"){const n=(e.pagination.page-1)*e.pagination.pageSize;o=o.slice(n,n+e.pagination.pageSize)}else if(e.pagination?.kind==="ratio"){const n=Math.ceil(o.length*e.pagination.ratio),t=(e.pagination.page-1)*n;o=o.slice(t,t+n)}return o};export{j as createExecutionUnits,O as mergeComponentRows,B as mergeExecutionResults};
1
+ import{SemanticDBError as l}from"../errors.js";import{clone as P}from"../utils/clone.js";import{groupRowIdentity as b,identityPart as R}from"./row-identity.js";const _=t=>{const e=P(t);return delete e.having,delete e.sort,delete e.limit,delete e.page,e},m=(t,e)=>t.operator&&e.get(t.operator).kind==="component"?!0:typeof t.pred=="object"&&m(t.pred,e)||(t.components?.some(i=>m(i,e))??!1),j=(t,e,i,o,n,r)=>{const s=t.preds??[],c=s.map((u,p)=>({pred:u,index:p,definition:u.operator?o.get(u.operator):void 0})).filter(({definition:u})=>u?.kind==="component");if(c.length===0)return;for(const u of s)if((!u.operator||o.get(u.operator).kind!=="component")&&(typeof u.pred=="object"&&m(u.pred,o)||(u.components?.some(p=>m(p,o))??!1)))throw new l("UNSUPPORTED_COMPONENT_NESTING","A component operator must currently be the top-level projection operator");const a=[],d=s.filter((u,p)=>!c.some(y=>y.index===p));if(d.length>0){const u=_(t);u.preds=d,a.push({kind:"query",logicform:u})}const g=t.groupby?.length??0;for(const{pred:u,index:p,definition:y}of c){const A=y?.createComponent;if(!A)continue;const S=e.projections[g+p]?.name;if(!S)throw new l("INVALID_COMPONENT_PLAN","Component output projection is missing");const C=r?.preds?.[p],h=A({logicform:t,pred:C??u,outputName:S,schema:i,resolveSchema:n,...r?{sourceLogicform:r}:{}});if(h.kind==="query"&&(h.logicform.preds??[]).some(E=>m(E,o)))throw new l("UNSUPPORTED_COMPONENT_NESTING","Nested component operators are not supported yet");a.push(h)}return a},f=t=>t instanceof Date?t.getTime():t,v=(t,e,i,o)=>{const n=f(t),r=f(i);switch(e){case"$eq":return n===r;case"$ne":return n!==r;case"$gt":return n!=null&&r!==void 0&&r!==null&&n>r;case"$gte":return n!=null&&r!==void 0&&r!==null&&n>=r;case"$lt":return n!=null&&r!==void 0&&r!==null&&n<r;case"$lte":return n!=null&&r!==void 0&&r!==null&&n<=r;case"$in":return Array.isArray(i)&&i.some(s=>f(s)===n);case"$nin":return Array.isArray(i)&&i.every(s=>f(s)!==n);case"$exists":return i?t!=null:t==null;case"$contains":return String(t).includes(String(i));case"$regex":return typeof i=="string"&&new RegExp(i,o).test(String(t));default:throw new l("UNSUPPORTED_QUERY_OPERATOR",`Unsupported having operator: ${e}`)}},$=(t,e)=>{if(!e||typeof e!="object"||Array.isArray(e))return f(t)===f(e);const i=Object.entries(e),o=e.args,n=o&&typeof o=="object"&&!Array.isArray(o)?o.options:void 0;return i.every(([r,s])=>{if(r==="args")return!0;if(r==="$and"||r==="$or"){if(!Array.isArray(s))return!1;const c=s.map(a=>$(t,a));return r==="$and"?c.every(Boolean):c.some(Boolean)}return v(t,r,s,typeof n=="string"?n:void 0)})},N=(t,e)=>Object.entries(e).every(([i,o])=>{if(i==="$and"||i==="$or"){if(!Array.isArray(o))return!1;const n=o.map(r=>N(t,r));return i==="$and"?n.every(Boolean):n.some(Boolean)}return $(t[i],o)}),O=(t,e)=>e.length===0?"0":t._id===void 0?b(t,e):R(t._id),w=(t,e)=>{const i=new Map;for(const o of t)for(const n of o){const r=O(n,e);i.set(r,{...i.get(r)??{},...n})}return[...i.values()]},B=(t,e)=>{const i=e.groupBy.map(({name:n})=>n);let o=w(t,i);if(e.having&&(o=o.filter(n=>N(n,e.having))),e.groupLimit){const n=e.groupLimit,r=new Map;for(const s of o){const c=O(s,n.partitionBy);r.set(c,[...r.get(c)??[],s])}o=[...r.values()].flatMap(s=>s.sort((c,a)=>{for(const d of n.orderBy){const g=f(c[d.name]),u=f(a[d.name]);if(g!==u)return g==null?d.direction==="ASC"?-1:1:u==null?d.direction==="ASC"?1:-1:(g<u?-1:1)*(d.direction==="ASC"?1:-1)}return 0}).slice(0,n.count))}if(e.orderBy.length>0&&o.sort((n,r)=>{for(const s of e.orderBy){const c=f(n[s.name]),a=f(r[s.name]);if(c!==a)return c==null?s.direction==="ASC"?-1:1:a==null?s.direction==="ASC"?1:-1:(c<a?-1:1)*(s.direction==="ASC"?1:-1)}return 0}),e.pagination?.kind==="rows"){const n=(e.pagination.page-1)*e.pagination.pageSize;o=o.slice(n,n+e.pagination.pageSize)}else if(e.pagination?.kind==="ratio"){const n=Math.ceil(o.length*e.pagination.ratio),r=(e.pagination.page-1)*n;o=o.slice(r,r+n)}return o};export{j as createExecutionUnits,w as mergeComponentRows,B as mergeExecutionResults};
@@ -1 +1 @@
1
- import{resolveQueryExpiration as d}from"../cache/expiry.js";import"node:crypto";import"../operators/index.js";import{isLogicalQuerySubquery as m,isLogicalRecursiveQuery as y}from"../planner/types.js";import{shortenSQL as g}from"../compiler/shorten-sql.js";const i=(e,t,r)=>{if(m(e)){t.push(e.plan);return}if(y(e)){r.set(`${e.schemaId}:${e.source}`,{schemaId:e.schemaId,source:e.source});return}if(Array.isArray(e)){for(const o of e)i(o,t,r);return}if(!(typeof e!="object"||e===null||e instanceof Date))for(const o of Object.values(e))i(o,t,r)},f=(e,t,r)=>{if(e.kind==="window"){f(e.input,t,r);for(const o of e.partitionBy)f(o,t,r);return}if(e.kind==="operator"){i(e.query,t,r),e.input&&f(e.input,t,r);for(const o of e.components??[])f(o,t,r)}},a=e=>{const t=new Map,r=c=>{t.set(`${c.schemaId}:${c.source}`,c)};if(e.composition)for(const c of e.composition)for(const s of a(c))r(s);else if(e.sourcePlan)for(const c of a(e.sourcePlan))r(c);else r({schemaId:e.schemaId,source:e.source});for(const c of e.joins??[])r({schemaId:c.targetSchemaId,source:c.targetSource});const o=[];i(e.query,o,t),i(e.having,o,t);for(const c of e.projections)f(c.expression,o,t);for(const c of o)for(const s of a(c))r(s);return[...t.values()]},w=e=>{const t=new Map;for(const r of e.flat())t.set(`${r.schemaId}:${r.source}`,r);return[...t.values()]},I=(e,t)=>{const r=[];for(const o of e){const c=t.findSource(o.schemaId,o.source);if(!c?.freshness)return;r.push(c.freshness)}return r},l=async(e,t,r)=>{e=u(e),r.signal?.throwIfAborted();const o=r.cache?I(t,r.schemaMappings):void 0;if(!r.cache||!o)return r.provider.execute({...e,...r.signal?{signal:r.signal}:{}});const c={namespace:r.cache.namespace,provider:r.provider.kind,sql:e.sql,parameters:e.parameters};try{const n=await r.cache.store.get(c);if(r.signal?.throwIfAborted(),n)return[...n]}catch{r.signal?.throwIfAborted()}const s=await r.provider.execute({...e,...r.signal?{signal:r.signal}:{}});try{const n=d(o,new Date);await r.cache.store.set(c,s,{sources:[...new Set(t.map(({source:h})=>h))],...n?{expiresAt:n}:{}})}catch{}return s},u=e=>({...e,sql:g(e.sql)});export{a as collectPlanSources,l as executeCompiledQuery,w as mergePhysicalSources,u as shortenCompiledQuery};
1
+ import{resolveQueryExpiration as d}from"../cache/expiry.js";import"node:crypto";import"../operators/index.js";import{isLogicalQuerySubquery as g,isLogicalRecursiveQuery as y}from"../planner/types.js";import{shortenSQL as w}from"../compiler/shorten-sql.js";const i=(r,t,e)=>{if(g(r)){t.push(r.plan);return}if(y(r)){e.set(`${r.schemaId}:${r.source}`,{schemaId:r.schemaId,source:r.source});return}if(Array.isArray(r)){for(const o of r)i(o,t,e);return}if(!(typeof r!="object"||r===null||r instanceof Date))for(const o of Object.values(r))i(o,t,e)},a=(r,t,e)=>{if(r.kind==="window"){a(r.input,t,e);for(const o of r.partitionBy)a(o,t,e);return}if(r.kind==="operator"){i(r.query,t,e),r.input&&a(r.input,t,e);for(const o of r.components??[])a(o,t,e)}},f=r=>{const t=new Map,e=c=>{t.set(`${c.schemaId}:${c.source}`,c)};if(r.composition)for(const c of r.composition)for(const s of f(c))e(s);else if(r.sourcePlan)for(const c of f(r.sourcePlan))e(c);else e({schemaId:r.schemaId,source:r.source});for(const c of r.joins??[])e({schemaId:c.targetSchemaId,source:c.targetSource});const o=[];i(r.query,o,t),i(r.having,o,t);for(const c of r.projections)a(c.expression,o,t);for(const c of o)for(const s of f(c))e(s);return[...t.values()]},I=r=>{const t=new Map;for(const e of r.flat())t.set(`${e.schemaId}:${e.source}`,e);return[...t.values()]},u=async(r,t)=>{try{return await r.execute(t)}catch(e){const o=e instanceof Error?e.message:String(e),c=`${r.kind} provider \u6267\u884C SQL \u4EA7\u751F\u9519\u8BEF\uFF1A`;throw e instanceof Error?(e.message.startsWith(c)||(e.message=`${c}${o}`),e):new Error(`${c}${o}`,{cause:e})}},l=(r,t)=>{const e=[];for(const o of r){const c=t.findSource(o.schemaId,o.source);if(!c?.freshness)return;e.push(c.freshness)}return e},S=async(r,t,e)=>{r=h(r),e.signal?.throwIfAborted();const o=e.cache?l(t,e.schemaMappings):void 0;if(!e.cache||!o)return u(e.provider,{...r,...e.signal?{signal:e.signal}:{}});const c={namespace:e.cache.namespace,provider:e.provider.kind,sql:r.sql,parameters:r.parameters};try{const n=await e.cache.store.get(c);if(e.signal?.throwIfAborted(),n)return[...n]}catch{e.signal?.throwIfAborted()}const s=await u(e.provider,{...r,...e.signal?{signal:e.signal}:{}});try{const n=d(o,new Date);await e.cache.store.set(c,s,{sources:[...new Set(t.map(({source:m})=>m))],...n?{expiresAt:n}:{}})}catch{}return s},h=r=>({...r,sql:w(r.sql)});export{f as collectPlanSources,S as executeCompiledQuery,I as mergePhysicalSources,h as shortenCompiledQuery};
@@ -1 +1 @@
1
- import{performance as ae}from"node:perf_hooks";import{SemanticDBError as S}from"../errors.js";import{compilePlan as Z}from"../compiler/compile-plan.js";import{compileCteCalculation as ie}from"../compiler/compile-cte.js";import{evaluateJavaScriptProjections as ee}from"./evaluate.js";import{createExecutionUnits as ce,mergeExecutionResults as fe,mergeComponentRows as ge}from"./components.js";import{fetchEntities as he}from"./fetch-entities.js";import{hydrateResult as we}from"./result-hydrator/hydrate-result.js";import{addRowIdentities as ye}from"./row-identity.js";import{mergePhysicalSources as me,collectPlanSources as Q,executeCompiledQuery as B,shortenCompiledQuery as Se}from"./execute-query.js";import{normalizeLogicform as G}from"../normalizer/normalize-logicform.js";import{assertSupportedLogicformVersion as ve}from"../normalizer/version.js";import{createOperatorRegistry as Re}from"../operators/index.js";import{buildLineage as Ee}from"../planner/build-lineage.js";import{createLogicalPlan as z,createResultSchema as Pe,createDerivedSourceSchema as De}from"../planner/create-logical-plan.js";import{getSchema as Ie}from"../schema/schema-registry.js";import{createSemanticTypeRegistry as Ne}from"../semantic-types/index.js";import{validatePermissions as _e,applyPermissions as oe,isColumnAllowed as V,getSchemaPermissions as Ce}from"../permissions/permissions.js";import{getConnectionProvider as Oe}from"./connection-registry.js";import{loadCustomFunctions as Ae}from"../custom-functions/custom-functions.js";import{registerCustomFunctionOperators as Le}from"../custom-functions/operator-adapter.js";import{assertNoLegacyLogicformFields as qe,splitLegacySchemas as Te,normalizeLegacyLogicform as je}from"../compatibility/logicform.js";import{normalizeSchemas as Ue}from"./compatibility.js";import{OperatorRegistry as Fe}from"../operators/registry.js";import{resolveSchema as Me}from"../semantic-types/resolve-schema.js";const le=n=>n instanceof Error?n:new Error(String(n)),xe=n=>n.stack?`${n.message} [stack: ${n.stack}]`:n.message,Ke=(n,i,s={})=>{const a=le(n);return{errorCode:"100",error:xe(a),result:[],...s.schema?{schema:s.schema}:{},normedLogicform:s.normedLogicform??i,sqls:s.sqls??[],...s.provider?{provider:s.provider}:{},...s.lineage?{lineage:s.lineage}:{}}},$e=async(n,i,s)=>{await n?.({...i,partialResult:s})},be=(n,i,s)=>({type:s,sql:n.sql,parameters:[...n.parameters],displaySql:i.displayQuery(n.sql,n.parameters)}),pe=(n,i)=>{if(i.connectionId)for(const s of Q(n)){const a=i.schemaMappings.findSource(s.schemaId,s.source);if(!a)throw new S("SCHEMA_MAPPING_NOT_FOUND",`Schema mapping is not registered: ${s.schemaId}:${s.source}`,{schemaId:s.schemaId,source:s.source});if(a.connectionId!==i.connectionId)throw new S("CROSS_CONNECTION_QUERY_UNSUPPORTED",`One physical query cannot use multiple database connections: ${i.connectionId}, ${a.connectionId}`,{schemaId:s.schemaId,source:s.source,connectionId:a.connectionId})}},ke=n=>{const i=new Set,s=a=>{a.schema&&i.add(a.schema),a.from&&s(a.from);for(const P of a.logicforms??[])s(P)};return s(n),[...i]},de=async(n,i)=>{const s=i.plugins??[],a=n.version,P=typeof a=="string"&&(/^2\./.test(a)||/^[3-9]\d*\./.test(a)),N=a===void 0||typeof a=="string"&&/^1\./.test(a);P&&qe(n);const v=N?Te(je(n),i.schemas):n,e={...i,schemas:Ue(i.schemas,s,N)},f=ae.now(),D=e.semanticTypes??Ne(),c=e.operators?new Fe(e.operators.definitions()):Re(s);if(e.operators)for(const t of s)for(const r of t.operators??[])if(c.has(r.name)){if(c.get(r.name)!==r)throw new S("PLUGIN_OPERATOR_ALREADY_REGISTERED",`Plugin Operator is already registered: ${r.name}`,{operator:r.name,plugin:t.name})}else c.register(r);e.customFunctions&&Le(c,{schemas:e.schemas,customFunctions:e.customFunctions,definitions:Ae(e.customFunctions),config:{...e.customFunctionConfig??{},...e.permissions?{permissions:e.permissions}:{}},...e.permissions?{permissions:e.permissions}:{},...e.locale?{locale:e.locale}:{}});const L=e.schemaMappings,se=new Map;let q={provider:e.provider.kind};const O=[],T=(t,r)=>{O.push(be(Se(t),e.provider,r)),q={...q,sqls:[...O]}},w=async(t,r={})=>{q={...q,...r},await $e(e.onProgress,t,q)},te=t=>Me(Ie(t,e.schemas),D,s),Y=new Map,g=t=>{const r=Y.get(t);return r||(Ce(e.permissions,t),te(t))},j=t=>{if(t.schema)return g(t.schema);if(t.from)return j(t.from);const r=t.logicforms?.[0];if(r)return j(r);throw new S("LOGICFORM_SCHEMA_REQUIRED","A LogicForm must contain a physical schema leaf")},x={resolveSchema:g,canUsePropertyRowIdentity:(t,r)=>V(e.permissions,t.id,r)},H=(t,r)=>{const h=De(z(t,j(t),c,L,x),r);return Y.set(h.id,h),h},J=async(t,r)=>{const h=`${t.id}:${r}`,K=se.get(h);if(K)return K;const d=(async()=>{const W=oe({version:v.version,schema:t.id,preds:[{operator:"$max",pred:r,name:"__latestDataDate__"}]},e.permissions,g,!1,new Set,s),A=await G(W,t,{semanticTypes:D,operators:c,plugins:s,...e.locale?{locale:e.locale}:{},resolveSchema:g}),_=z(A,t,c,L,x);pe(_,e);const U=Z(_,e.provider,c);T(U,"semantic-resolution");const I=(await B(U,Q(_),e))[0],y=I?.__latestDataDate__??Object.values(I??{})[0];if(y==null||!(y instanceof Date)&&typeof y!="string"&&typeof y!="number")return;const $=y instanceof Date?y:new Date(y);if(!Number.isNaN($.getTime()))return $})();return se.set(h,d),d};try{e.signal?.throwIfAborted(),ve(v),await w({stage:"started",logicform:v}),_e(e.permissions,te);const t=j(v);await w({stage:"validated",schema:t});const r=oe(v,e.permissions,g,!0,new Set,s),h=await G(r,t,{semanticTypes:D,operators:c,plugins:s,...e.locale?{locale:e.locale}:{},resolveSchema:g,resolveFromSchema:H,canProjectProperty:(o,l)=>V(e.permissions,o.id,l),requestLatestDataDate:(o,l)=>J(o,l.id)}),K=e.customFunctions?await G(v,t,{semanticTypes:D,operators:c,plugins:s,...e.locale?{locale:e.locale}:{},resolveSchema:g,resolveFromSchema:H,canProjectProperty:(o,l)=>V(e.permissions,o.id,l),requestLatestDataDate:(o,l)=>J(o,l.id)}):void 0;await w({stage:"normalized",normedLogicform:h},{normedLogicform:h});const d=z(h,t,c,L,x);pe(d,e);const W=d.columnProperties??[],A=Pe(d,t);Y.set(A.id,A),await w({stage:"planned",plan:d},{schema:A});const _=Ee(d,t,e.provider,c);await w({stage:"lineage",lineage:_},{lineage:_});const U=ce(h,d,t,c,g,K),F=async o=>{const l=j(o.logicform),R=o.prepare?await G(oe(o.logicform,e.permissions,g,!0,new Set,s),l,{semanticTypes:D,operators:c,plugins:s,...e.locale?{locale:e.locale}:{},resolveSchema:g,resolveFromSchema:H,canProjectProperty:(m,E)=>V(e.permissions,m.id,E),requestLatestDataDate:(m,E)=>J(m,E.id)}):o.logicform,p=z(R,l,c,L,x),C=ce(R,p,l,c,g,o.logicform);if(C){if(C.length!==1)throw new Error("A generated component must produce exactly one execution unit");const m=C[0];if(m.kind==="query"){const u=await F(m),b=u.component.mapResult,k=o.mapResult;return{...u,component:{...o,logicform:u.component.logicform,...b||k?{mapResult:ne=>{const re=b?.(ne)??[...ne];return k?.(re)??[...re]}}:{}}}}if(m.kind==="js")throw new S("UNSUPPORTED_NESTED_JS_COMPONENT","A JavaScript composite metric cannot currently be nested as a generated component");const E=await Promise.all(m.components.map(async u=>({name:u.name,prepared:await F(u.component),...u.component.groupKeyShifts?{groupKeyShifts:u.component.groupKeyShifts}:{},...u.joinKeys?{joinKeys:u.joinKeys}:{}}))),M=ie(m,E.map(({name:u,prepared:b,...k})=>({name:u,plan:b.plan,...k})),p,e.provider,c);return{component:{...o,logicform:R},plan:p,compiled:M,sources:me(E.map(({prepared:u})=>u.sources))}}return{component:{...o,logicform:R},plan:p,compiled:Z(p,e.provider,c),sources:Q(p)}},I=U?await Promise.all(U.map(async o=>{if(o.kind==="query")return{kind:"query",unit:o,...await F(o)};const l=await Promise.all(o.components.map(async p=>({name:p.name,prepared:await F(p.component),...p.component.groupKeyShifts?{groupKeyShifts:p.component.groupKeyShifts}:{},...p.joinKeys?{joinKeys:p.joinKeys}:{}})));if(o.kind==="js")return{kind:"js",unit:o,components:l.map(({prepared:p})=>p)};const R=l.map(({name:p,prepared:C,...m})=>({name:p,plan:C.plan,...m}));return{kind:"cte",unit:o,plan:d,compiled:ie(o,R,d,e.provider,c),sources:me(l.map(({prepared:p})=>p.sources))}})):void 0,y=I?void 0:Z(d,e.provider,c);if(I)for(const o of I)if(o.kind==="js")for(const l of o.components)T(l.compiled,"computation");else T(o.compiled,"computation");else T(y,"computation");await w({stage:"compiled",sqls:[...O]},{sqls:[...O]}),await w({stage:"executing",provider:e.provider.kind});const $=I?fe(await Promise.all(I.map(async o=>{if(o.kind==="js"){const p=await Promise.all(o.components.map(async m=>{const E=await B(m.compiled,m.sources,e),M=await ee(E,m.plan.projections,c);return m.component.mapResult?.(M)??M})),C=o.unit.components.map(({name:m})=>m);return Promise.all(ge(p,d.groupBy.map(({name:m})=>m)).map(async m=>{const E=await o.unit.mergeRow(m);return{...o.unit.exposeComponents?m:Object.fromEntries(Object.entries(m).filter(([u])=>!C.includes(u))),[o.unit.outputName]:E}}))}const l=await B(o.compiled,o.sources,e),R=o.kind==="query"?await ee(l,o.plan.projections,c):l;return o.kind==="query"?o.unit.mapResult?.(R)??R:R})),d):await(async()=>{const o=await B(y,Q(d),e);return ee(o,d.projections,c)})();await w({stage:"hydrating"});const ue=await we($,W,{semanticTypes:D,resolveSchema:g,fetchEntities:o=>he(o,{version:h.version,operators:c,schemaMappings:L,semanticTypes:D,plugins:s,...e.locale?{locale:e.locale}:{},resolveSchema:g,...e.permissions?{permissions:e.permissions}:{},provider:e.provider,...e.cache?{cache:e.cache}:{},...e.signal?{signal:e.signal}:{},onCompiled:l=>T(l,"semantic-resolution")})}),X=ye(ue,d.rowIdentity);return await w({stage:"result",result:X},{result:X,sqls:[...O]}),await w({stage:"completed",durationMs:ae.now()-f}),{result:X,schema:A,normedLogicform:h,sqls:[...O],lineage:_,provider:e.provider.kind}}catch(t){const r=le(t);try{await w({stage:"failed",error:r})}catch{}throw r}},Qe=async(n,i)=>{let s={};try{if(i.connections.length===0)throw new S("DATABASE_CONNECTION_REQUIRED","At least one database connection is required");const a=new Map;for(const f of i.connections){if(f.id.trim().length===0)throw new S("INVALID_DATABASE_CONNECTION","Database connection id must not be empty");if(a.has(f.id))throw new S("DATABASE_CONNECTION_ALREADY_REGISTERED",`Database connection id is duplicated: ${f.id}`,{connectionId:f.id});a.set(f.id,f)}const P=new Set;for(const f of ke(n))for(const D of i.schemaMappings.list(f))P.add(D.connectionId);if(P.size>1)throw new S("CROSS_CONNECTION_QUERY_UNSUPPORTED",`One LogicForm cannot compile into SQL for multiple database connections: ${[...P].join(", ")}`,{connectionIds:[...P]});const[N]=P;if(!N)throw new S("LOGICFORM_SCHEMA_REQUIRED","A LogicForm must contain at least one physical Schema leaf");const v=a.get(N);if(!v)throw new S("DATABASE_CONNECTION_NOT_FOUND",`Database connection is not registered: ${N}`,{connectionId:N});const e=Oe(v);return s={provider:e.kind},await de(n,{...i,provider:e,connectionId:v.id,onProgress:async f=>{s=f.partialResult,await i.onProgress?.(f)}})}catch(a){return Ke(a,n,s)}};export{Qe as execute,de as executeWithProvider};
1
+ import{performance as ae}from"node:perf_hooks";import{SemanticDBError as v}from"../errors.js";import{compilePlan as Z}from"../compiler/compile-plan.js";import{compileCteCalculation as ie}from"../compiler/compile-cte.js";import{evaluateJavaScriptProjections as ee}from"./evaluate.js";import{createExecutionUnits as ce,mergeExecutionResults as fe,mergeComponentRows as ge}from"./components.js";import{fetchEntities as he}from"./fetch-entities.js";import{hydrateResult as we}from"./result-hydrator/hydrate-result.js";import{addRowIdentities as ye}from"./row-identity.js";import{mergePhysicalSources as me,collectPlanSources as Q,executeCompiledQuery as k,shortenCompiledQuery as Se}from"./execute-query.js";import{normalizeLogicform as B}from"../normalizer/normalize-logicform.js";import{assertSupportedLogicformVersion as ve}from"../normalizer/version.js";import{createOperatorRegistry as Ee}from"../operators/index.js";import{buildLineage as Re}from"../planner/build-lineage.js";import{createLogicalPlan as G,createResultSchema as Pe,createDerivedSourceSchema as Ne}from"../planner/create-logical-plan.js";import{getSchema as Ce}from"../schema/schema-registry.js";import{createSemanticTypeRegistry as De}from"../semantic-types/index.js";import{validatePermissions as Ie,applyPermissions as oe,isColumnAllowed as W,getSchemaPermissions as _e}from"../permissions/permissions.js";import{getConnectionProvider as Le}from"./connection-registry.js";import{loadCustomFunctions as Oe}from"../custom-functions/custom-functions.js";import{registerCustomFunctionOperators as Ae}from"../custom-functions/operator-adapter.js";import{assertNoLegacyLogicformFields as qe,splitLegacySchemas as Te,normalizeLegacyLogicform as je}from"../compatibility/logicform.js";import{normalizeSchemas as Ue}from"./compatibility.js";import{OperatorRegistry as Me}from"../operators/registry.js";import{resolveSchema as xe}from"../semantic-types/resolve-schema.js";const le=n=>n instanceof Error?n:new Error(String(n)),Fe=(n,a="zh-CN")=>n instanceof v?n.localizedMessage(a):n.message,be=(n,a,t={},c="zh-CN")=>{const g=le(n);return{errorCode:"100",error:Fe(g,c),result:[],...t.schema?{schema:t.schema}:{},normedLogicform:t.normedLogicform??a,sqls:t.sqls??[],...t.provider?{provider:t.provider}:{},...t.lineage?{lineage:t.lineage}:{}}},Ke=async(n,a,t)=>{await n?.({...a,partialResult:t})},$e=(n,a,t,c,g,y=t)=>({type:t,explain:y==="latest-date"?g.toLowerCase().startsWith("en")?"Get latest date from database":"\u83B7\u53D6\u6570\u636E\u5E93\u6700\u65B0\u65E5\u671F":y==="entity-expansion"?g.toLowerCase().startsWith("en")?"Entity expansion":"\u5B9E\u4F53\u5C55\u5F00":y==="computation"?g.toLowerCase().startsWith("en")?"Numeric computation":"\u6570\u503C\u8BA1\u7B97":g.toLowerCase().startsWith("en")?"Semantic resolution":"\u8BED\u4E49\u89E3\u6790",logicform:c,sql:n.sql,parameters:[...n.parameters],displaySql:a.displayQuery(n.sql,n.parameters)}),pe=(n,a)=>{if(a.connectionId)for(const t of Q(n)){const c=a.schemaMappings.findSource(t.schemaId,t.source);if(!c)throw new v("SCHEMA_MAPPING_NOT_FOUND",`Schema mapping is not registered: ${t.schemaId}:${t.source}`,{schemaId:t.schemaId,source:t.source});if(c.connectionId!==a.connectionId)throw new v("CROSS_CONNECTION_QUERY_UNSUPPORTED",`One physical query cannot use multiple database connections: ${a.connectionId}, ${c.connectionId}`,{schemaId:t.schemaId,source:t.source,connectionId:c.connectionId})}},ze=n=>{const a=new Set,t=c=>{c.schema&&a.add(c.schema),c.from&&t(c.from);for(const g of c.logicforms??[])t(g)};return t(n),[...a]},de=async(n,a)=>{const t=a.plugins??[],c=n.version,g=typeof c=="string"&&(/^2\./.test(c)||/^[3-9]\d*\./.test(c)),y=c===void 0||typeof c=="string"&&/^1\./.test(c);g&&qe(n);const E=y?Te(je(n),a.schemas):n,e={...a,schemas:Ue(a.schemas,t,y)},F=ae.now(),d=e.semanticTypes??De(),i=e.operators?new Me(e.operators.definitions()):Ee(t);if(e.operators)for(const s of t)for(const r of s.operators??[])if(i.has(r.name)){if(i.get(r.name)!==r)throw new v("PLUGIN_OPERATOR_ALREADY_REGISTERED",`Plugin Operator is already registered: ${r.name}`,{operator:r.name,plugin:s.name})}else i.register(r);e.customFunctions&&Ae(i,{schemas:e.schemas,customFunctions:e.customFunctions,definitions:Oe(e.customFunctions),config:{...e.customFunctionConfig??{},...e.permissions?{permissions:e.permissions}:{}},...e.permissions?{permissions:e.permissions}:{},...e.locale?{locale:e.locale}:{}});const A=e.schemaMappings,te=new Map;let q={provider:e.provider.kind};const L=[],T=(s,r,u,O=r)=>{L.push($e(Se(s),e.provider,r,u,e.locale??"zh-CN",O)),q={...q,sqls:[...L]}},R=async(s,r={})=>{q={...q,...r},await Ke(e.onProgress,s,q)},se=s=>xe(Ce(s,e.schemas),d,t),V=new Map,w=s=>{const r=V.get(s);return r||(_e(e.permissions,s),se(s))},j=s=>{if(s.schema)return w(s.schema);if(s.from)return j(s.from);const r=s.logicforms?.[0];if(r)return j(r);throw new v("LOGICFORM_SCHEMA_REQUIRED","A LogicForm must contain a physical schema leaf")},b={resolveSchema:w,canUsePropertyRowIdentity:(s,r)=>W(e.permissions,s.id,r)},Y=(s,r)=>{const u=Ne(G(s,j(s),i,A,b),r);return V.set(u.id,u),u},H=async(s,r)=>{const u=`${s.id}:${r}`,O=te.get(u);if(O)return O;const f=(async()=>{const J=oe({version:E.version,schema:s.id,preds:[{operator:"$max",pred:r,name:"__latestDataDate__"}]},e.permissions,w,!1,new Set,t),D=await B(J,s,{semanticTypes:d,operators:i,plugins:t,...e.locale?{locale:e.locale}:{},resolveSchema:w}),I=G(D,s,i,A,b);pe(I,e);const U=Z(I,e.provider,i);T(U,"semantic-resolution",D,"latest-date");const C=(await k(U,Q(I),e))[0],P=C?.__latestDataDate__??Object.values(C??{})[0];if(P==null||!(P instanceof Date)&&typeof P!="string"&&typeof P!="number")return;const K=P instanceof Date?P:new Date(P);if(!Number.isNaN(K.getTime()))return K})();return te.set(u,f),f};try{e.signal?.throwIfAborted(),ve(E),await R({stage:"started",logicform:E}),Ie(e.permissions,se);const s=j(E);await R({stage:"validated",schema:s});const r=oe(E,e.permissions,w,!0,new Set,t),u=await B(r,s,{semanticTypes:d,operators:i,plugins:t,...e.locale?{locale:e.locale}:{},resolveSchema:w,resolveFromSchema:Y,canProjectProperty:(o,l)=>W(e.permissions,o.id,l),requestLatestDataDate:(o,l)=>H(o,l.id)}),O=e.customFunctions?await B(E,s,{semanticTypes:d,operators:i,plugins:t,...e.locale?{locale:e.locale}:{},resolveSchema:w,resolveFromSchema:Y,canProjectProperty:(o,l)=>W(e.permissions,o.id,l),requestLatestDataDate:(o,l)=>H(o,l.id)}):void 0;await R({stage:"normalized",normedLogicform:u},{normedLogicform:u});const f=G(u,s,i,A,b);pe(f,e);const J=f.columnProperties??[],D=Pe(f,s);V.set(D.id,D),await R({stage:"planned",plan:f},{schema:D});const I=Re(f,s,e.provider,i);await R({stage:"lineage",lineage:I},{lineage:I});const U=ce(u,f,s,i,w,O),M=async o=>{const l=j(o.logicform),S=o.prepare?await B(oe(o.logicform,e.permissions,w,!0,new Set,t),l,{semanticTypes:d,operators:i,plugins:t,...e.locale?{locale:e.locale}:{},resolveSchema:w,resolveFromSchema:Y,canProjectProperty:(m,N)=>W(e.permissions,m.id,N),requestLatestDataDate:(m,N)=>H(m,N.id)}):o.logicform,p=G(S,l,i,A,b),_=ce(S,p,l,i,w,o.logicform);if(_){if(_.length!==1)throw new Error("A generated component must produce exactly one execution unit");const m=_[0];if(m.kind==="query"){const h=await M(m),$=h.component.mapResult,z=o.mapResult;return{...h,component:{...o,logicform:h.component.logicform,...$||z?{mapResult:ne=>{const re=$?.(ne)??[...ne];return z?.(re)??[...re]}}:{}}}}if(m.kind==="js")throw new v("UNSUPPORTED_NESTED_JS_COMPONENT","A JavaScript composite metric cannot currently be nested as a generated component");const N=await Promise.all(m.components.map(async h=>({name:h.name,prepared:await M(h.component),...h.component.groupKeyShifts?{groupKeyShifts:h.component.groupKeyShifts}:{},...h.joinKeys?{joinKeys:h.joinKeys}:{}}))),x=ie(m,N.map(({name:h,prepared:$,...z})=>({name:h,plan:$.plan,...z})),p,e.provider,i);return{component:{...o,logicform:S},plan:p,compiled:x,sources:me(N.map(({prepared:h})=>h.sources))}}return{component:{...o,logicform:S},plan:p,compiled:Z(p,e.provider,i),sources:Q(p)}},C=U?await Promise.all(U.map(async o=>{if(o.kind==="query")return{kind:"query",unit:o,...await M(o)};const l=await Promise.all(o.components.map(async p=>({name:p.name,prepared:await M(p.component),...p.component.groupKeyShifts?{groupKeyShifts:p.component.groupKeyShifts}:{},...p.joinKeys?{joinKeys:p.joinKeys}:{}})));if(o.kind==="js")return{kind:"js",unit:o,components:l.map(({prepared:p})=>p)};const S=l.map(({name:p,prepared:_,...m})=>({name:p,plan:_.plan,...m}));return{kind:"cte",unit:o,plan:f,compiled:ie(o,S,f,e.provider,i),sources:me(l.map(({prepared:p})=>p.sources))}})):void 0,P=C?void 0:Z(f,e.provider,i);if(C)for(const o of C)if(o.kind==="js")for(const l of o.components)T(l.compiled,"computation",l.component.logicform);else T(o.compiled,"computation",u);else T(P,"computation",u);await R({stage:"compiled",sqls:[...L]},{sqls:[...L]}),await R({stage:"executing",provider:e.provider.kind});const K=C?fe(await Promise.all(C.map(async o=>{if(o.kind==="js"){const p=await Promise.all(o.components.map(async m=>{const N=await k(m.compiled,m.sources,e),x=await ee(N,m.plan.projections,i);return m.component.mapResult?.(x)??x})),_=o.unit.components.map(({name:m})=>m);return Promise.all(ge(p,f.groupBy.map(({name:m})=>m)).map(async m=>{const N=await o.unit.mergeRow(m);return{...o.unit.exposeComponents?m:Object.fromEntries(Object.entries(m).filter(([h])=>!_.includes(h))),[o.unit.outputName]:N}}))}const l=await k(o.compiled,o.sources,e),S=o.kind==="query"?await ee(l,o.plan.projections,i):l;return o.kind==="query"?o.unit.mapResult?.(S)??S:S})),f):await(async()=>{const o=await k(P,Q(f),e);return ee(o,f.projections,i)})();await R({stage:"hydrating"});const ue=await we(K,J,{semanticTypes:d,resolveSchema:w,fetchEntities:o=>he(o,{version:u.version,operators:i,schemaMappings:A,semanticTypes:d,plugins:t,...e.locale?{locale:e.locale}:{},resolveSchema:w,...e.permissions?{permissions:e.permissions}:{},provider:e.provider,...e.cache?{cache:e.cache}:{},...e.signal?{signal:e.signal}:{},onCompiled:(l,S)=>T(l,"semantic-resolution",S,"entity-expansion")})}),X=ye(ue,f.rowIdentity);return await R({stage:"result",result:X},{result:X,sqls:[...L]}),await R({stage:"completed",durationMs:ae.now()-F}),{result:X,schema:D,normedLogicform:u,sqls:[...L],lineage:I,provider:e.provider.kind}}catch(s){const r=le(s);try{await R({stage:"failed",error:r})}catch{}throw r}},Qe=async(n,a)=>{let t={};const c=a.locale??"zh-CN";try{if(a.connections.length===0)throw new v("DATABASE_CONNECTION_REQUIRED","At least one database connection is required");const g=new Map;for(const d of a.connections){if(d.id.trim().length===0)throw new v("INVALID_DATABASE_CONNECTION","Database connection id must not be empty");if(g.has(d.id))throw new v("DATABASE_CONNECTION_ALREADY_REGISTERED",`Database connection id is duplicated: ${d.id}`,{connectionId:d.id});g.set(d.id,d)}const y=new Set;for(const d of ze(n))for(const i of a.schemaMappings.list(d))y.add(i.connectionId);if(y.size>1)throw new v("CROSS_CONNECTION_QUERY_UNSUPPORTED",`One LogicForm cannot compile into SQL for multiple database connections: ${[...y].join(", ")}`,{connectionIds:[...y]});const[E]=y;if(!E)throw new v("LOGICFORM_SCHEMA_REQUIRED","A LogicForm must contain at least one physical Schema leaf");const e=g.get(E);if(!e)throw new v("DATABASE_CONNECTION_NOT_FOUND",`Database connection is not registered: ${E}`,{connectionId:E});const F=Le(e);return t={provider:F.kind},await de(n,{...a,locale:c,provider:F,connectionId:e.id,onProgress:async d=>{t=d.partialResult,await a.onProgress?.(d)}})}catch(g){return be(g,n,t,c)}};export{Qe as execute,de as executeWithProvider};
@@ -1,3 +1,4 @@
1
+ import type { LogicformType } from '../logicform-types';
1
2
  import type { OperatorRegistry } from '../operators/index';
2
3
  import type { DataRow } from '../physical/index';
3
4
  import type { SchemaMappingRegistry } from '../schema-mappings/index';
@@ -20,7 +21,7 @@ interface FetchEntityOptions {
20
21
  readonly provider: CoreComputeProvider;
21
22
  readonly cache?: QueryCacheExecutionOptions;
22
23
  readonly signal?: AbortSignal;
23
- readonly onCompiled?: (compiled: CompiledQuery) => void;
24
+ readonly onCompiled?: (compiled: CompiledQuery, logicform: LogicformType) => void;
24
25
  }
25
26
  export declare const fetchEntities: (request: EntityFetchRequest, options: FetchEntityOptions) => Promise<DataRow[]>;
26
27
  export {};
@@ -1 +1 @@
1
- import{compilePlan as h}from"../compiler/compile-plan.js";import"../operators/index.js";import{SemanticDBError as d}from"../errors.js";import{createLogicalPlan as y}from"../planner/create-logical-plan.js";import{normalizeLogicform as f}from"../normalizer/normalize-logicform.js";import{isColumnAllowed as l,applyPermissions as g}from"../permissions/permissions.js";import{addRowIdentities as v}from"./row-identity.js";import{executeCompiledQuery as u,collectPlanSources as w}from"./execute-query.js";const P=r=>{if(r.every(e=>typeof e=="string"))return[...r];if(r.every(e=>typeof e=="number"))return[...r];if(r.every(e=>typeof e=="boolean"))return[...r];throw new TypeError("Entity IDs must have one scalar type")},S=(r,e,a)=>{const c=P(e);if(!a||c.length<=a)return{[r]:{$in:c}};const n=[];for(let m=0;m<c.length;m+=a)n.push({[r]:{$in:c.slice(m,m+a)}});return{$or:n}},E=async(r,e)=>{if(!l(e.permissions,r.schema.id,r.idProperty))throw new d("COLUMN_PERMISSION_DENIED",`Entity hydration requires the ID property in the column permissions: ${r.schema.id}.${r.idProperty.name}`,{schemaId:r.schema.id,property:r.idProperty.name});const a=r.schema.properties.filter(i=>l(e.permissions,r.schema.id,i)),c={version:e.version,schema:r.schema.id,query:S(r.idProperty.name,r.ids,e.provider.maxInListItems),preds:a.map(i=>({pred:i.name,name:i.name}))},n=g(c,e.permissions,e.resolveSchema,!0,new Set,e.plugins),m=await f(n,r.schema,{semanticTypes:e.semanticTypes,operators:e.operators,...e.plugins?{plugins:e.plugins}:{},...e.locale?{locale:e.locale}:{},resolveSchema:e.resolveSchema}),o=y(m,r.schema,e.operators,e.schemaMappings,{resolveSchema:e.resolveSchema,canUsePropertyRowIdentity:(i,p)=>l(e.permissions,i.id,p)}),s=h(o,e.provider,e.operators);e.onCompiled?.(s);const t=await u(s,w(o),{provider:e.provider,schemaMappings:e.schemaMappings,...e.cache?{cache:e.cache}:{},...e.signal?{signal:e.signal}:{}});return v(t,o.rowIdentity)};export{E as fetchEntities};
1
+ import{compilePlan as h}from"../compiler/compile-plan.js";import"../operators/index.js";import{SemanticDBError as d}from"../errors.js";import{createLogicalPlan as y}from"../planner/create-logical-plan.js";import{normalizeLogicform as f}from"../normalizer/normalize-logicform.js";import{isColumnAllowed as l,applyPermissions as g}from"../permissions/permissions.js";import{addRowIdentities as v}from"./row-identity.js";import{executeCompiledQuery as u,collectPlanSources as w}from"./execute-query.js";const P=r=>{if(r.every(e=>typeof e=="string"))return[...r];if(r.every(e=>typeof e=="number"))return[...r];if(r.every(e=>typeof e=="boolean"))return[...r];throw new TypeError("Entity IDs must have one scalar type")},S=(r,e,c)=>{const m=P(e);if(!c||m.length<=c)return{[r]:{$in:m}};const n=[];for(let a=0;a<m.length;a+=c)n.push({[r]:{$in:m.slice(a,a+c)}});return{$or:n}},E=async(r,e)=>{if(!l(e.permissions,r.schema.id,r.idProperty))throw new d("COLUMN_PERMISSION_DENIED",`Entity hydration requires the ID property in the column permissions: ${r.schema.id}.${r.idProperty.name}`,{schemaId:r.schema.id,property:r.idProperty.name});const c=r.schema.properties.filter(i=>l(e.permissions,r.schema.id,i)),m={version:e.version,schema:r.schema.id,query:S(r.idProperty.name,r.ids,e.provider.maxInListItems),preds:c.map(i=>({pred:i.name,name:i.name}))},n=g(m,e.permissions,e.resolveSchema,!0,new Set,e.plugins),a=await f(n,r.schema,{semanticTypes:e.semanticTypes,operators:e.operators,...e.plugins?{plugins:e.plugins}:{},...e.locale?{locale:e.locale}:{},resolveSchema:e.resolveSchema}),o=y(a,r.schema,e.operators,e.schemaMappings,{resolveSchema:e.resolveSchema,canUsePropertyRowIdentity:(i,p)=>l(e.permissions,i.id,p)}),s=h(o,e.provider,e.operators);e.onCompiled?.(s,a);const t=await u(s,w(o),{provider:e.provider,schemaMappings:e.schemaMappings,...e.cache?{cache:e.cache}:{},...e.signal?{signal:e.signal}:{}});return v(t,o.rowIdentity)};export{E as fetchEntities};
@@ -1 +1 @@
1
- import{SemanticDBError as m}from"../../errors.js";import{createMissingEntity as u}from"./missing-entity.js";const y=(r,t)=>`${r}:${String(t)}`,w=r=>{const t=r.properties.filter(({type:e})=>e==="ID"),i=t[0];if(t.length!==1||!i)throw new m("RELATION_TARGET_ID_REQUIRED",`Relation target ${r.id} must contain exactly one ID property`,{schemaId:r.id,count:t.length});return i};class P{#s;#o=new Map;#t=new Map;#e=new Map;#i=!1;constructor(t){this.#s=t}load=(t,i)=>{const e=y(t,i),n=this.#o.get(e);if(n)return Promise.resolve(n);const s=this.#t.get(e);if(s)return s;const o=this.#s.resolveSchema(t),a=w(o);let d,l;const h=new Promise((c,p)=>{d=c,l=p}),f=this.#e.get(t)??{schema:o,idProperty:a,loads:new Map};return f.loads.set(e,{id:i,resolve:c=>d?.(c),reject:c=>l?.(c)}),this.#e.set(t,f),this.#t.set(e,h),this.#n(),h};#n(){this.#i||(this.#i=!0,queueMicrotask(()=>{this.#r()}))}async#r(){this.#i=!1;const t=this.#e;this.#e=new Map,await Promise.all([...t.values()].map(i=>this.#c(i)))}async#c(t){const i={schema:t.schema,idProperty:t.idProperty,ids:[...t.loads.values()].map(({id:e})=>e)};try{const e=await this.#s.fetchEntities(i),n=new Map;for(const s of e){const o=s[t.idProperty.name]??s._id;o!=null&&n.set(y(t.schema.id,o),s)}for(const[s,o]of t.loads){const a=n.get(s)??u(t.schema,t.idProperty,o.id);this.#o.set(s,a),this.#t.delete(s),o.resolve(a)}}catch(e){for(const[n,s]of t.loads)this.#t.delete(n),s.reject(e)}}}export{P as EntityLoader};
1
+ import{SemanticDBError as m}from"../../errors.js";import{createMissingEntity as u}from"./missing-entity.js";const y=(c,t)=>`${c}:${String(t)}`,w=c=>{const t=c.properties.filter(({type:i})=>i==="ID"),o=t[0];if(t.length!==1||!o)throw new m("RELATION_TARGET_ID_REQUIRED",`Relation target ${c.id} must contain exactly one ID property`,{schemaId:c.id,count:t.length});return o};class g{#s;#o=new Map;#t=new Map;#e=new Map;#i=!1;constructor(t){this.#s=t}load=(t,o)=>{const i=y(t,o),n=this.#o.get(i);if(n)return Promise.resolve(n);const s=this.#t.get(i);if(s)return s;const e=this.#s.resolveSchema(t),r=w(e);let d,l;const h=new Promise((a,p)=>{d=a,l=p}),f=this.#e.get(t)??{schema:e,idProperty:r,loads:new Map};return f.loads.set(i,{id:o,resolve:a=>d?.(a),reject:a=>l?.(a)}),this.#e.set(t,f),this.#t.set(i,h),this.#n(),h};#n(){this.#i||(this.#i=!0,queueMicrotask(()=>{this.#r()}))}async#r(){this.#i=!1;const t=this.#e;this.#e=new Map,await Promise.all([...t.values()].map(o=>this.#c(o)))}async#c(t){const o={schema:t.schema,idProperty:t.idProperty,ids:[...t.loads.values()].map(({id:i})=>i)};try{const i=await this.#s.fetchEntities(o),n=new Map;for(const s of i){const e=s[t.idProperty.name]??s._id;if(e!=null){const r=typeof e=="string"?e:typeof e=="number"||typeof e=="boolean"||typeof e=="bigint"?String(e):JSON.stringify(e),d=s._id===r?s:{...s,_id:r};n.set(y(t.schema.id,e),d)}}for(const[s,e]of t.loads){const r=n.get(s)??u(t.schema,t.idProperty,e.id);this.#o.set(s,r),this.#t.delete(s),e.resolve(r)}}catch(i){for(const[n,s]of t.loads)this.#t.delete(n),s.reject(i)}}}export{g as EntityLoader};
@@ -1 +1 @@
1
- import{EntityLoader as A}from"./entity-loader.js";import{formatResultValues as x}from"./format-values.js";const m=5,E=t=>typeof t=="object"&&t!==null&&!Array.isArray(t)&&!(t instanceof Date),_=(t,o)=>{const e=o.properties.find(({type:n})=>n==="ID");return e?t[e.name]:void 0},h=(t,o)=>{const e=structuredClone(t);if(!("_id"in e)){const n=_(e,o);(typeof n=="string"||typeof n=="number"||typeof n=="boolean")&&(e._id=String(n))}return e},b=(t,o,e,n)=>{const s=`${o.id}:${n?"recursive":"leaf"}`,a=t.get(s);if(a){a.rows.push(e);return}t.set(s,{rows:[e],properties:o.properties,expandEntities:n})},g=async(t,o,e)=>{const n=new A(e);let s=[{rows:t,properties:o,expandEntities:!0}];for(let a=0;s.length>0;a+=1){if(await Promise.all(s.map(i=>{const r=i.expandEntities&&a<m;return x(i.rows,i.properties,e.semanticTypes,r?n.load:(p,c)=>Promise.resolve(c))})),a>=m)return;const u=new Map;for(const i of s)if(i.expandEntities)for(const r of i.properties){if(r.primal_type!=="object"||!r.ref)continue;const p=e.resolveSchema(r.ref);for(const c of i.rows){if(!(r.name in c))continue;const f=c[r.name],d=r.isArray?Array.isArray(f)?f:[f]:[f];for(const[w,y]of d.entries()){if(!E(y))continue;const l=h(y,p);r.isArray?d[w]=l:c[r.name]=l,b(u,p,l,!r.isArray)}r.isArray&&(c[r.name]=d)}}s=[...u.values()]}};export{g as populateEntities};
1
+ import{EntityLoader as A}from"./entity-loader.js";import{formatResultValues as x}from"./format-values.js";const m=5,E=r=>typeof r=="object"&&r!==null&&!Array.isArray(r)&&!(r instanceof Date),_=(r,o)=>{const e=o.properties.find(({type:n})=>n==="ID");return e?r[e.name]:void 0},h=(r,o)=>{const e=structuredClone(r);if(!("_id"in e)){const n=_(e,o);(typeof n=="string"||typeof n=="number"||typeof n=="boolean")&&(e._id=String(n))}return e},b=(r,o,e,n)=>{const i=`${o.id}:${n?"recursive":"leaf"}`,a=r.get(i);if(a){a.rows.push(e);return}r.set(i,{rows:[e],properties:o.properties,expandEntities:n})},g=async(r,o,e)=>{const n=new A(e);let i=[{rows:r,properties:o,expandEntities:!0}];for(let a=0;i.length>0;a+=1){if(await Promise.all(i.map(s=>{const t=s.expandEntities&&a<m;return x(s.rows,s.properties,e.semanticTypes,t?n.load:(p,c)=>Promise.resolve(c))})),a>=m)return;const u=new Map;for(const s of i)if(s.expandEntities)for(const t of s.properties){if(t.primal_type!=="entity"&&t.primal_type!=="object"||!t.ref)continue;const p=e.resolveSchema(t.ref);for(const c of s.rows){if(!(t.name in c))continue;const f=c[t.name],d=t.isArray?Array.isArray(f)?f:[f]:[f];for(const[w,y]of d.entries()){if(!E(y))continue;const l=h(y,p);t.isArray?d[w]=l:c[t.name]=l,b(u,p,l,!t.isArray)}t.isArray&&(c[t.name]=d)}}i=[...u.values()]}};export{g as populateEntities};
@@ -1,4 +1,5 @@
1
1
  import type { DataRow } from '../physical/index';
2
2
  import type { RowIdentityPlan } from '../planner/index';
3
- export declare const groupRowIdentity: (row: DataRow, names: readonly string[]) => string;
3
+ export declare const identityPart: (value: unknown) => string;
4
+ export declare const groupRowIdentity: (row: DataRow, names: readonly string[], separator?: string, nullLabel?: string) => string;
4
5
  export declare const addRowIdentities: (rows: readonly DataRow[], identity: RowIdentityPlan) => DataRow[];
@@ -1 +1 @@
1
- import{randomUUID as e}from"node:crypto";const p=n=>{if(n&&typeof n=="object"){if("_id"in n)return String(n._id);if("id"in n)return String(n.id)}return String(n)},t=(n,r)=>r.map(i=>p(n[i])).join(""),s=(n,r)=>n.map(i=>{if(r.kind==="property"){const{[r.internalName]:o,...d}=i;return{...d,_id:String(o)}}return r.kind==="group"?{...i,_id:t(i,r.names)}:{...i,_id:r.kind==="singleton"?"0":e()}});export{s as addRowIdentities,t as groupRowIdentity};
1
+ import{randomUUID as o}from"node:crypto";const e=n=>n==null?"\u7A7A\u503C":typeof n=="object"?"_id"in n?String(n._id):"id"in n?String(n.id):JSON.stringify(n):String(n),f=(n,r,i="_",d="\u7A7A\u503C")=>r.map(t=>n[t]===null||n[t]===void 0?d:e(n[t])).join(i),p=(n,r)=>n.map(i=>{if(r.kind==="property"){const{[r.internalName]:d,...t}=i;return{...t,_id:String(d)}}return r.kind==="group"?{...i,_id:i._id===void 0?f(i,r.names,r.separator,r.nullLabel):e(i._id)}:{...i,_id:r.kind==="singleton"?"0":o()}});export{p as addRowIdentities,f as groupRowIdentity,e as identityPart};
@@ -108,6 +108,10 @@ export type ExecutedSqlType = 'computation' | 'semantic-resolution';
108
108
  export interface ExecutedSql {
109
109
  /** Why this SQL participates in the LogicForm execution. */
110
110
  readonly type: ExecutedSqlType;
111
+ /** Human-readable, localized explanation of why this SQL is executed. */
112
+ readonly explain: string;
113
+ /** Normalized LogicForm represented by this SQL statement. */
114
+ readonly logicform: LogicformType;
111
115
  /** Parameterized SQL used for provider execution and cache identity. */
112
116
  readonly sql: string;
113
117
  readonly parameters: SqlParameters;
@@ -1 +1 @@
1
- import{SemanticDBError as t}from"../errors.js";import{resolvePropertyPath as P}from"../schema/resolve-property-path.js";import{defaultPredName as d}from"./default-pred-name.js";import{normalizeQuery as y}from"./query.js";const w=e=>{if(typeof e!="object"||e===null||Array.isArray(e))return!1;const o=Object.getPrototypeOf(e);return o===Object.prototype||o===null},l=async(e,o,n)=>{const a=n.operators,i={...n.locale?{locale:n.locale}:{}};if(!a)throw new Error("Normalizer operator registry is not initialized");if(e.name!==void 0&&(typeof e.name!="string"||e.name.length===0))throw new t("INVALID_PRED_NAME","PredItem name must be a non-empty string");if(e.args!==void 0&&!w(e.args))throw new t("INVALID_OPERATOR_ARGS","Pred args must be a plain object");const m=e.schema===void 0||e.schema===o.id?o:n.resolveSchema?.(e.schema);if(!m)throw new t("SCHEMA_RESOLVER_REQUIRED",`Cross-Schema pred requires Schema ${e.schema}`);if(e.schema!==void 0&&e.schema!==o.id)throw new t("CROSS_SCHEMA_PRED_UNSUPPORTED","Cross-Schema pred.schema is not supported; use a composed LogicForm with logicforms",{sourceSchemaId:o.id,targetSchemaId:e.schema});const f=await y(e.query,m,n);if(e.pred!==void 0&&e.components!==void 0)throw new t("AMBIGUOUS_OPERATOR_INPUT","A PredItem cannot define both pred and components");if(e.components!==void 0&&!Array.isArray(e.components))throw new t("INVALID_OPERATOR_COMPONENTS","Pred components must be an array");const p=e.components?await Promise.all(e.components.map(r=>l(r,m,n))):void 0,s={...e,...f?{query:f}:{},...p?{components:p}:{}},c=r=>r.operator?a.get(r.operator).normalizePred?.({pred:r,schema:m})??r:r;if(typeof e.pred=="string"){const r=P(m,e.pred,n.resolveSchema),u={...s,pred:r.canonicalName},h={...s,pred:r.canonicalId};return c({...h,name:h.name??d(u,a,i)})}if(e.pred&&typeof e.pred=="object"&&!Array.isArray(e.pred)){const r={...s,pred:await l(e.pred,m,n)};return c({...r,name:r.name??d(r,a,i)})}if(e.pred!==void 0)throw new t("UNSUPPORTED_PRED","A pred must be a property name or nested pred");return c({...s,name:s.name??d(s,a,i)})},A=(e,o,n)=>Promise.all((e??[]).map(a=>l(a,o,n)));export{A as normalizePreds};
1
+ import{SemanticDBError as t}from"../errors.js";import{resolvePropertyPath as u}from"../schema/resolve-property-path.js";import{defaultPredName as d}from"./default-pred-name.js";import{normalizeQuery as y}from"./query.js";const w=e=>{if(typeof e!="object"||e===null||Array.isArray(e))return!1;const o=Object.getPrototypeOf(e);return o===Object.prototype||o===null},f=async(e,o,n)=>{const a=n.operators,s={...n.locale?{locale:n.locale}:{}};if(!a)throw new Error("Normalizer operator registry is not initialized");if(e.name!==void 0&&(typeof e.name!="string"||e.name.length===0))throw new t("INVALID_PRED_NAME","PredItem name must be a non-empty string");if(e.name==="_id")throw new t("RESERVED_OUTPUT_NAME","PredItem name _id is reserved for executor-owned row identity");if(e.args!==void 0&&!w(e.args))throw new t("INVALID_OPERATOR_ARGS","Pred args must be a plain object");const m=e.schema===void 0||e.schema===o.id?o:n.resolveSchema?.(e.schema);if(!m)throw new t("SCHEMA_RESOLVER_REQUIRED",`Cross-Schema pred requires Schema ${e.schema}`);if(e.schema!==void 0&&e.schema!==o.id)throw new t("CROSS_SCHEMA_PRED_UNSUPPORTED","Cross-Schema pred.schema is not supported; use a composed LogicForm with logicforms",{sourceSchemaId:o.id,targetSchemaId:e.schema});const l=await y(e.query,m,n);if(e.pred!==void 0&&e.components!==void 0)throw new t("AMBIGUOUS_OPERATOR_INPUT","A PredItem cannot define both pred and components");if(e.components!==void 0&&!Array.isArray(e.components))throw new t("INVALID_OPERATOR_COMPONENTS","Pred components must be an array");const h=e.components?await Promise.all(e.components.map(r=>f(r,m,n))):void 0,i={...e,...l?{query:l}:{},...h?{components:h}:{}},c=r=>r.operator?a.get(r.operator).normalizePred?.({pred:r,schema:m})??r:r;if(typeof e.pred=="string"){const r=u(m,e.pred,n.resolveSchema),p={...i,pred:r.canonicalName},P={...i,pred:r.canonicalId};return c({...P,name:P.name??d(p,a,s)})}if(e.pred&&typeof e.pred=="object"&&!Array.isArray(e.pred)){const r={...i,pred:await f(e.pred,m,n)};return c({...r,name:r.name??d(r,a,s)})}if(e.pred!==void 0)throw new t("UNSUPPORTED_PRED","A pred must be a property name or nested pred");return c({...i,name:i.name??d(i,a,s)})},E=(e,o,n)=>Promise.all((e??[]).map(a=>f(a,o,n)));export{E as normalizePreds};
@@ -1 +1 @@
1
- import{SemanticDBError as f}from"../errors.js";import{resolvePropertyPath as _}from"../schema/resolve-property-path.js";import{normalizePrimalQuery as L}from"../semantic-types/primal-types/index.js";import{isLogicform as h}from"../logicform-types.js";import{clone as $}from"../utils/clone.js";import{assertSupportedLogicformVersion as b}from"./version.js";import{applyEntityIdQuery as O}from"./entity-id.js";const I=e=>typeof e=="object"&&e!==null&&!Array.isArray(e)&&typeof e.schema=="string"&&typeof e.query=="object"&&e.query!==null&&!Array.isArray(e.query),N=e=>{if(!h(e))return!1;const o=e;return o.groupby!==void 0||o.preds!==void 0||o.from!==void 0||o.logicforms!==void 0||o.having!==void 0||o.sort!==void 0||o.limit!==void 0||o.limitBy!==void 0||o.page!==void 0},R=async(e,o)=>{if(h(e)){if(!o.normalizeNestedLogicform)throw new f("LOGICFORM_NORMALIZER_UNAVAILABLE","Nested query LogicForm must be normalized through normalizeLogicform");return o.normalizeNestedLogicform(e)}return Array.isArray(e)?Promise.all(e.map(r=>R(r,o))):typeof e!="object"||e===null||e instanceof Date?e:Object.fromEntries(await Promise.all(Object.entries(e).map(async([r,s])=>[r,await R(s,o)])))},A=(e,o)=>Object.fromEntries(Object.entries(e).map(([r,s])=>(r==="$and"||r==="$or")&&Array.isArray(s)?[r,s.map(n=>A(n,o))]:[`${o}_${r}`,s])),S=e=>{if(typeof e!="object"||e===null||Array.isArray(e))return e;const o=Object.entries(e),[r]=o;return o.length===1&&r?.[0]==="$eq"?r[1]:e},w=async(e,o,r,s)=>{const n=a=>{if(Array.isArray(a)){a.forEach(n);return}if(typeof a!="object"||a===null||h(a))return;const l=a;if("$options"in l)throw new f("UNSUPPORTED_QUERY_OPERATOR","$options is a LogicForm 1.x field; use args.options in LogicForm 2.0");Object.values(l).forEach(n)};n(e);const t=s.semanticTypes.get(r.type),i={schema:o,property:r,now:s.now??new Date,requestLatestDataDate:()=>Promise.resolve(s.requestLatestDataDate?.(o,r))},y=await L($(e),i),d=t.normalizeQuery?t.normalizeQuery(y,i):y;if(r.comparable){const a=new Set(r.comparable.values),l=(c,p)=>{if(!(h(c)||c===null)){if(Array.isArray(c)){c.forEach(m=>l(m,p));return}if(typeof c=="object"){Object.entries(c).forEach(([m,u])=>{["args","$exists","$regex","$contains"].includes(m)||l(u,m)});return}if(!(p&&["args","$exists","$regex","$contains"].includes(p))&&(typeof c!="string"||!a.has(c)))throw new f("COMPARABLE_VALUE_NOT_FOUND",`Comparable category ${r.name} does not contain value: ${JSON.stringify(c)}`,{property:r.name,value:c})}};l(d)}return d},P=async(e,o,r,s)=>{const n=typeof e=="object"&&e!==null&&!Array.isArray(e)?e:{root:e},t=typeof n.schema=="string"?n.schema:r.ref??o.id,i=t===o.id?o:s.resolveSchema?.(t);if(!i)throw new f("SCHEMA_RESOLVER_REQUIRED",`$recursive requires Schema ${t}`);const y=n.idProperty??n.id_property,d=i.properties.filter(({type:u})=>u==="ID"),a=y===void 0?d.length===1?d[0]:void 0:i.properties.find(({id:u,name:g})=>u===y||g===y);if(!a||y===void 0&&d.length!==1)throw new f("RECURSIVE_ID_PROPERTY_REQUIRED",`$recursive Schema ${t} must have exactly one ID property or idProperty`);const l=Array.isArray(n.roots),c=l?n.roots:n.root===void 0?[]:[n.root];if(!l&&c.length===0||c.some(u=>u===void 0))throw new f("RECURSIVE_ROOT_REQUIRED","$recursive requires root or roots");const p=await Promise.all(c.map(u=>w(u,i,a,s))),m=n.parentProperty??n.parent_property??i.parentProperty??"pid";if(typeof m!="string"||m.length===0)throw new f("RECURSIVE_PARENT_PROPERTY_REQUIRED","$recursive parentProperty must be a string");if(n.includeSelf!==void 0&&typeof n.includeSelf!="boolean"||n.include_self!==void 0&&typeof n.include_self!="boolean")throw new f("INVALID_RECURSIVE_QUERY","$recursive includeSelf must be boolean");return{schema:t,roots:p,includeSelf:n.includeSelf??n.include_self??!0,idProperty:a.name,parentProperty:m}},E=async(e,o,r)=>{const s={};for(const[n,t]of Object.entries(e)){if(n==="$and"||n==="$or"){if(!Array.isArray(t))throw new f("INVALID_QUERY_VALUE",`${n} requires an array`);s[n]=await Promise.all(t.map(async a=>{if(typeof a!="object"||a===null||Array.isArray(a))throw new f("INVALID_QUERY_VALUE",`${n} items must be query objects`);return E(a,o,r)}));continue}const i=_(o,n,r.resolveSchema);if(N(t)&&i.property.primal_type==="object"&&i.property.ref){if(t.schema!==i.property.ref)throw new f("RELATION_SCHEMA_MISMATCH",`Relation ${i.canonicalName} references ${i.property.ref}, not ${t.schema??"undefined"}`);if(!r.normalizeNestedLogicform)throw new f("LOGICFORM_NORMALIZER_UNAVAILABLE","Nested query LogicForm must be normalized through normalizeLogicform");const a=await r.normalizeNestedLogicform(t);s[i.canonicalName]={$in:a};continue}if(I(t)){if(t.version!==void 0&&b(t),i.property.primal_type!=="object"||!i.property.ref)throw new f("INVALID_RELATION_QUERY",`Nested LogicForm query requires an object property: ${i.canonicalName}`);if(t.schema!==i.property.ref)throw new f("RELATION_SCHEMA_MISMATCH",`Relation ${i.canonicalName} references ${i.property.ref}, not ${t.schema}`);const a=r.resolveSchema?.(t.schema);if(!a)throw new f("SCHEMA_RESOLVER_REQUIRED",`Nested relation query requires Schema ${t.schema}`);const l=O(t.query,t.entity_id,a,r.plugins),c=await E(l,a,r);Object.assign(s,A(c,i.canonicalName));continue}if(typeof t=="object"&&t!==null&&!Array.isArray(t)&&Object.prototype.hasOwnProperty.call(t,"$recursive")){const a=t;if(Object.keys(a).filter(c=>c!=="$recursive").length>0)throw new f("INVALID_RECURSIVE_QUERY","$recursive cannot be combined with other field operators");s[i.canonicalName]={$recursive:await P(a.$recursive,i.schema,i.property,r)};continue}const y=await R(t,r),d=await w(y,i.schema,i.property,r);s[i.canonicalName]=S(d)}return s},j=async(e,o,r)=>{if(e)return E(e,o,r)};export{j as normalizeQuery,E as normalizeQueryObject};
1
+ import{SemanticDBError as f}from"../errors.js";import{resolvePropertyPath as g}from"../schema/resolve-property-path.js";import{normalizePrimalQuery as L}from"../semantic-types/primal-types/index.js";import{isLogicform as h}from"../logicform-types.js";import{clone as $}from"../utils/clone.js";import{assertSupportedLogicformVersion as b}from"./version.js";import{applyEntityIdQuery as I}from"./entity-id.js";const O=e=>typeof e=="object"&&e!==null&&!Array.isArray(e)&&typeof e.schema=="string"&&typeof e.query=="object"&&e.query!==null&&!Array.isArray(e.query),N=e=>{if(!h(e))return!1;const t=e;return t.groupby!==void 0||t.preds!==void 0||t.from!==void 0||t.logicforms!==void 0||t.having!==void 0||t.sort!==void 0||t.limit!==void 0||t.limitBy!==void 0||t.page!==void 0},R=async(e,t)=>{if(h(e)){if(!t.normalizeNestedLogicform)throw new f("LOGICFORM_NORMALIZER_UNAVAILABLE","Nested query LogicForm must be normalized through normalizeLogicform");return t.normalizeNestedLogicform(e)}return Array.isArray(e)?Promise.all(e.map(r=>R(r,t))):typeof e!="object"||e===null||e instanceof Date?e:Object.fromEntries(await Promise.all(Object.entries(e).map(async([r,s])=>[r,await R(s,t)])))},A=(e,t)=>Object.fromEntries(Object.entries(e).map(([r,s])=>(r==="$and"||r==="$or")&&Array.isArray(s)?[r,s.map(o=>A(o,t))]:[`${t}_${r}`,s])),S=e=>{if(typeof e!="object"||e===null||Array.isArray(e))return e;const t=Object.entries(e),[r]=t;return t.length===1&&r?.[0]==="$eq"?r[1]:e},w=async(e,t,r,s)=>{const o=a=>{if(Array.isArray(a)){a.forEach(o);return}if(typeof a!="object"||a===null||h(a))return;const l=a;if("$options"in l)throw new f("UNSUPPORTED_QUERY_OPERATOR","$options is a LogicForm 1.x field; use args.options in LogicForm 2.0");Object.values(l).forEach(o)};o(e);const n=s.semanticTypes.get(r.type),i={schema:t,property:r,now:s.now??new Date,requestLatestDataDate:()=>Promise.resolve(s.requestLatestDataDate?.(t,r))},y=await L($(e),i),m=n.normalizeQuery?n.normalizeQuery(y,i):y;if(r.comparable){const a=new Set(r.comparable.values),l=(c,p)=>{if(!(h(c)||c===null)){if(Array.isArray(c)){c.forEach(d=>l(d,p));return}if(typeof c=="object"){Object.entries(c).forEach(([d,u])=>{["args","$exists","$regex","$contains"].includes(d)||l(u,d)});return}if(!(p&&["args","$exists","$regex","$contains"].includes(p))&&(typeof c!="string"||!a.has(c)))throw new f("COMPARABLE_VALUE_NOT_FOUND",`Comparable category ${r.name} does not contain value: ${JSON.stringify(c)}`,{property:r.name,value:c})}};l(m)}return m},P=async(e,t,r,s)=>{const o=typeof e=="object"&&e!==null&&!Array.isArray(e)?e:{root:e},n=typeof o.schema=="string"?o.schema:r.ref??t.id,i=n===t.id?t:s.resolveSchema?.(n);if(!i)throw new f("SCHEMA_RESOLVER_REQUIRED",`$recursive requires Schema ${n}`);const y=o.idProperty??o.id_property,m=i.properties.filter(({type:u})=>u==="ID"),a=y===void 0?m.length===1?m[0]:void 0:i.properties.find(({id:u,name:_})=>u===y||_===y);if(!a||y===void 0&&m.length!==1)throw new f("RECURSIVE_ID_PROPERTY_REQUIRED",`$recursive Schema ${n} must have exactly one ID property or idProperty`);const l=Array.isArray(o.roots),c=l?o.roots:o.root===void 0?[]:[o.root];if(!l&&c.length===0||c.some(u=>u===void 0))throw new f("RECURSIVE_ROOT_REQUIRED","$recursive requires root or roots");const p=await Promise.all(c.map(u=>w(u,i,a,s))),d=o.parentProperty??o.parent_property??i.parentProperty??"pid";if(typeof d!="string"||d.length===0)throw new f("RECURSIVE_PARENT_PROPERTY_REQUIRED","$recursive parentProperty must be a string");if(o.includeSelf!==void 0&&typeof o.includeSelf!="boolean"||o.include_self!==void 0&&typeof o.include_self!="boolean")throw new f("INVALID_RECURSIVE_QUERY","$recursive includeSelf must be boolean");return{schema:n,roots:p,includeSelf:o.includeSelf??o.include_self??!0,idProperty:a.name,parentProperty:d}},E=async(e,t,r)=>{const s={};for(const[o,n]of Object.entries(e)){if(o==="$and"||o==="$or"){if(!Array.isArray(n))throw new f("INVALID_QUERY_VALUE",`${o} requires an array`);s[o]=await Promise.all(n.map(async a=>{if(typeof a!="object"||a===null||Array.isArray(a))throw new f("INVALID_QUERY_VALUE",`${o} items must be query objects`);return E(a,t,r)}));continue}const i=g(t,o,r.resolveSchema);if(N(n)&&(i.property.primal_type==="entity"||i.property.primal_type==="object")&&i.property.ref){if(n.schema!==i.property.ref)throw new f("RELATION_SCHEMA_MISMATCH",`Relation ${i.canonicalName} references ${i.property.ref}, not ${n.schema??"undefined"}`);if(!r.normalizeNestedLogicform)throw new f("LOGICFORM_NORMALIZER_UNAVAILABLE","Nested query LogicForm must be normalized through normalizeLogicform");const a=await r.normalizeNestedLogicform(n);s[i.canonicalName]={$in:a};continue}if(O(n)){if(n.version!==void 0&&b(n),i.property.primal_type!=="entity"&&i.property.primal_type!=="object")throw new f("INVALID_RELATION_QUERY",`Relation query ${o} must resolve to an entity property`);if(!i.property.ref)throw new f("INVALID_RELATION_QUERY",`Nested LogicForm query requires an object property: ${i.canonicalName}`);if(n.schema!==i.property.ref)throw new f("RELATION_SCHEMA_MISMATCH",`Relation ${i.canonicalName} references ${i.property.ref}, not ${n.schema}`);const a=r.resolveSchema?.(n.schema);if(!a)throw new f("SCHEMA_RESOLVER_REQUIRED",`Nested relation query requires Schema ${n.schema}`);const l=I(n.query,n.entity_id,a,r.plugins),c=await E(l,a,r);Object.assign(s,A(c,i.canonicalName));continue}if(typeof n=="object"&&n!==null&&!Array.isArray(n)&&Object.prototype.hasOwnProperty.call(n,"$recursive")){const a=n;if(Object.keys(a).filter(c=>c!=="$recursive").length>0)throw new f("INVALID_RECURSIVE_QUERY","$recursive cannot be combined with other field operators");s[i.canonicalName]={$recursive:await P(a.$recursive,i.schema,i.property,r)};continue}const y=await R(n,r),m=await w(y,i.schema,i.property,r);s[i.canonicalName]=S(m)}return s},j=async(e,t,r)=>{if(e)return E(e,t,r)};export{j as normalizeQuery,E as normalizeQueryObject};
@@ -1 +1 @@
1
- import{SemanticDBError as m}from"../../errors.js";import{clone as s}from"../../utils/clone.js";import{combineQueries as g,collectPredQueries as P}from"./period-shift.js";import{requireTimestampProperty as f}from"./time-range.js";import{groupbyOutputName as l}from"../groupby.js";const $=new Set(["day","week","month","quarter","year"]),q={day:"\u65E5",week:"\u5468",month:"\u6708",quarter:"\u5B63\u5EA6",year:"\u5E74"},w=r=>{if(typeof r!="string"||!$.has(r))throw new m("INVALID_AVERAGE_PER_PERIOD_ARGS","$averagePerPeriod args.granularity must be day, week, month, quarter, or year");return r},u=r=>{const e=s(r);return delete e.having,delete e.sort,delete e.limit,delete e.page,e},i=r=>{const e=s(r);return delete e.query,e.pred&&typeof e.pred=="object"&&(e.pred=i(e.pred)),e.components&&(e.components=e.components.map(i)),e},h={name:"$averagePerPeriod",defaultName:({operandName:r,args:e,locale:o})=>o.toLowerCase().startsWith("en")?`Average ${r} per ${typeof e.granularity=="string"?e.granularity:"period"}`:`${r}${q[String(e.granularity)]??"\u5468\u671F"}\u5747\u503C`,kind:"component",createComponent({logicform:r,pred:e,schema:o}){if(!e.pred||typeof e.pred!="object"||Array.isArray(e.pred))throw new m("INVALID_AVERAGE_PER_PERIOD_INPUT","$averagePerPeriod pred must be a nested metric");const d=w(e.args?.granularity),c=f(o,"$averagePerPeriod"),y=i(e.pred),t=g([r.query,...P(e)]),n=u(r);n.preds=[{...y,name:"__average_per_period_numerator__"}],t&&(n.query=t);const a=u(r);a.preds=[{operator:"$countDistinct",pred:{operator:"$dateBucket",pred:c.name,args:{granularity:d},name:"__average_per_period_bucket__"},name:"__average_per_period_denominator__"}],t&&(a.query=t);const _=(r.groupby??[]).map(l);return{kind:"cte",components:[{name:"numerator",component:{kind:"query",logicform:n}},{name:"denominator",component:{kind:"query",logicform:a},joinKeys:_}],toSQL:({values:p})=>`${p.numerator} / NULLIF(${p.denominator}, 0)`}},getOutputProperty({name:r,inputProperty:e}){if(!e)throw new Error("$averagePerPeriod requires a nested metric");return{...e,name:r,isArray:!1}}};export{h as averagePerPeriodOperator};
1
+ import{SemanticDBError as p}from"../../errors.js";import{clone as m}from"../../utils/clone.js";import{combineQueries as g,collectPredQueries as P}from"./period-shift.js";import{requireTimestampProperty as f}from"./time-range.js";import{groupbyOutputName as l}from"../groupby.js";const $=new Set(["day","week","month","quarter","year"]),q={day:"\u65E5",week:"\u5468",month:"\u6708",quarter:"\u5B63\u5EA6",year:"\u5E74"},w=r=>{if(typeof r!="string"||!$.has(r))throw new p("INVALID_AVERAGE_PER_PERIOD_ARGS","$averagePerPeriod args.granularity must be day, week, month, quarter, or year");return r},u=r=>{const e=m(r);return delete e.having,delete e.sort,delete e.limit,delete e.page,e},i=r=>{const e=m(r);return delete e.query,e.pred&&typeof e.pred=="object"&&(e.pred=i(e.pred)),e.components&&(e.components=e.components.map(i)),e},h={name:"$averagePerPeriod",defaultName:({operandName:r,args:e,locale:o})=>o.toLowerCase().startsWith("en")?`Average ${r} per ${typeof e.granularity=="string"?e.granularity:"period"}`:`${r}${q[String(e.granularity)]??"\u5468\u671F"}\u5747\u503C`,kind:"component",createComponent({logicform:r,pred:e,schema:o}){if(!e.pred||typeof e.pred!="object"||Array.isArray(e.pred))throw new p("INVALID_AVERAGE_PER_PERIOD_INPUT","$averagePerPeriod pred must be a nested metric");const d=w(e.args?.granularity),c=f(o,"$averagePerPeriod"),y=i(e.pred),t=g([r.query,...P(e)]),n=u(r);n.preds=[{...y,name:"__avg_period_num__"}],t&&(n.query=t);const a=u(r);a.preds=[{operator:"$countDistinct",pred:{operator:"$dateBucket",pred:c.name,args:{granularity:d},name:"__avg_period_bucket__"},name:"__avg_period_den__"}],t&&(a.query=t);const _=(r.groupby??[]).map(l);return{kind:"cte",components:[{name:"num",component:{kind:"query",logicform:n}},{name:"den",component:{kind:"query",logicform:a},joinKeys:_}],toSQL:({values:s})=>`${s.num} / NULLIF(${s.den}, 0)`}},getOutputProperty({name:r,inputProperty:e}){if(!e)throw new Error("$averagePerPeriod requires a nested metric");return{...e,name:r,isArray:!1}}};export{h as averagePerPeriodOperator};
@@ -1 +1 @@
1
- import{clone as n}from"../../utils/clone.js";import{combineQueries as m,collectPredQueries as a,buildPeriodShiftComponent as _}from"./period-shift.js";import{periodComparisonName as d}from"./period-name.js";const y=r=>{const e=n(r);return delete e.having,delete e.sort,delete e.limit,delete e.page,e},$=({logicform:r,pred:e,schema:t,resolveSchema:u})=>{if(!e.pred||typeof e.pred!="object"||Array.isArray(e.pred))throw new Error("$periodDifference pred must be a nested metric");const i=n(e.pred);i.name="__period_difference_current__";const o=y(r);o.preds=[i];const c=m([r.query,...a(e)]);c&&(o.query=c);const f={pred:n(e.pred),...e.args?{args:e.args}:{}},s=_({logicform:r,pred:f,outputName:"__period_difference_previous__",schema:t,resolveSchema:u});if(s.kind!=="query")throw new Error("$periodDifference period shift must produce a query component");return{kind:"cte",components:[{name:"current",component:{kind:"query",logicform:o}},{name:"previous",component:s}],toSQL:({values:p})=>`${p.current} - ${p.previous}`}},h={name:"$periodDifference",defaultName:({operandName:r,args:e,locale:t})=>t.toLowerCase().startsWith("en")?`${r} ${d(e,t)} difference`:`${r}${d(e,t)}\u5DEE\u503C`,kind:"component",createComponent:$,getOutputProperty({name:r,inputProperty:e}){if(!e)throw new Error("$periodDifference requires a nested metric");return{...e,name:r,isArray:!1}}};export{h as periodDifferenceOperator};
1
+ import{clone as n}from"../../utils/clone.js";import{combineQueries as m,collectPredQueries as a,buildPeriodShiftComponent as _}from"./period-shift.js";import{periodComparisonName as d}from"./period-name.js";const y=r=>{const e=n(r);return delete e.having,delete e.sort,delete e.limit,delete e.page,e},$=({logicform:r,pred:e,schema:t,resolveSchema:f})=>{if(!e.pred||typeof e.pred!="object"||Array.isArray(e.pred))throw new Error("$periodDifference pred must be a nested metric");const i=n(e.pred);i.name="__period_diff_current__";const o=y(r);o.preds=[i];const p=m([r.query,...a(e)]);p&&(o.query=p);const u={pred:n(e.pred),...e.args?{args:e.args}:{}},c=_({logicform:r,pred:u,outputName:"__period_diff_prev__",schema:t,resolveSchema:f});if(c.kind!=="query")throw new Error("$periodDifference period shift must produce a query component");return{kind:"cte",components:[{name:"current",component:{kind:"query",logicform:o}},{name:"prev",component:c}],toSQL:({values:s})=>`${s.current} - ${s.prev}`}},h={name:"$periodDifference",defaultName:({operandName:r,args:e,locale:t})=>t.toLowerCase().startsWith("en")?`${r} ${d(e,t)} difference`:`${r}${d(e,t)}\u5DEE\u503C`,kind:"component",createComponent:$,getOutputProperty({name:r,inputProperty:e}){if(!e)throw new Error("$periodDifference requires a nested metric");return{...e,name:r,isArray:!1}}};export{h as periodDifferenceOperator};
@@ -1 +1 @@
1
- import{clone as n}from"../../utils/clone.js";import{combineQueries as a,collectPredQueries as h,buildPeriodShiftComponent as w}from"./period-shift.js";import{periodComparisonName as d}from"./period-name.js";const y=r=>{const e=n(r);return delete e.having,delete e.sort,delete e.limit,delete e.page,e},_=({logicform:r,pred:e,schema:t,resolveSchema:c})=>{if(!e.pred||typeof e.pred!="object"||Array.isArray(e.pred))throw new Error("$periodGrowth pred must be a nested metric");const i=n(e.pred);i.name="__period_growth_current__";const o=y(r);o.preds=[i];const p=a([r.query,...h(e)]);p&&(o.query=p);const m={pred:n(e.pred),...e.args?{args:e.args}:{}},s=w({logicform:r,pred:m,outputName:"__period_growth_previous__",schema:t,resolveSchema:c});if(s.kind!=="query")throw new Error("$periodGrowth period shift must produce a query component");return{kind:"cte",components:[{name:"current",component:{kind:"query",logicform:o}},{name:"previous",component:s}],toSQL:({values:u})=>`${u.current} / NULLIF(ABS(${u.previous}), 0) - 1`}},f={name:"$periodGrowth",defaultName:({operandName:r,args:e,locale:t})=>t.toLowerCase().startsWith("en")?`${r} ${d(e,t)} growth`:`${r}${d(e,t)}\u589E\u957F\u7387`,kind:"component",createComponent:_,getOutputProperty({name:r}){return{name:r,type:"percentage",primal_type:"number",isArray:!1}}};export{f as periodGrowthOperator};
1
+ import{clone as n}from"../../utils/clone.js";import{combineQueries as a,collectPredQueries as h,buildPeriodShiftComponent as w}from"./period-shift.js";import{periodComparisonName as d}from"./period-name.js";const y=r=>{const e=n(r);return delete e.having,delete e.sort,delete e.limit,delete e.page,e},_=({logicform:r,pred:e,schema:t,resolveSchema:c})=>{if(!e.pred||typeof e.pred!="object"||Array.isArray(e.pred))throw new Error("$periodGrowth pred must be a nested metric");const p=n(e.pred);p.name="__period_growth_current__";const o=y(r);o.preds=[p];const i=a([r.query,...h(e)]);i&&(o.query=i);const m={pred:n(e.pred),...e.args?{args:e.args}:{}},s=w({logicform:r,pred:m,outputName:"__period_growth_prev__",schema:t,resolveSchema:c});if(s.kind!=="query")throw new Error("$periodGrowth period shift must produce a query component");return{kind:"cte",components:[{name:"current",component:{kind:"query",logicform:o}},{name:"prev",component:s}],toSQL:({values:u})=>`${u.current} / NULLIF(ABS(${u.prev}), 0) - 1`}},f={name:"$periodGrowth",defaultName:({operandName:r,args:e,locale:t})=>t.toLowerCase().startsWith("en")?`${r} ${d(e,t)} growth`:`${r}${d(e,t)}\u589E\u957F\u7387`,kind:"component",createComponent:_,getOutputProperty({name:r}){return{name:r,type:"percentage",primal_type:"number",isArray:!1}}};export{f as periodGrowthOperator};
@@ -1 +1 @@
1
- import{SemanticDBError as s}from"../../errors.js";import{bindTrustedSqlExpression as a}from"../sql-expression.js";const i=new Set(["boolean","date","number","object","string"]),p=r=>{if(r===void 0)return[];if(!Array.isArray(r))throw new s("INVALID_SQL_ARGS","$sql args.parameters must be an array");return r},l=(r,e)=>{const t=e.type,n=e.primal_type;if(typeof t!="string"||t.length===0||!i.has(n))throw new s("INVALID_SQL_OUTPUT","$sql requires args.type and a valid args.primal_type");if(e.isArray!==void 0&&typeof e.isArray!="boolean")throw new s("INVALID_SQL_OUTPUT","$sql args.isArray must be boolean");if(e.ref!==void 0&&typeof e.ref!="string")throw new s("INVALID_SQL_OUTPUT","$sql args.ref must be a Schema id");return{name:r,type:t,primal_type:n,isArray:e.isArray===!0,...typeof e.ref=="string"?{ref:e.ref}:{}}},f={name:"$sql",defaultName:({locale:r})=>r.toLowerCase().startsWith("en")?"Calculated value":"\u8BA1\u7B97\u503C",kind:"scalar",requiresProperty:!1,toSQL({args:r,expression:e,expressions:t,sql:n}){if(e||t&&t.length>0)throw new s("INVALID_SQL_INPUT","$sql does not accept pred or components; put the expression in args.sql");return a(r.sql,p(r.parameters),o=>n.value(o))},getOutputProperty({name:r,args:e,inputProperty:t,componentProperties:n}){if(t||n&&n.length>0)throw new s("INVALID_SQL_INPUT","$sql does not accept pred or components; put the expression in args.sql");return l(r,e)}};export{f as sqlOperator};
1
+ import{SemanticDBError as s}from"../../errors.js";import{bindTrustedSqlExpression as a}from"../sql-expression.js";const i=new Set(["boolean","date","number","entity","object","string"]),p=r=>{if(r===void 0)return[];if(!Array.isArray(r))throw new s("INVALID_SQL_ARGS","$sql args.parameters must be an array");return r},l=(r,e)=>{const t=e.type,n=e.primal_type;if(typeof t!="string"||t.length===0||!i.has(n))throw new s("INVALID_SQL_OUTPUT","$sql requires args.type and a valid args.primal_type");if(e.isArray!==void 0&&typeof e.isArray!="boolean")throw new s("INVALID_SQL_OUTPUT","$sql args.isArray must be boolean");if(e.ref!==void 0&&typeof e.ref!="string")throw new s("INVALID_SQL_OUTPUT","$sql args.ref must be a Schema id");return{name:r,type:t,primal_type:n==="object"?"entity":n,isArray:e.isArray===!0,...typeof e.ref=="string"?{ref:e.ref}:{}}},y={name:"$sql",defaultName:({locale:r})=>r.toLowerCase().startsWith("en")?"Calculated value":"\u8BA1\u7B97\u503C",kind:"scalar",requiresProperty:!1,toSQL({args:r,expression:e,expressions:t,sql:n}){if(e||t&&t.length>0)throw new s("INVALID_SQL_INPUT","$sql does not accept pred or components; put the expression in args.sql");return a(r.sql,p(r.parameters),o=>n.value(o))},getOutputProperty({name:r,args:e,inputProperty:t,componentProperties:n}){if(t||n&&n.length>0)throw new s("INVALID_SQL_INPUT","$sql does not accept pred or components; put the expression in args.sql");return l(r,e)}};export{y as sqlOperator};
@@ -30,6 +30,7 @@ export declare class ClickHouseProvider implements CoreComputeProvider {
30
30
  dateBucket(expression: string, level: string): string;
31
31
  dateShift(expression: string, granularity: string, offset: number): string;
32
32
  aggregateFunction(name: string, expression?: string): string;
33
+ stringify(expression: string): string;
33
34
  concat(expressions: readonly string[]): string;
34
35
  substring(expression: string, start: number, length: number): string;
35
36
  regex(expression: string, pattern: string, options?: string): string;
@@ -1 +1 @@
1
- import{createClient as i}from"@clickhouse/client";import{displayClickHouseQuery as s}from"./display-query.js";const u=a=>a===null?"Nullable(String)":typeof a=="boolean"?"Bool":typeof a=="number"?"Float64":a instanceof Date?"DateTime64(3)":"String",c=a=>a instanceof Date?a.toISOString().replace("T"," ").replace("Z",""):a;class l{kind="clickhouse";client;constructor(e={}){this.client=e.client??i({url:e.url??"http://localhost:8123",username:e.username??"default",password:e.password??"",database:e.database??"default"})}quoteIdentifier(e){return`\`${e.replaceAll("`","``")}\``}tableAlias(e,r){return`${e} AS ${this.quoteIdentifier(r)}`}paginate(e,r,t){return`${e} LIMIT ${r}${t>0?` OFFSET ${t}`:""}`}parameter(e,r){return`{p${e}:${u(r)}}`}displayQuery(e,r){return s(e,r)}dateBucket(e,r){const n={hour:"toStartOfHour",day:"toStartOfDay",week:"toStartOfWeek",month:"toStartOfMonth",quarter:"toStartOfQuarter",year:"toStartOfYear"}[r];if(!n)throw new Error(`Unsupported ClickHouse date level: ${r}`);return`${n}(${e})`}dateShift(e,r,t){if(!new Set(["second","minute","hour","day","week","month","quarter","year"]).has(r)||!Number.isInteger(t))throw new Error(`Unsupported ClickHouse date shift: ${r}/${t}`);return`dateAdd('${r}', ${t}, ${e})`}aggregateFunction(e,r="*"){if(!/^[A-Za-z][A-Za-z0-9_]*$/.test(e))throw new Error(`Unsupported ClickHouse aggregate function: ${e}`);return`${e.toUpperCase()}(${r})`}concat(e){return`concat(${e.join(", ")})`}substring(e,r,t){return`substring(${e}, ${r}, ${t})`}regex(e,r,t){if(t&&t!=="i")throw new Error(`Unsupported ClickHouse regex options: ${t}`);const n=t==="i"?`concat('(?i)', ${r})`:r;return`match(${e}, ${n})`}arrayContains(e,r){return`has(${e}, ${r})`}expandArray(e,r,t){const n=this.quoteIdentifier(r);return{joinSql:`ARRAY JOIN ${e} AS ${n}`,valueExpression:n}}async execute(e){e.signal?.throwIfAborted();const r=Object.fromEntries(e.parameters.map((n,o)=>[`p${o}`,c(n)]));return(await this.client.query({query:e.sql,format:"JSONEachRow",query_params:r,...e.signal?{abort_signal:e.signal}:{}})).json()}async close(){await this.client.close()}}export{l as ClickHouseProvider};
1
+ import{createClient as i}from"@clickhouse/client";import{displayClickHouseQuery as s}from"./display-query.js";const u=a=>a===null?"Nullable(String)":typeof a=="boolean"?"Bool":typeof a=="number"?"Float64":a instanceof Date?"DateTime64(3)":"String",c=a=>a instanceof Date?a.toISOString().replace("T"," ").replace("Z",""):a;class l{kind="clickhouse";client;constructor(r={}){this.client=r.client??i({url:r.url??"http://localhost:8123",username:r.username??"default",password:r.password??"",database:r.database??"default"})}quoteIdentifier(r){return`\`${r.replaceAll("`","``")}\``}tableAlias(r,t){return`${r} AS ${this.quoteIdentifier(t)}`}paginate(r,t,e){return`${r} LIMIT ${t}${e>0?` OFFSET ${e}`:""}`}parameter(r,t){return`{p${r}:${u(t)}}`}displayQuery(r,t){return s(r,t)}dateBucket(r,t){const n={hour:"toStartOfHour",day:"toStartOfDay",week:"toStartOfWeek",month:"toStartOfMonth",quarter:"toStartOfQuarter",year:"toStartOfYear"}[t];if(!n)throw new Error(`Unsupported ClickHouse date level: ${t}`);return`${n}(${r})`}dateShift(r,t,e){if(!new Set(["second","minute","hour","day","week","month","quarter","year"]).has(t)||!Number.isInteger(e))throw new Error(`Unsupported ClickHouse date shift: ${t}/${e}`);return`dateAdd('${t}', ${e}, ${r})`}aggregateFunction(r,t="*"){if(!/^[A-Za-z][A-Za-z0-9_]*$/.test(r))throw new Error(`Unsupported ClickHouse aggregate function: ${r}`);return`${r.toUpperCase()}(${t})`}stringify(r){return`toString(${r})`}concat(r){return`concat(${r.join(", ")})`}substring(r,t,e){return`substring(${r}, ${t}, ${e})`}regex(r,t,e){if(e&&e!=="i")throw new Error(`Unsupported ClickHouse regex options: ${e}`);const n=e==="i"?`concat('(?i)', ${t})`:t;return`match(${r}, ${n})`}arrayContains(r,t){return`has(${r}, ${t})`}expandArray(r,t,e){const n=this.quoteIdentifier(t);return{joinSql:`ARRAY JOIN ${r} AS ${n}`,valueExpression:n}}async execute(r){r.signal?.throwIfAborted();const t=Object.fromEntries(r.parameters.map((n,o)=>[`p${o}`,c(n)]));return(await this.client.query({query:r.sql,format:"JSONEachRow",query_params:t,...r.signal?{abort_signal:r.signal}:{}})).json()}async close(){await this.client.close()}}export{l as ClickHouseProvider};
@@ -1 +1 @@
1
- import u from"mysql2";const o={quoteIdentifier(r){return`\`${r.replaceAll("`","``")}\``},tableAlias(r,t){return`${r} AS ${this.quoteIdentifier(t)}`},paginate(r,t,e){return`${r} LIMIT ${t}${e>0?` OFFSET ${e}`:""}`},parameter(r,t){return"?"},displayQuery(r,t){return u.format(r,[...t])},dateBucket(r,t){const e={hour:"%Y-%m-%d %H:00:00",day:"%Y-%m-%d",month:"%Y-%m-01",year:"%Y-01-01"};if(t==="week")return`DATE_SUB(DATE(${r}), INTERVAL WEEKDAY(${r}) DAY)`;if(t==="quarter")return`MAKEDATE(YEAR(${r}), 1) + INTERVAL (QUARTER(${r}) - 1) QUARTER`;const n=e[t];if(!n)throw new Error(`Unsupported MySQL-compatible date level: ${t}`);return`DATE_FORMAT(${r}, '${n}')`},dateShift(r,t,e){if(!new Set(["second","minute","hour","day","week","month","quarter","year"]).has(t)||!Number.isInteger(e))throw new Error(`Unsupported MySQL-compatible date shift: ${t}/${e}`);return`DATE_ADD(${r}, INTERVAL ${e} ${t.toUpperCase()})`},aggregateFunction(r,t="*"){if(!/^[A-Za-z][A-Za-z0-9_]*$/.test(r))throw new Error(`Unsupported MySQL-compatible aggregate function: ${r}`);return`${r.toUpperCase()}(${t})`},concat(r){return`CONCAT(${r.join(", ")})`},substring(r,t,e){return`SUBSTRING(${r}, ${t}, ${e})`},regex(r,t,e){if(e&&e!=="i")throw new Error(`Unsupported MySQL-compatible regex options: ${e}`);return e==="i"?`LOWER(${r}) REGEXP LOWER(${t})`:`${r} REGEXP ${t}`},arrayContains(r,t){return`JSON_OVERLAPS(${r}, JSON_ARRAY(${t}))`},expandArray(r,t,e){const n=this.quoteIdentifier(t),$=this.quoteIdentifier(e);return{joinSql:`CROSS JOIN JSON_TABLE(CASE WHEN JSON_TYPE(${r}) = 'ARRAY' THEN ${r} ELSE JSON_ARRAY() END, '$[*]' COLUMNS (${$} JSON PATH '$')) AS ${n}`,valueExpression:`${n}.${$}`}}};export{o as mysqlCompatibleSql};
1
+ import u from"mysql2";const A={quoteIdentifier(r){return`\`${r.replaceAll("`","``")}\``},tableAlias(r,t){return`${r} AS ${this.quoteIdentifier(t)}`},paginate(r,t,e){return`${r} LIMIT ${t}${e>0?` OFFSET ${e}`:""}`},parameter(r,t){return"?"},displayQuery(r,t){return u.format(r,[...t])},dateBucket(r,t){const e={hour:"%Y-%m-%d %H:00:00",day:"%Y-%m-%d",month:"%Y-%m-01",year:"%Y-01-01"};if(t==="week")return`DATE_SUB(DATE(${r}), INTERVAL WEEKDAY(${r}) DAY)`;if(t==="quarter")return`MAKEDATE(YEAR(${r}), 1) + INTERVAL (QUARTER(${r}) - 1) QUARTER`;const n=e[t];if(!n)throw new Error(`Unsupported MySQL-compatible date level: ${t}`);return`DATE_FORMAT(${r}, '${n}')`},dateShift(r,t,e){if(!new Set(["second","minute","hour","day","week","month","quarter","year"]).has(t)||!Number.isInteger(e))throw new Error(`Unsupported MySQL-compatible date shift: ${t}/${e}`);return`DATE_ADD(${r}, INTERVAL ${e} ${t.toUpperCase()})`},aggregateFunction(r,t="*"){if(!/^[A-Za-z][A-Za-z0-9_]*$/.test(r))throw new Error(`Unsupported MySQL-compatible aggregate function: ${r}`);return`${r.toUpperCase()}(${t})`},stringify(r){return`CAST(${r} AS CHAR)`},concat(r){return`CONCAT(${r.join(", ")})`},substring(r,t,e){return`SUBSTRING(${r}, ${t}, ${e})`},regex(r,t,e){if(e&&e!=="i")throw new Error(`Unsupported MySQL-compatible regex options: ${e}`);return e==="i"?`LOWER(${r}) REGEXP LOWER(${t})`:`${r} REGEXP ${t}`},arrayContains(r,t){return`JSON_OVERLAPS(${r}, JSON_ARRAY(${t}))`},expandArray(r,t,e){const n=this.quoteIdentifier(t),$=this.quoteIdentifier(e);return{joinSql:`CROSS JOIN JSON_TABLE(CASE WHEN JSON_TYPE(${r}) = 'ARRAY' THEN ${r} ELSE JSON_ARRAY() END, '$[*]' COLUMNS (${$} JSON PATH '$')) AS ${n}`,valueExpression:`${n}.${$}`}}};export{A as mysqlCompatibleSql};
@@ -21,6 +21,7 @@ export declare abstract class MySQLProtocolProvider implements CoreComputeProvid
21
21
  dateBucket(expression: string, level: string): string;
22
22
  dateShift(expression: string, granularity: string, offset: number): string;
23
23
  aggregateFunction(name: string, expression?: string): string;
24
+ stringify(expression: string): string;
24
25
  concat(expressions: readonly string[]): string;
25
26
  substring(expression: string, start: number, length: number): string;
26
27
  regex(expression: string, pattern: string, options?: string): string;
@@ -1 +1 @@
1
- import{createPool as o}from"mysql2/promise";import{mysqlCompatibleSql as t}from"./mysql-compatible-sql.js";class i{supportsRecursiveCte=!0;pool;constructor(r={}){this.pool=r.pool??o(r.connection??{})}quoteIdentifier(r){return t.quoteIdentifier(r)}tableAlias(r,e){return t.tableAlias(r,e)}paginate(r,e,a){return t.paginate(r,e,a)}parameter(r,e){return t.parameter(r,e)}displayQuery(r,e){return t.displayQuery(r,e)}dateBucket(r,e){return t.dateBucket(r,e)}dateShift(r,e,a){return t.dateShift(r,e,a)}aggregateFunction(r,e){return t.aggregateFunction(r,e)}concat(r){return t.concat(r)}substring(r,e,a){return t.substring(r,e,a)}regex(r,e,a){return t.regex(r,e,a)}arrayContains(r,e){const a=t.arrayContains?.bind(t);if(!a)throw new Error("MySQL-compatible array conditions are unavailable");return a(r,e)}expandArray(r,e,a){const n=t.expandArray?.bind(t);if(!n)throw new Error("MySQL-compatible array expansion is unavailable");return n(r,e,a)}async execute(r){r.signal?.throwIfAborted();const[e]=await this.pool.execute(r.sql,r.parameters);if(!Array.isArray(e))throw new Error(`${this.kind} provider expected an array result`);return e}async close(){await this.pool.end()}}export{i as MySQLProtocolProvider};
1
+ import{createPool as o}from"mysql2/promise";import{mysqlCompatibleSql as t}from"./mysql-compatible-sql.js";class i{supportsRecursiveCte=!0;pool;constructor(r={}){this.pool=r.pool??o({decimalNumbers:!0,...r.connection})}quoteIdentifier(r){return t.quoteIdentifier(r)}tableAlias(r,e){return t.tableAlias(r,e)}paginate(r,e,a){return t.paginate(r,e,a)}parameter(r,e){return t.parameter(r,e)}displayQuery(r,e){return t.displayQuery(r,e)}dateBucket(r,e){return t.dateBucket(r,e)}dateShift(r,e,a){return t.dateShift(r,e,a)}aggregateFunction(r,e){return t.aggregateFunction(r,e)}stringify(r){return t.stringify(r)}concat(r){return t.concat(r)}substring(r,e,a){return t.substring(r,e,a)}regex(r,e,a){return t.regex(r,e,a)}arrayContains(r,e){const a=t.arrayContains?.bind(t);if(!a)throw new Error("MySQL-compatible array conditions are unavailable");return a(r,e)}expandArray(r,e,a){const n=t.expandArray?.bind(t);if(!n)throw new Error("MySQL-compatible array expansion is unavailable");return n(r,e,a)}async execute(r){r.signal?.throwIfAborted();const[e]=await this.pool.execute(r.sql,r.parameters);if(!Array.isArray(e))throw new Error(`${this.kind} provider expected an array result`);return e}async close(){await this.pool.end()}}export{i as MySQLProtocolProvider};
@@ -39,6 +39,7 @@ export declare class OracleProvider implements CoreComputeProvider {
39
39
  dateBucket(expression: string, level: string): string;
40
40
  dateShift(expression: string, granularity: string, offset: number): string;
41
41
  aggregateFunction(name: string, expression?: string): string;
42
+ stringify(expression: string): string;
42
43
  concat(expressions: readonly string[]): string;
43
44
  substring(expression: string, start: number, length: number): string;
44
45
  regex(expression: string, pattern: string, options?: string): string;
@@ -1 +1 @@
1
- import c from"oracledb";import{displayOracleQuery as l}from"./display-query.js";const u=o=>{if(o==null)return null;if(typeof o=="bigint")return o.toString();if(typeof o=="string"||typeof o=="number"||typeof o=="boolean"||o instanceof Date||Buffer.isBuffer(o))return o;throw new TypeError(`Unsupported Oracle parameter type: ${typeof o}`)},d=o=>o.reason instanceof Error?o.reason:new DOMException("The operation was aborted","AbortError");class p{kind="oracle";supportsHavingAlias=!1;maxInListItems=1e3;#t;#e;#r;constructor(r={}){this.#t=r.connection??{},this.#e=r.driver??{createPool:t=>c.createPool(t)},this.#r=r.pool?Promise.resolve(r.pool):void 0}quoteIdentifier(r){return`"${r.replaceAll('"','""')}"`}tableAlias(r,t){return`${r} ${this.quoteIdentifier(t)}`}paginate(r,t,e){return e>0?`${r} OFFSET ${e} ROWS FETCH NEXT ${t} ROWS ONLY`:`${r} FETCH FIRST ${t} ROWS ONLY`}parameter(r,t){return`:${r+1}`}displayQuery(r,t){return l(r,t.map(u))}dateBucket(r,t){const e={hour:"HH24",day:void 0,week:"IW",month:"MM",quarter:"Q",year:"YYYY"};if(!(t in e))throw new Error(`Unsupported Oracle date level: ${t}`);const n=e[t];return n?`TRUNC(${r}, '${n}')`:`TRUNC(${r})`}dateShift(r,t,e){if(!Number.isInteger(e))throw new Error(`Unsupported Oracle date shift: ${t}/${e}`);const i={month:1,quarter:3,year:12}[t];if(i)return`ADD_MONTHS(${r}, ${e*i})`;const s={second:{unit:"SECOND",multiplier:1},minute:{unit:"MINUTE",multiplier:1},hour:{unit:"HOUR",multiplier:1},day:{unit:"DAY",multiplier:1},week:{unit:"DAY",multiplier:7}}[t];if(!s)throw new Error(`Unsupported Oracle date shift: ${t}/${e}`);return`(${r} + NUMTODSINTERVAL(${e*s.multiplier}, '${s.unit}'))`}aggregateFunction(r,t="*"){if(!/^[A-Za-z][A-Za-z0-9_]*$/.test(r))throw new Error(`Unsupported Oracle aggregate function: ${r}`);return`${r.toUpperCase()}(${t})`}concat(r){return`(${r.join(" || ")})`}substring(r,t,e){return`SUBSTR(${r}, ${t}, ${e})`}regex(r,t,e){if(e&&e!=="i")throw new Error(`Unsupported Oracle regex options: ${e}`);return`REGEXP_LIKE(${r}, ${t}${e?", 'i'":""})`}arrayContains(r,t){return`JSON_EXISTS(${r}, '$?(@ == $value)' PASSING ${t} AS "value")`}expandArray(r,t,e){const n=this.quoteIdentifier(t),i=this.quoteIdentifier(e);return{joinSql:`CROSS APPLY JSON_TABLE(${r}, '$[*]' COLUMNS (${i} VARCHAR2(4000) PATH '$')) ${n}`,valueExpression:`${n}.${i}`}}async#o(){this.#r??=this.#e.createPool(this.#t);try{return await this.#r}catch(r){throw this.#r=void 0,r}}async execute(r){r.signal?.throwIfAborted();const t=await this.#o();r.signal?.throwIfAborted();const e=await t.getConnection(),n=r.signal,i=()=>{e.break().catch(()=>{})};n?.addEventListener("abort",i,{once:!0});try{const a=await e.execute(r.sql,r.parameters.map(u),{outFormat:c.OUT_FORMAT_OBJECT});if(n?.throwIfAborted(),!Array.isArray(a.rows))throw new Error("Oracle provider expected an array result");return a.rows}catch(a){throw n?.aborted?d(n):a}finally{n?.removeEventListener("abort",i),await e.close()}}async close(){const r=this.#r;if(!r)return;await(await r).close(0),this.#r=void 0}}export{p as OracleProvider};
1
+ import c from"oracledb";import{displayOracleQuery as l}from"./display-query.js";const u=o=>{if(o==null)return null;if(typeof o=="bigint")return o.toString();if(typeof o=="string"||typeof o=="number"||typeof o=="boolean"||o instanceof Date||Buffer.isBuffer(o))return o;throw new TypeError(`Unsupported Oracle parameter type: ${typeof o}`)},d=o=>o.reason instanceof Error?o.reason:new DOMException("The operation was aborted","AbortError");class p{kind="oracle";supportsHavingAlias=!1;maxInListItems=1e3;#t;#e;#r;constructor(r={}){this.#t=r.connection??{},this.#e=r.driver??{createPool:t=>c.createPool(t)},this.#r=r.pool?Promise.resolve(r.pool):void 0}quoteIdentifier(r){return`"${r.replaceAll('"','""')}"`}tableAlias(r,t){return`${r} ${this.quoteIdentifier(t)}`}paginate(r,t,e){return e>0?`${r} OFFSET ${e} ROWS FETCH NEXT ${t} ROWS ONLY`:`${r} FETCH FIRST ${t} ROWS ONLY`}parameter(r,t){return`:${r+1}`}displayQuery(r,t){return l(r,t.map(u))}dateBucket(r,t){const e={hour:"HH24",day:void 0,week:"IW",month:"MM",quarter:"Q",year:"YYYY"};if(!(t in e))throw new Error(`Unsupported Oracle date level: ${t}`);const n=e[t];return n?`TRUNC(${r}, '${n}')`:`TRUNC(${r})`}dateShift(r,t,e){if(!Number.isInteger(e))throw new Error(`Unsupported Oracle date shift: ${t}/${e}`);const i={month:1,quarter:3,year:12}[t];if(i)return`ADD_MONTHS(${r}, ${e*i})`;const s={second:{unit:"SECOND",multiplier:1},minute:{unit:"MINUTE",multiplier:1},hour:{unit:"HOUR",multiplier:1},day:{unit:"DAY",multiplier:1},week:{unit:"DAY",multiplier:7}}[t];if(!s)throw new Error(`Unsupported Oracle date shift: ${t}/${e}`);return`(${r} + NUMTODSINTERVAL(${e*s.multiplier}, '${s.unit}'))`}aggregateFunction(r,t="*"){if(!/^[A-Za-z][A-Za-z0-9_]*$/.test(r))throw new Error(`Unsupported Oracle aggregate function: ${r}`);return`${r.toUpperCase()}(${t})`}stringify(r){return`TO_CHAR(${r})`}concat(r){return`(${r.join(" || ")})`}substring(r,t,e){return`SUBSTR(${r}, ${t}, ${e})`}regex(r,t,e){if(e&&e!=="i")throw new Error(`Unsupported Oracle regex options: ${e}`);return`REGEXP_LIKE(${r}, ${t}${e?", 'i'":""})`}arrayContains(r,t){return`JSON_EXISTS(${r}, '$?(@ == $value)' PASSING ${t} AS "value")`}expandArray(r,t,e){const n=this.quoteIdentifier(t),i=this.quoteIdentifier(e);return{joinSql:`CROSS APPLY JSON_TABLE(${r}, '$[*]' COLUMNS (${i} VARCHAR2(4000) PATH '$')) ${n}`,valueExpression:`${n}.${i}`}}async#o(){this.#r??=this.#e.createPool(this.#t);try{return await this.#r}catch(r){throw this.#r=void 0,r}}async execute(r){r.signal?.throwIfAborted();const t=await this.#o();r.signal?.throwIfAborted();const e=await t.getConnection(),n=r.signal,i=()=>{e.break().catch(()=>{})};n?.addEventListener("abort",i,{once:!0});try{const a=await e.execute(r.sql,r.parameters.map(u),{outFormat:c.OUT_FORMAT_OBJECT});if(n?.throwIfAborted(),!Array.isArray(a.rows))throw new Error("Oracle provider expected an array result");return a.rows}catch(a){throw n?.aborted?d(n):a}finally{n?.removeEventListener("abort",i),await e.close()}}async close(){const r=this.#r;if(!r)return;await(await r).close(0),this.#r=void 0}}export{p as OracleProvider};
@@ -24,6 +24,7 @@ export declare class PostgreSQLProvider implements CoreComputeProvider {
24
24
  dateBucket(expression: string, level: string): string;
25
25
  dateShift(expression: string, granularity: string, offset: number): string;
26
26
  aggregateFunction(name: string, expression?: string): string;
27
+ stringify(expression: string): string;
27
28
  concat(expressions: readonly string[]): string;
28
29
  substring(expression: string, start: number, length: number): string;
29
30
  regex(expression: string, pattern: string, options?: string): string;
@@ -1 +1 @@
1
- import{Pool as s}from"pg";import{displayPostgreSQLQuery as a}from"./display-query.js";class i{kind="postgresql";supportsHavingAlias=!1;supportsRecursiveCte=!0;pool;constructor(r={}){this.pool=r.pool??new s(r.connection)}quoteIdentifier(r){return`"${r.replaceAll('"','""')}"`}tableAlias(r,e){return`${r} AS ${this.quoteIdentifier(e)}`}paginate(r,e,t){return`${r} LIMIT ${e}${t>0?` OFFSET ${t}`:""}`}parameter(r,e){return`$${r+1}`}displayQuery(r,e){return a(r,e)}dateBucket(r,e){if(!new Set(["hour","day","week","month","quarter","year"]).has(e))throw new Error(`Unsupported PostgreSQL date level: ${e}`);return`DATE_TRUNC('${e}', ${r})`}dateShift(r,e,t){if(!new Set(["second","minute","hour","day","week","month","quarter","year"]).has(e)||!Number.isInteger(t))throw new Error(`Unsupported PostgreSQL date shift: ${e}/${t}`);return`(${r} + (${t} * INTERVAL '1 ${e}'))`}aggregateFunction(r,e="*"){if(!/^[A-Za-z][A-Za-z0-9_]*$/.test(r))throw new Error(`Unsupported PostgreSQL aggregate function: ${r}`);return`${r.toUpperCase()}(${e})`}concat(r){return`CONCAT(${r.join(", ")})`}substring(r,e,t){return`SUBSTRING(${r} FROM ${e} FOR ${t})`}regex(r,e,t){if(t&&t!=="i")throw new Error(`Unsupported PostgreSQL regex options: ${t}`);return`${r} ${t==="i"?"~*":"~"} ${e}`}arrayContains(r,e){return`${e} = ANY(${r})`}expandArray(r,e,t){const o=this.quoteIdentifier(e),n=this.quoteIdentifier(t);return{joinSql:`CROSS JOIN LATERAL unnest(${r}) AS ${o}(${n})`,valueExpression:`${o}.${n}`}}async execute(r){r.signal?.throwIfAborted();const e=await this.pool.query(r.sql,[...r.parameters]);if(r.signal?.throwIfAborted(),!Array.isArray(e.rows))throw new Error("PostgreSQL provider expected an array result");return e.rows}async close(){await this.pool.end()}}export{i as PostgreSQLProvider};
1
+ import{Pool as s}from"pg";import{displayPostgreSQLQuery as i}from"./display-query.js";class a{kind="postgresql";supportsHavingAlias=!1;supportsRecursiveCte=!0;pool;constructor(r={}){this.pool=r.pool??new s(r.connection)}quoteIdentifier(r){return`"${r.replaceAll('"','""')}"`}tableAlias(r,e){return`${r} AS ${this.quoteIdentifier(e)}`}paginate(r,e,t){return`${r} LIMIT ${e}${t>0?` OFFSET ${t}`:""}`}parameter(r,e){return`$${r+1}`}displayQuery(r,e){return i(r,e)}dateBucket(r,e){if(!new Set(["hour","day","week","month","quarter","year"]).has(e))throw new Error(`Unsupported PostgreSQL date level: ${e}`);return`DATE_TRUNC('${e}', ${r})`}dateShift(r,e,t){if(!new Set(["second","minute","hour","day","week","month","quarter","year"]).has(e)||!Number.isInteger(t))throw new Error(`Unsupported PostgreSQL date shift: ${e}/${t}`);return`(${r} + (${t} * INTERVAL '1 ${e}'))`}aggregateFunction(r,e="*"){if(!/^[A-Za-z][A-Za-z0-9_]*$/.test(r))throw new Error(`Unsupported PostgreSQL aggregate function: ${r}`);return`${r.toUpperCase()}(${e})`}stringify(r){return`CAST(${r} AS TEXT)`}concat(r){return`CONCAT(${r.join(", ")})`}substring(r,e,t){return`SUBSTRING(${r} FROM ${e} FOR ${t})`}regex(r,e,t){if(t&&t!=="i")throw new Error(`Unsupported PostgreSQL regex options: ${t}`);return`${r} ${t==="i"?"~*":"~"} ${e}`}arrayContains(r,e){return`${e} = ANY(${r})`}expandArray(r,e,t){const o=this.quoteIdentifier(e),n=this.quoteIdentifier(t);return{joinSql:`CROSS JOIN LATERAL unnest(${r}) AS ${o}(${n})`,valueExpression:`${o}.${n}`}}async execute(r){r.signal?.throwIfAborted();const e=await this.pool.query(r.sql,[...r.parameters]);if(r.signal?.throwIfAborted(),!Array.isArray(e.rows))throw new Error("PostgreSQL provider expected an array result");return e.rows}async close(){await this.pool.end()}}export{a as PostgreSQLProvider};
@@ -14,6 +14,7 @@ export interface SqlRenderCapabilities {
14
14
  dateBucket(expression: string, level: string): string;
15
15
  dateShift(expression: string, granularity: string, offset: number): string;
16
16
  aggregateFunction(name: string, expression?: string): string;
17
+ stringify(expression: string): string;
17
18
  concat(expressions: readonly string[]): string;
18
19
  substring(expression: string, start: number, length: number): string;
19
20
  regex(expression: string, pattern: string, options?: string): string;
@@ -31,6 +31,7 @@ export declare class SnowflakeProvider implements CoreComputeProvider {
31
31
  dateBucket(expression: string, level: string): string;
32
32
  dateShift(expression: string, granularity: string, offset: number): string;
33
33
  aggregateFunction(name: string, expression?: string): string;
34
+ stringify(expression: string): string;
34
35
  concat(expressions: readonly string[]): string;
35
36
  substring(expression: string, start: number, length: number): string;
36
37
  regex(expression: string, pattern: string, options?: string): string;
@@ -1 +1 @@
1
- import $ from"snowflake-sdk";import{displayQuestionMarkQuery as h}from"./display-query.js";const p=n=>{if(n==null)return null;if(n instanceof Date)return n.toISOString();if(typeof n=="bigint")return n.toString();if(typeof n=="string"||typeof n=="number"||typeof n=="boolean")return n;throw new TypeError(`Unsupported Snowflake parameter type: ${typeof n}`)},w=n=>n.reason instanceof Error?n.reason:new DOMException("The operation was aborted","AbortError");class l{kind="snowflake";#e;#r=!1;#t;constructor(e={}){this.#e=e.client??$.createConnection(e.connection??{})}quoteIdentifier(e){return`"${e.replaceAll('"','""')}"`}tableAlias(e,r){return`${e} AS ${this.quoteIdentifier(r)}`}paginate(e,r,t){return`${e} LIMIT ${r}${t>0?` OFFSET ${t}`:""}`}parameter(e,r){return"?"}displayQuery(e,r){return h(e,r.map(p))}dateBucket(e,r){if(!new Set(["hour","day","week","month","quarter","year"]).has(r))throw new Error(`Unsupported Snowflake date level: ${r}`);return`DATE_TRUNC('${r.toUpperCase()}', ${e})`}dateShift(e,r,t){if(!new Set(["second","minute","hour","day","week","month","quarter","year"]).has(r)||!Number.isInteger(t))throw new Error(`Unsupported Snowflake date shift: ${r}/${t}`);return`DATEADD(${r.toUpperCase()}, ${t}, ${e})`}aggregateFunction(e,r="*"){if(!/^[A-Za-z][A-Za-z0-9_]*$/.test(e))throw new Error(`Unsupported Snowflake aggregate function: ${e}`);return`${e.toUpperCase()}(${r})`}concat(e){return`CONCAT(${e.join(", ")})`}substring(e,r,t){return`SUBSTRING(${e}, ${r}, ${t})`}regex(e,r,t){if(t&&t!=="i")throw new Error(`Unsupported Snowflake regex options: ${t}`);return t?`REGEXP_INSTR(${e}, ${r}, 1, 1, 0, '${t}') > 0`:`REGEXP_INSTR(${e}, ${r}) > 0`}arrayContains(e,r){return`ARRAY_CONTAINS(${r}::VARIANT, ${e})`}expandArray(e,r,t){const o=this.quoteIdentifier(r);return{joinSql:`, LATERAL FLATTEN(INPUT => ${e}) ${o}`,valueExpression:`${o}.VALUE`}}async#n(){if(!this.#r){this.#t??=new Promise((e,r)=>{this.#e.connect(t=>{if(t){r(t);return}this.#r=!0,e()})});try{await this.#t}catch(e){throw this.#t=void 0,e}}}async execute(e){e.signal?.throwIfAborted(),await this.#n(),e.signal?.throwIfAborted();const r=e.parameters.map(p);return new Promise((t,o)=>{const i=e.signal,c={};let f=!1;const a=s=>{f||(f=!0,i?.removeEventListener("abort",u),s())},u=()=>{c.current?.cancel(),i&&a(()=>o(w(i)))};i?.addEventListener("abort",u,{once:!0}),c.current=this.#e.execute({sqlText:e.sql,binds:r,complete:(s,A,d)=>{if(s){a(()=>o(s));return}if(!Array.isArray(d)){a(()=>o(new Error("Snowflake provider expected an array result")));return}a(()=>t(d))}}),i?.aborted&&u()})}async close(){this.#r&&await new Promise((e,r)=>{this.#e.destroy(t=>{if(t){r(t);return}this.#r=!1,this.#t=void 0,e()})})}}export{l as SnowflakeProvider};
1
+ import $ from"snowflake-sdk";import{displayQuestionMarkQuery as h}from"./display-query.js";const p=n=>{if(n==null)return null;if(n instanceof Date)return n.toISOString();if(typeof n=="bigint")return n.toString();if(typeof n=="string"||typeof n=="number"||typeof n=="boolean")return n;throw new TypeError(`Unsupported Snowflake parameter type: ${typeof n}`)},w=n=>n.reason instanceof Error?n.reason:new DOMException("The operation was aborted","AbortError");class l{kind="snowflake";#e;#r=!1;#t;constructor(e={}){this.#e=e.client??$.createConnection(e.connection??{})}quoteIdentifier(e){return`"${e.replaceAll('"','""')}"`}tableAlias(e,r){return`${e} AS ${this.quoteIdentifier(r)}`}paginate(e,r,t){return`${e} LIMIT ${r}${t>0?` OFFSET ${t}`:""}`}parameter(e,r){return"?"}displayQuery(e,r){return h(e,r.map(p))}dateBucket(e,r){if(!new Set(["hour","day","week","month","quarter","year"]).has(r))throw new Error(`Unsupported Snowflake date level: ${r}`);return`DATE_TRUNC('${r.toUpperCase()}', ${e})`}dateShift(e,r,t){if(!new Set(["second","minute","hour","day","week","month","quarter","year"]).has(r)||!Number.isInteger(t))throw new Error(`Unsupported Snowflake date shift: ${r}/${t}`);return`DATEADD(${r.toUpperCase()}, ${t}, ${e})`}aggregateFunction(e,r="*"){if(!/^[A-Za-z][A-Za-z0-9_]*$/.test(e))throw new Error(`Unsupported Snowflake aggregate function: ${e}`);return`${e.toUpperCase()}(${r})`}stringify(e){return`TO_VARCHAR(${e})`}concat(e){return`CONCAT(${e.join(", ")})`}substring(e,r,t){return`SUBSTRING(${e}, ${r}, ${t})`}regex(e,r,t){if(t&&t!=="i")throw new Error(`Unsupported Snowflake regex options: ${t}`);return t?`REGEXP_INSTR(${e}, ${r}, 1, 1, 0, '${t}') > 0`:`REGEXP_INSTR(${e}, ${r}) > 0`}arrayContains(e,r){return`ARRAY_CONTAINS(${r}::VARIANT, ${e})`}expandArray(e,r,t){const o=this.quoteIdentifier(r);return{joinSql:`, LATERAL FLATTEN(INPUT => ${e}) ${o}`,valueExpression:`${o}.VALUE`}}async#n(){if(!this.#r){this.#t??=new Promise((e,r)=>{this.#e.connect(t=>{if(t){r(t);return}this.#r=!0,e()})});try{await this.#t}catch(e){throw this.#t=void 0,e}}}async execute(e){e.signal?.throwIfAborted(),await this.#n(),e.signal?.throwIfAborted();const r=e.parameters.map(p);return new Promise((t,o)=>{const i=e.signal,c={};let f=!1;const a=s=>{f||(f=!0,i?.removeEventListener("abort",u),s())},u=()=>{c.current?.cancel(),i&&a(()=>o(w(i)))};i?.addEventListener("abort",u,{once:!0}),c.current=this.#e.execute({sqlText:e.sql,binds:r,complete:(s,A,d)=>{if(s){a(()=>o(s));return}if(!Array.isArray(d)){a(()=>o(new Error("Snowflake provider expected an array result")));return}a(()=>t(d))}}),i?.aborted&&u()})}async close(){this.#r&&await new Promise((e,r)=>{this.#e.destroy(t=>{if(t){r(t);return}this.#r=!1,this.#t=void 0,e()})})}}export{l as SnowflakeProvider};
@@ -1 +1 @@
1
- import{SemanticDBError as m}from"../errors.js";import{createOperatorRegistry as ce}from"../operators/index.js";import{isComposedLogicform as ue,isLogicform as te}from"../logicform-types.js";import{buildFieldMap as me}from"../schema/schema-registry.js";import{resolvePropertyPath as z}from"../schema/resolve-property-path.js";import{SchemaMappingRegistry as de}from"../schema-mappings/registry.js";import{createPlannerSemanticRegistry as ye}from"./semantics/registry-defaults.js";import{selectSchemaMapping as le,mappedPropertyColumn as ne}from"./select-schema-mapping.js";const fe=(e,o)=>`result:${e}:${o.map(({name:c,type:y,primal_type:d})=>`${c}:${y}:${d}`).join("|")}`,oe=(e,o)=>{const c=(e.columnProperties??[]).map(y=>({...y,id:y.name}));return{id:fe(e.schemaId,e.columnProperties??[]),name:`${o.name} result`,type:o.type,temporalSemantics:"normal",properties:c}},ie=(e,o)=>({...oe(e,o),id:o.id,name:o.name,type:o.type}),v=e=>{if(!e.name)throw new m("MISSING_PRED_NAME","Planner requires every normalized PredItem to have a name");return e.name},he=(e,o,c)=>{const y=new Set(c),d=(I,g)=>{if(!(te(I)||I===null)){if(Array.isArray(I)){I.forEach(l=>d(l,g));return}if(typeof I=="object"){Object.entries(I).forEach(([l,E])=>{["args","$exists","$regex","$contains"].includes(l)||d(E,l)});return}if(!(g&&["args","$exists","$regex","$contains"].includes(g))&&(typeof I!="string"||!y.has(I)))throw new m("COMPARABLE_VALUE_NOT_FOUND",`Comparable category ${o} does not contain value: ${JSON.stringify(I)}`,{property:o,value:I})}};d(e)},ge=(e,o)=>{if(!o.udf)return{};const c=e.properties.find(({type:y})=>y==="timestamp")?.id;return{sql:o.udf.sql,...c?{timestampColumn:c}:{}}},se=e=>e.sql?{kind:"property-sql",schemaId:e.schemaId,property:e.property,propertyId:e.column,sql:e.sql,qualifier:e.qualifier,...e.timestampColumn?{timestampColumn:e.timestampColumn}:{}}:{kind:"column",...e},ae=(e,o,c={})=>({name:e,type:o.type,primal_type:o.primal_type,isArray:o.isArray,...o.ref?{ref:o.ref}:{},...o.comparable?{comparable:o.comparable}:{},...c}),M=(e,o,c,y,d,I,g,l,E="projection")=>{const T=v(e);if(E==="groupby"&&e.query)throw new m("GROUPBY_QUERY_NOT_ALLOWED","A groupby expression cannot define pred.query");if(!e.operator){if(typeof e.pred!="string")throw new m("PRED_PROPERTY_REQUIRED","A projection must reference a property");const h=c(e.pred);return{expression:se(h.field),outputProperty:ae(h.reference,h.property),sourceProperty:h.property,sourceField:h.field}}const S=o.get(e.operator);if(E==="groupby"&&S.kind!=="scalar")throw new m("OPERATOR_NOT_ALLOWED_IN_GROUPBY",`Operator ${e.operator} cannot be used in groupby`,{operator:e.operator});if(E==="groupby"&&!S.toSQL)throw new m("JAVASCRIPT_OPERATOR_NOT_ALLOWED_IN_GROUPBY",`JavaScript-only operator ${e.operator} cannot be used in groupby`,{operator:e.operator});if(S.planInput&&typeof e.pred!="string")throw new m("OPERATOR_DIRECT_PROPERTY_REQUIRED",`${e.operator} requires a direct property pred`);const b=S.planInput&&typeof e.pred=="string"?S.planInput({reference:e.pred,args:e.args??{},schema:g,...l?{resolveSchema:l}:{}}):void 0,A=b?.reference??e.pred,_=b?.args??e.args??{},u=A&&typeof A=="object"&&!Array.isArray(A)?M(A,o,c,y,d,I,g,l,E):typeof A=="string"?M({pred:A,name:A},o,c,y,d,I,g,l,E):void 0,s=e.components?.map(h=>M(h,o,c,y,d,I,g,l,E))??[];if(S.requiresProperty!==!1&&!u&&s.length===0)throw new m("PRED_PROPERTY_REQUIRED",`${e.operator} requires a property`);const P=T,f=S.getOutputProperty?.({name:P,...u?.sourceProperty?{property:u.sourceProperty}:{},...u?{inputProperty:{...u.outputProperty,name:P}}:{},...s.length>0?{componentProperties:s.map(({outputProperty:h})=>h)}:{},args:_})??(u?ae(P,u.outputProperty,{isArray:!1}):{name:P,type:"number",primal_type:"number",isArray:!1});if(S.planExpression){const h=S.planExpression({pred:e,...u?{input:u.expression,inputProperty:u.outputProperty}:{},components:s.map(({expression:w})=>w),args:_,resolveKey:d,getOperator:w=>o.get(w),planQuery:I});if(h)return{expression:h,outputProperty:f,...u?.sourceProperty?{sourceProperty:u.sourceProperty}:{},...u?.sourceField?{sourceField:u.sourceField}:{}}}if(S.kind==="window"){if(!u||s.length>0||e.query)throw new m("INVALID_WINDOW_OPERATOR_INPUT",`${e.operator} requires exactly one nested pred and does not accept pred.query`);const h=e.args?.direction??"desc";if(h!=="asc"&&h!=="desc")throw new m("INVALID_WINDOW_ARGS",`${e.operator} direction must be asc or desc`);const w=e.args?.partitionBy??[];if(!Array.isArray(w)||w.some(D=>typeof D!="string"))throw new m("INVALID_WINDOW_ARGS",`${e.operator} partitionBy must be an array of groupby names`);return{expression:{kind:"window",function:S.windowFunction,input:u.expression,partitionBy:w.map(D=>y(String(D))),direction:h==="asc"?"ASC":"DESC"},outputProperty:f}}return{expression:{kind:"operator",operator:e.operator,args:_,...e.query?{query:I(e.query)}:{},...u?{input:u.expression}:{},...s.length>0?{components:s.map(({expression:h})=>h)}:{},...u?.sourceProperty?{property:u.sourceProperty.name,column:u.sourceProperty.id}:{},...u?.sourceField?{qualifier:u.sourceField.qualifier,schemaId:u.sourceField.schemaId}:{}},outputProperty:f,...u?.sourceProperty?{sourceProperty:u.sourceProperty}:{},...u?.sourceField?{sourceField:u.sourceField}:{}}},Ie=(e,o,c,y,d,I,g,l,E,T)=>{const S=v(e),b=typeof e.pred=="string"?e.pred:void 0,_=M(e,o,!T||!b?c:s=>{const P=c(s);return s===b?{...P,field:{...P.field,column:T}}:P},y,d,I,g,l),u=E?.resolveProjection({pred:e,schema:g,..._.sourceProperty?{sourceProperty:_.sourceProperty}:{},outputProperty:{..._.outputProperty,name:S},...e.operator?{operator:o.get(e.operator)}:{}})??{..._.outputProperty,name:S};return{name:S,expression:_.expression,outputProperty:u}},Se=e=>{const o=new Set;for(const c of e){if(o.has(c.name))throw new m("DUPLICATE_OUTPUT_NAME",`Duplicate output name: ${c.name}`);o.add(c.name)}},Pe=e=>{let o="__semanticdb_row_identity";for(;e.has(o);)o=`_${o}`;return o},X=(e,o,c)=>{if(!e.schema||e.schema===o.id)return o;const y=c.resolveSchema?.(e.schema);if(!y)throw new m("SCHEMA_RESOLVER_REQUIRED",`Cannot plan nested LogicForm schema without a schema resolver: ${e.schema}`,{schemaId:e.schema});return y},we=(e,o,c,y,d)=>{if(e.logicforms.length<2)throw new m("COMPOSITION_REQUIRES_MULTIPLE_LOGICFORMS","logicforms must contain at least two LogicForms");const I=["schema","from","query","preds","groupby","having","limit","page","entity_id"].filter(s=>e[s]!==void 0);if(I.length>0)throw new m("INVALID_COMPOSED_LOGICFORM",`A composed LogicForm cannot define: ${I.join(", ")}`,{fields:I});const g=e.logicforms.map(s=>F(s,X(s,o,d),c,y,d)),l=g[0]?.groupBy.map(({name:s})=>s)??[];for(const s of g){const P=s.groupBy.map(({name:f})=>f);if(P.length!==l.length||P.some((f,h)=>f!==l[h]))throw new m("INCOMPATIBLE_COMPOSITION_GROUPBY","All composed LogicForms must have the same groupby output names in the same order",{expected:l,received:P});if(s.rowIdentity.kind!==(l.length>0?"group":"singleton"))throw new m("UNSTABLE_COMPOSITION_IDENTITY","Composed LogicForms must be grouped or aggregate to a singleton row")}const E=[],T=[],S=new Map;g.forEach((s,P)=>{s.projections.forEach((f,h)=>{const w=l.includes(f.name),D=S.get(f.name);if(D!==void 0&&!w)throw new m("DUPLICATE_COMPOSITION_OUTPUT",`Composed LogicForms produce the same output: ${f.name}`,{name:f.name,childIndexes:[D,P]});if(D!==void 0)return;S.set(f.name,P),E.push({name:f.name,expression:{kind:"column",property:f.name,column:f.name}});const B=s.columnProperties?.[h];B&&T.push(B)})});const b=new Set(E.map(({name:s})=>s)),A=Object.entries(e.sort??{}).map(([s,P])=>{if(!b.has(s))throw new m("INVALID_SORT",`Sort field is not an output: ${s}`);const f=T.find(h=>h.name===s);return{name:s,direction:P===1?"ASC":"DESC",...f?.comparable?{comparable:f.comparable}:{},...f?.comparable?{expression:{kind:"column",property:s,column:s}}:{}}}),_=e.limitBy===void 0||l.length<2?void 0:{count:e.limitBy,partitionBy:l.slice(0,-1),orderBy:A.length>0?A:(()=>{const s=l.at(-1)??"",P=T.find(f=>f.name===s);return[{name:s,direction:"ASC",...P?.comparable?{comparable:P.comparable}:{}}]})()},u=e.limitBy!==void 0&&l.length<2?{kind:"rows",page:1,pageSize:e.limitBy}:void 0;return{schemaId:o.id,source:"",composition:g,fields:Object.fromEntries(E.map(({name:s})=>[s,s])),projections:E,columnProperties:T,groupBy:E.filter(({name:s})=>l.includes(s)),orderBy:A,rowIdentity:l.length>0?{kind:"group",names:l}:{kind:"singleton"},..._?{groupLimit:_}:{},...u?{pagination:u}:{}}},F=(e,o,c=ce(),y=new de,d={})=>{if(ue(e))return we(e,o,c,y,d);for(const r of e.preds??[]){if(!r.operator)continue;const t=c.get(r.operator).rewriteLogicform?.({logicform:e,schema:o});if(t)return F(t,o,c,y,d)}const I=d.semanticRegistry??ye();if(e.from){const r=X(e.from,o,d),t=F(e.from,r,c,y,d),n=ie(t,o),i={...e};delete i.from,i.schema??=n.id;const p=F(i,n,c,y,d),a=new Set(p.projections.map(({name:R})=>R)),N=t.rowIdentity.kind==="group"&&t.rowIdentity.names.every(R=>a.has(R))||t.rowIdentity.kind==="singleton"?t.rowIdentity:{kind:"generated"};return{...p,source:t.source,sourcePlan:t,rowIdentity:N}}if(!e.schema)throw new m("LOGICFORM_SCHEMA_REQUIRED","A physical LogicForm must define schema");const g=le(e,o,y);e=g.logicform;const l=new Map(g.predRewrites.map(({predIndex:r,column:t})=>[r,t])),E=I.resolveQuery({logicform:e,schema:o,...e.query?{query:e.query}:{}}),T=o.id,S=new Map,b=new Map,A=new Map,_=r=>{const t=z(o,r,d.resolveSchema);let n=T;const i=[];for(const a of t.relations){i.push(a.property.name);const N=i.join("_"),R=N;if(!S.has(N)){const q=a.targetSchema.properties.filter(({type:L})=>L==="ID"),[C]=q;if(q.length!==1||!C)throw new m(a.targetSchema.temporalSemantics==="snapshot"?"SNAPSHOT_JOIN_REQUIRES_ID_AND_TIMESTAMP":"RELATION_TARGET_ID_REQUIRED",a.targetSchema.temporalSemantics==="snapshot"?`Snapshot Schema ${a.targetSchema.id} requires an ID Property when joined`:`Relation target ${a.targetSchema.id} must contain exactly one ID property`,{schemaId:a.targetSchema.id,count:q.length});const $=a.sourceSchema.properties.find(({type:L})=>L==="timestamp"),U=a.targetSchema.properties.find(({type:L})=>L==="timestamp"),O=$?.id,Q=U?.id;if(a.targetSchema.temporalSemantics==="snapshot"){if(!U)throw new m("SNAPSHOT_JOIN_REQUIRES_ID_AND_TIMESTAMP",`Snapshot Schema ${a.targetSchema.id} requires an ID and timestamp Property when joined`,{schemaId:a.targetSchema.id});if(!$)throw new m("SNAPSHOT_JOIN_SOURCE_TIMESTAMP_REQUIRED",`Snapshot Schema ${a.targetSchema.id} cannot join from a Schema without a timestamp Property`,{schemaId:a.sourceSchema.id});const L=["second","minute","hour","day","week","month","quarter","year"],J=$.granularity??"day",k=U.granularity??"day";if(L.indexOf(J)>L.indexOf(k))throw new m("SNAPSHOT_JOIN_GRANULARITY_TOO_COARSE",`Cannot join ${a.targetSchema.id} snapshot granularity ${k} to coarser source granularity ${J}`,{sourceSchemaId:a.sourceSchema.id,targetSchemaId:a.targetSchema.id,sourceGranularity:J,targetGranularity:k})}const j=a.property.isArray?N:void 0;j&&!b.has(j)&&b.set(j,{path:j,sourceAlias:n,sourceColumn:a.property.id,...a.property.udf?{sourceSql:a.property.udf.sql,...O?{sourceTimestampColumn:O}:{}}:{},alias:`__array_${N}`});const H=y.get(a.targetSchema.id);S.set(N,{kind:"left",path:N,sourceAlias:n,sourceColumn:a.property.id,...a.property.udf?{sourceSql:a.property.udf.sql,...O?{sourceTimestampColumn:O}:{}}:{},targetSchemaId:a.targetSchema.id,targetSource:H.source,...H.sourceSql?{targetSourceSql:H.sourceSql}:{},targetAlias:R,targetColumn:C.id,...j?{sourceArrayExpansionPath:j}:{},...C.udf?{targetSql:C.udf.sql,...Q?{targetTimestampColumn:Q}:{}}:{},...a.targetSchema.temporalSemantics==="snapshot"&&O?{sourceTimestampColumn:O,sourceTimestampGranularity:$.granularity??"day"}:{},...a.targetSchema.temporalSemantics==="snapshot"&&Q?{targetTimestampColumn:Q,targetTimestampGranularity:U.granularity??"day"}:{}})}n=R}const p={schemaId:t.schema.id,property:t.property.name,propertyId:t.property.id,column:t.relations.length===0?ne(g.mapping,o,t.canonicalName)??t.property.id:t.property.id,qualifier:n,...t.property.isArray?{isArray:!0}:{},...t.property.comparable?{comparable:t.property.comparable}:{},...ge(t.schema,t.property)};return A.set(t.canonicalName,p),{reference:t.canonicalName,schema:t.schema,property:t.property,field:p}},u=r=>{if(r)for(const[t,n]of Object.entries(r))if((t==="$and"||t==="$or")&&Array.isArray(n))for(const i of n)typeof i=="object"&&i!==null&&!Array.isArray(i)&&u(i);else t.startsWith("$")||_(t)},s=r=>{if(te(r)){const t=X(r,o,d),n=F(r,t,c,y,d);if(n.projections.length!==1)throw new m("SUBQUERY_SINGLE_COLUMN_REQUIRED",`Query LogicForm must output exactly one column, received ${n.projections.length}`,{schemaId:r.schema,projectionCount:n.projections.length});return{kind:"logicform-subquery",plan:n}}return Array.isArray(r)?r.map(s):typeof r!="object"||r===null||r instanceof Date?r:Object.fromEntries(Object.entries(r).map(([t,n])=>[t,s(n)]))},P=(r,t)=>{if(typeof t!="object"||t===null||Array.isArray(t))throw new m("INVALID_RECURSIVE_QUERY","$recursive must be normalized before planning");const n=t,i=_(r),p=typeof n.schema=="string"?n.schema:i.property.ref??i.schema.id,a=p===o.id?o:d.resolveSchema?.(p);if(!a)throw new m("SCHEMA_RESOLVER_REQUIRED",`$recursive requires Schema ${p}`);const N=a.properties.find(({id:C,name:$})=>C===n.idProperty||$===n.idProperty),R=a.properties.find(({id:C,name:$})=>C===n.parentProperty||$===n.parentProperty);if(!N)throw new m("RECURSIVE_ID_PROPERTY_REQUIRED",`$recursive ID property not found: ${String(n.idProperty)}`);if(!R)throw new m("RECURSIVE_PARENT_PROPERTY_REQUIRED",`$recursive parent property not found: ${String(n.parentProperty)}`);if(!Array.isArray(n.roots)||typeof n.includeSelf!="boolean")throw new m("INVALID_RECURSIVE_QUERY","$recursive must be normalized before planning");const q=y.get(p);return{kind:"recursive-query",schemaId:p,source:q.source,...q.sourceSql?{sourceSql:q.sourceSql}:{},idColumn:N.id,parentColumn:R.id,roots:n.roots,includeSelf:n.includeSelf}},f=r=>Object.fromEntries(Object.entries(r).map(([t,n])=>{if((t==="$and"||t==="$or")&&Array.isArray(n))return[t,n.map(p=>f(p))];if(typeof n=="object"&&n!==null&&!Array.isArray(n)&&Object.prototype.hasOwnProperty.call(n,"$recursive"))return[t,{$recursive:P(t,n.$recursive)}];const i=t.startsWith("$")?void 0:_(t).property.comparable;return[t,i?{kind:"ordinal-query",comparable:i,condition:s(n)}:s(n)]})),h=r=>{const t=i=>{if(!i||typeof i!="object")return!1;if(Array.isArray(i))return i.some(t);const p=i;return Object.prototype.hasOwnProperty.call(p,"$and")||Object.values(p).some(t)};if(t(r))return r;const n=new Set(Object.keys(r).flatMap(i=>{if(i.startsWith("$"))return[];const p=z(o,i,d.resolveSchema);return p.relations.length===1&&p.property.type==="ID"?[p.relations[0]?.property.name]:[]}));return Object.fromEntries(Object.entries(r).map(([i,p])=>{if(i==="$and"||i==="$or")return Array.isArray(p)?[i,p.map(O=>typeof O=="object"&&O!==null&&!Array.isArray(O)?h(O):O)]:[i,p];if(i.startsWith("$"))return[i,p];const a=z(o,i,d.resolveSchema);if(a.relations.length===0)return[i,p];const N=a.relations.map(({property:O})=>O.name).join("_");if(S.has(N))return[i,p];const[R]=a.relations;if(!R)return[i,p];if(n.has(R.property.name))return[i,p];const q=R.property.name,C=a.canonicalName.slice(q.length+1),$=R.targetSchema.properties.find(({type:O})=>O==="ID");if(!$)throw new m("RELATION_TARGET_ID_REQUIRED",`Relation target ${R.targetSchema.id} must contain an ID property`,{schemaId:R.targetSchema.id});if(a.relations.length===1&&a.property.id===$.id)return[R.property.id,p];const U={version:e.version,schema:R.targetSchema.id,query:C?{[C]:p}:{},preds:[{pred:$.name,name:$.name}]};return[R.property.id,{$in:U}]}))},w=(e.groupby??[]).map(r=>{const t=v(r);if(r.operator==="$case"&&r.args?.mode==="auto"&&(r.args.min===void 0||r.args.max===void 0))throw new m("AUTO_CASE_GROUPBY_REQUIRES_BOUNDS","$case auto mode in groupby requires explicit args.min and args.max");const n=M(r,c,_,D,B,f,o,d.resolveSchema,"groupby");let i=n.expression;if(n.sourceProperty?.isArray&&n.sourceField){const p=`groupby:${t}`;b.set(p,{path:p,sourceAlias:n.sourceField.qualifier,sourceColumn:n.sourceField.column,...n.sourceField.sql?{sourceSql:n.sourceField.sql,...n.sourceField.timestampColumn?{sourceTimestampColumn:n.sourceField.timestampColumn}:{}}:{},alias:`__array_group_${b.size}`}),i={kind:"array-element",schemaId:n.sourceField.schemaId,property:n.sourceProperty.name,expansionPath:p}}return{name:t,expression:i,outputProperty:{...n.outputProperty,name:t,isArray:!1}}});function D(r){const t=(e.groupby??[]).findIndex(i=>typeof i.pred=="string"&&i.pred===r||i.name===r),n=t>=0?w[t]:void 0;if(!n)throw new m("WINDOW_PARTITION_NOT_GROUPED",`Window partition must reference a groupby item: ${r}`);return n.expression}function B(r){const t=(e.groupby??[]).findIndex(i=>typeof i.pred=="string"&&i.pred===r||i.name===r),n=t>=0?w[t]:void 0;return n?n.expression:se(_(r).field)}const G=r=>{r.operator&&c.get(r.operator).validatePlan?.({pred:r,groupByNames:w.map(({name:t})=>t),resolveGroupName:t=>{const n=(e.groupby??[]).findIndex(i=>i.name===t||typeof i.pred=="string"&&i.pred===t);return n>=0?w[n]?.name:void 0},getOperator:t=>c.get(t)}),r.pred&&typeof r.pred=="object"&&G(r.pred);for(const t of r.components??[])G(t)};for(const r of e.preds??[])G(r);const x=[...w,...(e.preds??[]).map((r,t)=>Ie(r,c,_,D,B,f,o,d.resolveSchema,I,l.get(t)))],Z=r=>Object.fromEntries(Object.entries(r).map(([t,n])=>{if((t==="$and"||t==="$or")&&Array.isArray(n))return[t,n.map(p=>Z(p))];const i=x.find(({name:p})=>p===t)?.outputProperty.comparable;return i&&he(n,t,i.values),[t,i?{kind:"ordinal-query",comparable:i,condition:s(n)}:s(n)]})),V=E?h(E):void 0;u(V);const Y=r=>{u(r.query),r.pred&&typeof r.pred=="object"&&Y(r.pred);for(const t of r.components??[])Y(t)};for(const r of e.preds??[])Y(r);if(x.length===0)throw new m("EMPTY_PROJECTION","LogicForm must contain preds or groupby");Se(x);const K=new Set(x.map(({name:r})=>r)),pe=w.length>0?{kind:"group",names:w.map(({name:r})=>r)}:x.every(({expression:r})=>r.kind==="column")?(()=>{const r=o.properties.find(({type:t})=>t==="ID");return r&&(d.canUsePropertyRowIdentity?.(o,r)??!0)?{kind:"property",column:r.id,internalName:Pe(K)}:{kind:"generated"}})():{kind:"singleton"},W=Object.entries(e.sort??{}).map(([r,t])=>{if(!K.has(r))throw new m("INVALID_SORT",`Sort field is not an output: ${r}`);const n=x.find(i=>i.name===r);return{name:r,direction:t===1?"ASC":"DESC",...n?.outputProperty.comparable?{comparable:n.outputProperty.comparable,expression:n.expression}:{}}}),ee=e.limit===void 0?void 0:e.limit>0&&e.limit<1?{kind:"ratio",page:e.page??1,ratio:e.limit}:{kind:"rows",page:e.page??1,pageSize:e.limit},re=e.limitBy===void 0?void 0:{count:e.limitBy,partitionBy:w.slice(0,-1).map(({name:r})=>r),orderBy:W.length>0?W:(()=>{const r=w.at(-1);return[{name:r?.name??"",direction:"ASC",...r?.outputProperty.comparable?{comparable:r.outputProperty.comparable,expression:r.expression}:{}}]})()};return{schemaId:o.id,source:g.mapping.source,...g.mapping.sourceSql?{sourceSql:g.mapping.sourceSql}:{},mappingId:g.mapping.id??g.mapping.source,sourceAlias:T,fields:{...me(o),...Object.fromEntries(o.properties.flatMap(r=>{const t=ne(g.mapping,o,r.name);return t?[[r.name,t],[r.id,t]]:[]}))},fieldReferences:Object.fromEntries(A),joins:[...S.values()],arrayExpansions:[...b.values()],projections:x.map(({name:r,expression:t})=>({name:r,expression:t})),columnProperties:x.map(({outputProperty:r})=>r),groupBy:w.map(({name:r,expression:t})=>({name:r,expression:t})),...V?{query:f(V)}:{},...e.having?{having:Z(e.having)}:{},orderBy:W,rowIdentity:pe,...ee?{pagination:ee}:{},...re?{groupLimit:re}:{}}};export{ie as createDerivedSourceSchema,F as createLogicalPlan,oe as createResultSchema};
1
+ import{SemanticDBError as m}from"../errors.js";import{createOperatorRegistry as ce}from"../operators/index.js";import{isComposedLogicform as ue,isLogicform as re}from"../logicform-types.js";import{buildFieldMap as me}from"../schema/schema-registry.js";import{resolvePropertyPath as z}from"../schema/resolve-property-path.js";import{SchemaMappingRegistry as de}from"../schema-mappings/registry.js";import{createPlannerSemanticRegistry as ye}from"./semantics/registry-defaults.js";import{selectSchemaMapping as le,mappedPropertyColumn as te}from"./select-schema-mapping.js";const fe=(e,o)=>`result:${e}:${o.map(({name:c,type:y,primal_type:d})=>`${c}:${y}:${d}`).join("|")}`,ne=(e,o)=>{const c=(e.columnProperties??[]).map(y=>({...y,id:y.name}));return{id:fe(e.schemaId,e.columnProperties??[]),name:`${o.name} result`,type:o.type,temporalSemantics:"normal",properties:c}},oe=(e,o)=>({...ne(e,o),id:o.id,name:o.name,type:o.type}),v=e=>{if(!e.name)throw new m("MISSING_PRED_NAME","Planner requires every normalized PredItem to have a name");return e.name},he=(e,o,c)=>{const y=new Set(c),d=(l,S)=>{if(!(re(l)||l===null)){if(Array.isArray(l)){l.forEach(f=>d(f,S));return}if(typeof l=="object"){Object.entries(l).forEach(([f,w])=>{["args","$exists","$regex","$contains"].includes(f)||d(w,f)});return}if(!(S&&["args","$exists","$regex","$contains"].includes(S))&&(typeof l!="string"||!y.has(l)))throw new m("COMPARABLE_VALUE_NOT_FOUND",`Comparable category ${o} does not contain value: ${JSON.stringify(l)}`,{property:o,value:l})}};d(e)},ge=(e,o,c)=>{if(!o.udf)return{};const d=o.udf.sql.includes("$TS")?e.properties.find(({type:l})=>l==="timestamp")?.id:void 0;return{sql:o.udf.sql,...d?{timestampColumn:d}:{},...d&&c?{timestampQualifier:c}:{}}},se=e=>e.sql?{kind:"property-sql",schemaId:e.schemaId,property:e.property,propertyId:e.column,sql:e.sql,qualifier:e.qualifier,...e.timestampColumn?{timestampColumn:e.timestampColumn}:{},...e.timestampQualifier?{timestampQualifier:e.timestampQualifier}:{}}:{kind:"column",...e},ie=(e,o,c={})=>({name:e,type:o.type,primal_type:o.primal_type,isArray:o.isArray,...o.ref?{ref:o.ref}:{},...o.comparable?{comparable:o.comparable}:{},...c}),Q=(e,o,c,y,d,l,S,f,w="projection")=>{const b=v(e);if(w==="groupby"&&e.query)throw new m("GROUPBY_QUERY_NOT_ALLOWED","A groupby expression cannot define pred.query");if(!e.operator){if(typeof e.pred!="string")throw new m("PRED_PROPERTY_REQUIRED","A projection must reference a property");const g=c(e.pred);return{expression:se(g.field),outputProperty:ie(g.reference,g.property),sourceProperty:g.property,sourceField:g.field}}const I=o.get(e.operator);if(w==="groupby"&&I.kind!=="scalar")throw new m("OPERATOR_NOT_ALLOWED_IN_GROUPBY",`Operator ${e.operator} cannot be used in groupby`,{operator:e.operator});if(w==="groupby"&&!I.toSQL)throw new m("JAVASCRIPT_OPERATOR_NOT_ALLOWED_IN_GROUPBY",`JavaScript-only operator ${e.operator} cannot be used in groupby`,{operator:e.operator});if(I.planInput&&typeof e.pred!="string")throw new m("OPERATOR_DIRECT_PROPERTY_REQUIRED",`${e.operator} requires a direct property pred`);const T=I.planInput&&typeof e.pred=="string"?I.planInput({reference:e.pred,args:e.args??{},schema:S,...f?{resolveSchema:f}:{}}):void 0,A=T?.reference??e.pred,_=T?.args??e.args??{},u=A&&typeof A=="object"&&!Array.isArray(A)?Q(A,o,c,y,d,l,S,f,w):typeof A=="string"?Q({pred:A,name:A},o,c,y,d,l,S,f,w):void 0,i=e.components?.map(g=>Q(g,o,c,y,d,l,S,f,w))??[];if(I.requiresProperty!==!1&&!u&&i.length===0)throw new m("PRED_PROPERTY_REQUIRED",`${e.operator} requires a property`);const P=b,h=I.getOutputProperty?.({name:P,...u?.sourceProperty?{property:u.sourceProperty}:{},...u?{inputProperty:{...u.outputProperty,name:P}}:{},...i.length>0?{componentProperties:i.map(({outputProperty:g})=>g)}:{},args:_})??(u?ie(P,u.outputProperty,{isArray:!1}):{name:P,type:"number",primal_type:"number",isArray:!1});if(I.planExpression){const g=I.planExpression({pred:e,...u?{input:u.expression,inputProperty:u.outputProperty}:{},components:i.map(({expression:E})=>E),args:_,resolveKey:d,getOperator:E=>o.get(E),planQuery:l});if(g)return{expression:g,outputProperty:h,...u?.sourceProperty?{sourceProperty:u.sourceProperty}:{},...u?.sourceField?{sourceField:u.sourceField}:{}}}if(I.kind==="window"){if(!u||i.length>0||e.query)throw new m("INVALID_WINDOW_OPERATOR_INPUT",`${e.operator} requires exactly one nested pred and does not accept pred.query`);const g=e.args?.direction??"desc";if(g!=="asc"&&g!=="desc")throw new m("INVALID_WINDOW_ARGS",`${e.operator} direction must be asc or desc`);const E=e.args?.partitionBy??[];if(!Array.isArray(E)||E.some(q=>typeof q!="string"))throw new m("INVALID_WINDOW_ARGS",`${e.operator} partitionBy must be an array of groupby names`);return{expression:{kind:"window",function:I.windowFunction,input:u.expression,partitionBy:E.map(q=>y(String(q))),direction:g==="asc"?"ASC":"DESC"},outputProperty:h}}return{expression:{kind:"operator",operator:e.operator,args:_,...e.query?{query:l(e.query)}:{},...u?{input:u.expression}:{},...i.length>0?{components:i.map(({expression:g})=>g)}:{},...u?.sourceProperty?{property:u.sourceProperty.name,column:u.sourceProperty.id}:{},...u?.sourceField?{qualifier:u.sourceField.qualifier,schemaId:u.sourceField.schemaId}:{}},outputProperty:h,...u?.sourceProperty?{sourceProperty:u.sourceProperty}:{},...u?.sourceField?{sourceField:u.sourceField}:{}}},Se=(e,o,c,y,d,l,S,f,w,b)=>{const I=v(e),T=typeof e.pred=="string"?e.pred:void 0,_=Q(e,o,!b||!T?c:i=>{const P=c(i);return i===T?{...P,field:{...P.field,column:b}}:P},y,d,l,S,f),u=w?.resolveProjection({pred:e,schema:S,..._.sourceProperty?{sourceProperty:_.sourceProperty}:{},outputProperty:{..._.outputProperty,name:I},...e.operator?{operator:o.get(e.operator)}:{}})??{..._.outputProperty,name:I};return{name:I,expression:_.expression,outputProperty:u}},Ie=e=>{const o=new Set;for(const c of e){if(c.name==="_id")throw new m("RESERVED_OUTPUT_NAME","Output name _id is reserved for executor-owned row identity");if(o.has(c.name))throw new m("DUPLICATE_OUTPUT_NAME",`Duplicate output name: ${c.name}`);o.add(c.name)}},X=(e,o,c)=>{if(!e.schema||e.schema===o.id)return o;const y=c.resolveSchema?.(e.schema);if(!y)throw new m("SCHEMA_RESOLVER_REQUIRED",`Cannot plan nested LogicForm schema without a schema resolver: ${e.schema}`,{schemaId:e.schema});return y},Pe=(e,o,c,y,d)=>{if(e.logicforms.length<2)throw new m("COMPOSITION_REQUIRES_MULTIPLE_LOGICFORMS","logicforms must contain at least two LogicForms");const l=["schema","from","query","preds","groupby","having","limit","page","entity_id"].filter(i=>e[i]!==void 0);if(l.length>0)throw new m("INVALID_COMPOSED_LOGICFORM",`A composed LogicForm cannot define: ${l.join(", ")}`,{fields:l});const S=e.logicforms.map(i=>B(i,X(i,o,d),c,y,d)),f=S[0]?.groupBy.map(({name:i})=>i)??[];for(const i of S){const P=i.groupBy.map(({name:h})=>h);if(P.length!==f.length||P.some((h,g)=>h!==f[g]))throw new m("INCOMPATIBLE_COMPOSITION_GROUPBY","All composed LogicForms must have the same groupby output names in the same order",{expected:f,received:P});if(i.rowIdentity.kind!==(f.length>0?"group":"singleton"))throw new m("UNSTABLE_COMPOSITION_IDENTITY","Composed LogicForms must be grouped or aggregate to a singleton row")}const w=[],b=[],I=new Map;S.forEach((i,P)=>{i.projections.forEach((h,g)=>{const E=f.includes(h.name),q=I.get(h.name);if(q!==void 0&&!E)throw new m("DUPLICATE_COMPOSITION_OUTPUT",`Composed LogicForms produce the same output: ${h.name}`,{name:h.name,childIndexes:[q,P]});if(q!==void 0)return;I.set(h.name,P),w.push({name:h.name,expression:{kind:"column",property:h.name,column:h.name}});const M=i.columnProperties?.[g];M&&b.push(M)})});const T=new Set(w.map(({name:i})=>i)),A=Object.entries(e.sort??{}).map(([i,P])=>{if(!T.has(i))throw new m("INVALID_SORT",`Sort field is not an output: ${i}`);const h=b.find(g=>g.name===i);return{name:i,direction:P===1?"ASC":"DESC",...h?.comparable?{comparable:h.comparable}:{},...h?.comparable?{expression:{kind:"column",property:i,column:i}}:{}}}),_=e.limitBy===void 0||f.length<2?void 0:{count:e.limitBy,partitionBy:f.slice(0,-1),orderBy:A.length>0?A:(()=>{const i=f.at(-1)??"",P=b.find(h=>h.name===i);return[{name:i,direction:"ASC",...P?.comparable?{comparable:P.comparable}:{}}]})()},u=e.limitBy!==void 0&&f.length<2?{kind:"rows",page:1,pageSize:e.limitBy}:void 0;return{schemaId:o.id,source:"",composition:S,fields:Object.fromEntries(w.map(({name:i})=>[i,i])),projections:w,columnProperties:b,groupBy:w.filter(({name:i})=>f.includes(i)),orderBy:A,rowIdentity:f.length>0?{kind:"group",names:f,separator:"_",nullLabel:"\u7A7A\u503C"}:{kind:"singleton"},..._?{groupLimit:_}:{},...u?{pagination:u}:{}}},B=(e,o,c=ce(),y=new de,d={})=>{if(ue(e))return Pe(e,o,c,y,d);for(const r of e.preds??[]){if(!r.operator)continue;const t=c.get(r.operator).rewriteLogicform?.({logicform:e,schema:o});if(t)return B(t,o,c,y,d)}const l=d.semanticRegistry??ye();if(e.from){const r=X(e.from,o,d),t=B(e.from,r,c,y,d),n=oe(t,o),s={...e};delete s.from,s.schema??=n.id;const p=B(s,n,c,y,d),a=new Set(p.projections.map(({name:N})=>N)),$=t.rowIdentity.kind==="group"&&t.rowIdentity.names.every(N=>a.has(N))||t.rowIdentity.kind==="singleton"?t.rowIdentity:{kind:"generated"};return{...p,source:t.source,sourcePlan:t,rowIdentity:$}}if(!e.schema)throw new m("LOGICFORM_SCHEMA_REQUIRED","A physical LogicForm must define schema");const S=le(e,o,y);e=S.logicform;const f=new Map(S.predRewrites.map(({predIndex:r,column:t})=>[r,t])),w=l.resolveQuery({logicform:e,schema:o,...e.query?{query:e.query}:{}}),b=o.id,I=new Map,T=new Map,A=new Map,_=r=>{const t=z(o,r,d.resolveSchema);let n=b;const s=[];for(const a of t.relations){s.push(a.property.name);const $=s.join("_"),N=$;if(!I.has($)){const R=a.targetSchema.properties.filter(({type:j})=>j==="ID"),[C]=R;if(R.length!==1||!C)throw new m(a.targetSchema.temporalSemantics==="snapshot"?"SNAPSHOT_JOIN_REQUIRES_ID_AND_TIMESTAMP":"RELATION_TARGET_ID_REQUIRED",a.targetSchema.temporalSemantics==="snapshot"?`Snapshot Schema ${a.targetSchema.id} requires an ID Property when joined`:`Relation target ${a.targetSchema.id} must contain exactly one ID property`,{schemaId:a.targetSchema.id,count:R.length});const D=a.sourceSchema.properties.find(({type:j})=>j==="timestamp"),L=a.targetSchema.properties.find(({type:j})=>j==="timestamp"),U=D?.id,O=L?.id;if(a.targetSchema.temporalSemantics==="snapshot"){if(!L)throw new m("SNAPSHOT_JOIN_REQUIRES_ID_AND_TIMESTAMP",`Snapshot Schema ${a.targetSchema.id} requires an ID and timestamp Property when joined`,{schemaId:a.targetSchema.id});if(!D)throw new m("SNAPSHOT_JOIN_SOURCE_TIMESTAMP_REQUIRED",`Snapshot Schema ${a.targetSchema.id} cannot join from a Schema without a timestamp Property`,{schemaId:a.sourceSchema.id});const j=["second","minute","hour","day","week","month","quarter","year"],J=D.granularity??"day",k=L.granularity??"day";if(j.indexOf(J)>j.indexOf(k))throw new m("SNAPSHOT_JOIN_GRANULARITY_TOO_COARSE",`Cannot join ${a.targetSchema.id} snapshot granularity ${k} to coarser source granularity ${J}`,{sourceSchemaId:a.sourceSchema.id,targetSchemaId:a.targetSchema.id,sourceGranularity:J,targetGranularity:k})}const F=a.property.isArray?$:void 0;F&&!T.has(F)&&T.set(F,{path:F,sourceAlias:n,sourceColumn:a.property.id,...a.property.udf?{sourceSql:a.property.udf.sql,...U?{sourceTimestampColumn:U}:{}}:{},alias:`__array_${$}`});const H=y.get(a.targetSchema.id);I.set($,{kind:"left",path:$,sourceAlias:n,sourceColumn:a.property.id,...a.property.udf?{sourceSql:a.property.udf.sql,...U?{sourceTimestampColumn:U}:{}}:{},targetSchemaId:a.targetSchema.id,targetSource:H.source,...H.sourceSql?{targetSourceSql:H.sourceSql}:{},targetAlias:N,targetColumn:C.id,...F?{sourceArrayExpansionPath:F}:{},...C.udf?{targetSql:C.udf.sql,...O?{targetTimestampColumn:O}:{}}:{},...a.targetSchema.temporalSemantics==="snapshot"&&U?{sourceTimestampColumn:U,sourceTimestampGranularity:D.granularity??"day"}:{},...a.targetSchema.temporalSemantics==="snapshot"&&O?{targetTimestampColumn:O,targetTimestampGranularity:L.granularity??"day"}:{}})}n=N}const p={schemaId:t.schema.id,property:t.property.name,propertyId:t.property.id,column:t.relations.length===0?te(S.mapping,o,t.canonicalName)??t.property.id:t.property.id,qualifier:n,...t.property.isArray?{isArray:!0}:{},...t.property.comparable?{comparable:t.property.comparable}:{},...ge(t.relations.length>0?o:t.schema,t.property,t.relations.length>0?b:n)};return A.set(t.canonicalName,p),{reference:t.canonicalName,schema:t.schema,property:t.property,field:p}},u=r=>{if(r)for(const[t,n]of Object.entries(r))if((t==="$and"||t==="$or")&&Array.isArray(n))for(const s of n)typeof s=="object"&&s!==null&&!Array.isArray(s)&&u(s);else t.startsWith("$")||_(t)},i=r=>{if(re(r)){const t=X(r,o,d),n=B(r,t,c,y,d);if(n.projections.length!==1)throw new m("SUBQUERY_SINGLE_COLUMN_REQUIRED",`Query LogicForm must output exactly one column, received ${n.projections.length}`,{schemaId:r.schema,projectionCount:n.projections.length});return{kind:"logicform-subquery",plan:n}}return Array.isArray(r)?r.map(i):typeof r!="object"||r===null||r instanceof Date?r:Object.fromEntries(Object.entries(r).map(([t,n])=>[t,i(n)]))},P=(r,t)=>{if(typeof t!="object"||t===null||Array.isArray(t))throw new m("INVALID_RECURSIVE_QUERY","$recursive must be normalized before planning");const n=t,s=_(r),p=typeof n.schema=="string"?n.schema:s.property.ref??s.schema.id,a=p===o.id?o:d.resolveSchema?.(p);if(!a)throw new m("SCHEMA_RESOLVER_REQUIRED",`$recursive requires Schema ${p}`);const $=a.properties.find(({id:C,name:D})=>C===n.idProperty||D===n.idProperty),N=a.properties.find(({id:C,name:D})=>C===n.parentProperty||D===n.parentProperty);if(!$)throw new m("RECURSIVE_ID_PROPERTY_REQUIRED",`$recursive ID property not found: ${String(n.idProperty)}`);if(!N)throw new m("RECURSIVE_PARENT_PROPERTY_REQUIRED",`$recursive parent property not found: ${String(n.parentProperty)}`);if(!Array.isArray(n.roots)||typeof n.includeSelf!="boolean")throw new m("INVALID_RECURSIVE_QUERY","$recursive must be normalized before planning");const R=y.get(p);return{kind:"recursive-query",schemaId:p,source:R.source,...R.sourceSql?{sourceSql:R.sourceSql}:{},idColumn:$.id,parentColumn:N.id,roots:n.roots,includeSelf:n.includeSelf}},h=r=>Object.fromEntries(Object.entries(r).map(([t,n])=>{if((t==="$and"||t==="$or")&&Array.isArray(n))return[t,n.map(p=>h(p))];if(typeof n=="object"&&n!==null&&!Array.isArray(n)&&Object.prototype.hasOwnProperty.call(n,"$recursive"))return[t,{$recursive:P(t,n.$recursive)}];const s=t.startsWith("$")?void 0:_(t).property.comparable;return[t,s?{kind:"ordinal-query",comparable:s,condition:i(n)}:i(n)]})),g=r=>{const t=s=>{if(!s||typeof s!="object")return!1;if(Array.isArray(s))return s.some(t);const p=s;return Object.prototype.hasOwnProperty.call(p,"$and")||Object.values(p).some(t)};if(t(r))return r;const n=new Set(Object.keys(r).flatMap(s=>{if(s.startsWith("$"))return[];const p=z(o,s,d.resolveSchema);return p.relations.length===1&&p.property.type==="ID"?[p.relations[0]?.property.name]:[]}));return Object.fromEntries(Object.entries(r).map(([s,p])=>{if(s==="$and"||s==="$or")return Array.isArray(p)?[s,p.map(O=>typeof O=="object"&&O!==null&&!Array.isArray(O)?g(O):O)]:[s,p];if(s.startsWith("$"))return[s,p];const a=z(o,s,d.resolveSchema);if(a.relations.length===0)return[s,p];if(a.property.udf?.sql.includes("$TS")===!0||a.relations.some(({property:O})=>O.udf?.sql.includes("$TS")===!0))return[s,p];const N=a.relations.map(({property:O})=>O.name).join("_");if(I.has(N))return[s,p];const[R]=a.relations;if(!R)return[s,p];if(n.has(R.property.name))return[s,p];const C=R.property.name,D=a.canonicalName.slice(C.length+1),L=R.targetSchema.properties.find(({type:O})=>O==="ID");if(!L)throw new m("RELATION_TARGET_ID_REQUIRED",`Relation target ${R.targetSchema.id} must contain an ID property`,{schemaId:R.targetSchema.id});if(a.relations.length===1&&a.property.id===L.id)return[R.property.id,p];const U={version:e.version,schema:R.targetSchema.id,query:D?{[D]:p}:{},preds:[{pred:L.name,name:L.name}]};return[R.property.id,{$in:U}]}))},E=(e.groupby??[]).map(r=>{const t=v(r);if(r.operator==="$case"&&r.args?.mode==="auto"&&(r.args.min===void 0||r.args.max===void 0))throw new m("AUTO_CASE_GROUPBY_REQUIRES_BOUNDS","$case auto mode in groupby requires explicit args.min and args.max");const n=Q(r,c,_,q,M,h,o,d.resolveSchema,"groupby");let s=n.expression;if(n.sourceProperty?.isArray&&n.sourceField){const p=`groupby:${t}`;T.set(p,{path:p,sourceAlias:n.sourceField.qualifier,sourceColumn:n.sourceField.column,...n.sourceField.sql?{sourceSql:n.sourceField.sql,...n.sourceField.timestampColumn?{sourceTimestampColumn:n.sourceField.timestampColumn}:{}}:{},alias:`__array_group_${T.size}`}),s={kind:"array-element",schemaId:n.sourceField.schemaId,property:n.sourceProperty.name,expansionPath:p}}return{name:t,expression:s,outputProperty:{...n.outputProperty,name:t,isArray:!1}}});function q(r){const t=(e.groupby??[]).findIndex(s=>typeof s.pred=="string"&&s.pred===r||s.name===r),n=t>=0?E[t]:void 0;if(!n)throw new m("WINDOW_PARTITION_NOT_GROUPED",`Window partition must reference a groupby item: ${r}`);return n.expression}function M(r){const t=(e.groupby??[]).findIndex(s=>typeof s.pred=="string"&&s.pred===r||s.name===r),n=t>=0?E[t]:void 0;return n?n.expression:se(_(r).field)}const G=r=>{r.operator&&c.get(r.operator).validatePlan?.({pred:r,groupByNames:E.map(({name:t})=>t),resolveGroupName:t=>{const n=(e.groupby??[]).findIndex(s=>s.name===t||typeof s.pred=="string"&&s.pred===t);return n>=0?E[n]?.name:void 0},getOperator:t=>c.get(t)}),r.pred&&typeof r.pred=="object"&&G(r.pred);for(const t of r.components??[])G(t)};for(const r of e.preds??[])G(r);const x=[...E,...(e.preds??[]).map((r,t)=>Se(r,c,_,q,M,h,o,d.resolveSchema,l,f.get(t)))],Z=r=>Object.fromEntries(Object.entries(r).map(([t,n])=>{if((t==="$and"||t==="$or")&&Array.isArray(n))return[t,n.map(p=>Z(p))];const s=x.find(({name:p})=>p===t)?.outputProperty.comparable;return s&&he(n,t,s.values),[t,s?{kind:"ordinal-query",comparable:s,condition:i(n)}:i(n)]})),V=w?g(w):void 0;u(V);const Y=r=>{u(r.query),r.pred&&typeof r.pred=="object"&&Y(r.pred);for(const t of r.components??[])Y(t)};for(const r of e.preds??[])Y(r);if(x.length===0)throw new m("EMPTY_PROJECTION","LogicForm must contain preds or groupby");Ie(x);const ae=new Set(x.map(({name:r})=>r)),pe=E.length>0?{kind:"group",names:E.map(({name:r})=>r),separator:"_",nullLabel:"\u7A7A\u503C"}:x.every(({expression:r})=>r.kind==="column")||x.some(({expression:r})=>r.kind==="property-sql"&&o.properties.some(({id:t,type:n})=>n==="ID"&&t===r.propertyId))?(()=>{const r=o.properties.find(({type:t})=>t==="ID");return r&&(d.canUsePropertyRowIdentity?.(o,r)??!0)?{kind:"property",column:r.id,internalName:"_id"}:{kind:"generated"}})():{kind:"singleton"},W=Object.entries(e.sort??{}).map(([r,t])=>{if(!ae.has(r))throw new m("INVALID_SORT",`Sort field is not an output: ${r}`);const n=x.find(s=>s.name===r);return{name:r,direction:t===1?"ASC":"DESC",...n?.outputProperty.comparable?{comparable:n.outputProperty.comparable,expression:n.expression}:{}}}),K=e.limit===void 0?void 0:e.limit>0&&e.limit<1?{kind:"ratio",page:e.page??1,ratio:e.limit}:{kind:"rows",page:e.page??1,pageSize:e.limit},ee=e.limitBy===void 0?void 0:{count:e.limitBy,partitionBy:E.slice(0,-1).map(({name:r})=>r),orderBy:W.length>0?W:(()=>{const r=E.at(-1);return[{name:r?.name??"",direction:"ASC",...r?.outputProperty.comparable?{comparable:r.outputProperty.comparable,expression:r.expression}:{}}]})()};return{schemaId:o.id,source:S.mapping.source,...S.mapping.sourceSql?{sourceSql:S.mapping.sourceSql}:{},mappingId:S.mapping.id??S.mapping.source,sourceAlias:b,fields:{...me(o),...Object.fromEntries(o.properties.flatMap(r=>{const t=te(S.mapping,o,r.name);return t?[[r.name,t],[r.id,t]]:[]}))},fieldReferences:Object.fromEntries(A),joins:[...I.values()],arrayExpansions:[...T.values()],projections:x.map(({name:r,expression:t})=>({name:r,expression:t})),columnProperties:x.map(({outputProperty:r})=>r),groupBy:E.map(({name:r,expression:t})=>({name:r,expression:t})),...V?{query:h(V)}:{},...e.having?{having:Z(e.having)}:{},orderBy:W,rowIdentity:pe,...K?{pagination:K}:{},...ee?{groupLimit:ee}:{}}};export{oe as createDerivedSourceSchema,B as createLogicalPlan,ne as createResultSchema};
@@ -1 +1 @@
1
- import{SemanticDBError as M}from"../errors.js";import{clone as v}from"../utils/clone.js";const C=e=>e.id??e.source,h=(e,r)=>{if(e===r)return!0;if(e instanceof Date||r instanceof Date)return e instanceof Date&&r instanceof Date&&e.getTime()===r.getTime();if(Array.isArray(e)||Array.isArray(r))return Array.isArray(e)&&Array.isArray(r)&&e.length===r.length&&e.every((o,s)=>h(o,r[s]));if(typeof e!="object"||e===null||typeof r!="object"||r===null)return!1;const t=Object.entries(e),n=r;return t.length===Object.keys(n).length&&t.every(([o,s])=>Object.prototype.hasOwnProperty.call(n,o)&&h(s,n[o]))},a=e=>{if(!e)return[];const r=[];for(const[t,n]of Object.entries(e))if(n!==void 0){if(t==="$or")return;if(t==="$and"){if(!Array.isArray(n))return;for(const o of n){const s=a(o);if(!s)return;r.push(...s)}continue}if(t.startsWith("$"))return;r.push({property:t,condition:n})}return r},$=(e,r)=>{if(!e)return;const t={};for(const[n,o]of Object.entries(e))if(o!==void 0){if(n==="$and"&&Array.isArray(o)){const s=o.map(d=>$(d,r)).filter(d=>d!==void 0);s.length>0&&(t[n]=s);continue}n!=="$or"&&r.some(s=>s.property===n&&h(s.condition,o))||(t[n]=o)}return Object.keys(t).length>0?t:void 0},b=(e,r)=>e.properties.find(({id:t,name:n})=>t===r||n===r),O=(e,r,t)=>{const n=b(r,t);if(n)return e.properties?e.properties[n.name]??e.properties[n.id]:n.id},I=(e,r,t)=>e.properties===void 0||O(e,r,t)!==void 0,R=(e,r,t)=>{if(!e||r.properties===void 0)return;const n=a(e);if(!n)return"query contains an unsupported logical expression";const o=n.find(({property:s})=>!I(r,t,s));return o?`query property is not available: ${o.property}`:void 0},q=e=>[...typeof e.pred=="string"?[e.pred]:e.pred&&typeof e.pred=="object"?q(e.pred):[],...e.components?.flatMap(q)??[]],w=(e,r,t,n,o)=>{if(e.match.schema!==void 0&&e.match.schema!==n)return!1;const s=typeof r.pred=="string"?b(t,r.pred):void 0,d=b(t,e.match.pred),i=r.pred===e.match.pred||!!(s&&d&&s.id===d.id);if(r.operator!==e.match.operator||!i||r.components||r.args&&Object.keys(r.args).length>0)return!1;const c=a(e.match.query);return c!==void 0&&c.every(u=>o.some(p=>p.property===u.property&&h(p.condition,u.condition)))},D=(e,r,t)=>{if(t.predMappings===void 0&&t.properties===void 0)return{mapping:t,logicform:e,predRewrites:[],priority:t.priority??0,specificity:0};const n=a(e.query);if(!n&&t.predMappings)return"query contains $or or another unsupported logical expression";const o=v(e),s=[],d=[];for(const[u,p]of(e.preds??[]).entries()){const f=a(p.query);if(!f&&t.predMappings)return`PredItem ${u} query contains an unsupported logical expression`;if(t.predMappings){const A=[...n??[],...f??[]],P=t.predMappings.filter(m=>w(m,p,r,p.schema??r.id,A));if(P.length===0)return`PredItem ${u} is not mapped`;if(P.length>1)return`PredItem ${u} matches multiple mapping rules`;const g=P[0],j=a(g.match.query)??[];d.push(...j);const l=o.preds?.[u];if(l){const m=$(l.query,j);m?l.query=m:delete l.query}s.push({predIndex:u,column:g.column});continue}const y=q(p).find(A=>!I(t,r,A));if(y)return`PredItem ${u} property is not available: ${y}`}const i=$(o.query,d);i?o.query=i:delete o.query;const c=R(o.query,t,r);if(c)return c;for(const[u,p]of(o.preds??[]).entries()){const f=R(p.query,t,r);if(f)return`PredItem ${u} ${f}`}for(const u of o.groupby??[]){const f=q(u).find(y=>!I(t,r,y));if(f)return`groupby property is not available: ${f}`}return{mapping:t,logicform:o,predRewrites:s,priority:t.priority??0,specificity:s.length}},E=(e,r,t)=>{const n=[],o=[];for(const i of t.list(r.id)){const c=D(e,r,i);typeof c=="string"?o.push({mappingId:C(i),reason:c}):n.push(c)}if(n.length===0)throw new M("QUERY_NOT_COVERED_BY_DATA_SOURCE","\u5F53\u524D\u63A5\u5165\u7684\u6570\u636E\u4E0D\u652F\u6301\u6B64\u95EE\u9898\u3002\u5982\u9700\u652F\u6301\uFF0C\u9700\u8981\u63A5\u5165\u660E\u7EC6\u5C42\u6570\u636E\uFF0C\u8BF7\u8054\u7CFB\u6570\u636E\u7BA1\u7406\u5458\u3002",{schemaId:r.id,requiredDataLayer:"detail",rejectedMappings:o});n.sort((i,c)=>c.priority-i.priority||c.specificity-i.specificity);const s=n[0],d=n.filter(({priority:i,specificity:c})=>i===s.priority&&c===s.specificity);if(d.length>1)throw new M("SCHEMA_MAPPING_AMBIGUOUS",`Multiple Schema mappings cover the LogicForm for ${r.id}`,{schemaId:r.id,mappingIds:d.map(({mapping:i})=>C(i))});return s};export{O as mappedPropertyColumn,E as selectSchemaMapping};
1
+ import{SemanticDBError as M}from"../errors.js";import{clone as v}from"../utils/clone.js";const C=r=>r.id??r.source,$=(r,e)=>{if(r===e)return!0;if(r instanceof Date||e instanceof Date)return r instanceof Date&&e instanceof Date&&r.getTime()===e.getTime();if(Array.isArray(r)||Array.isArray(e))return Array.isArray(r)&&Array.isArray(e)&&r.length===e.length&&r.every((o,i)=>$(o,e[i]));if(typeof r!="object"||r===null||typeof e!="object"||e===null)return!1;const t=Object.entries(r),n=e;return t.length===Object.keys(n).length&&t.every(([o,i])=>Object.prototype.hasOwnProperty.call(n,o)&&$(i,n[o]))},a=r=>{if(!r)return[];const e=[];for(const[t,n]of Object.entries(r))if(n!==void 0){if(t==="$or")return;if(t==="$and"){if(!Array.isArray(n))return;for(const o of n){const i=a(o);if(!i)return;e.push(...i)}continue}if(t.startsWith("$"))return;e.push({property:t,condition:n})}return e},b=(r,e)=>{if(!r)return;const t={};for(const[n,o]of Object.entries(r))if(o!==void 0){if(n==="$and"&&Array.isArray(o)){const i=o.map(d=>b(d,e)).filter(d=>d!==void 0);i.length>0&&(t[n]=i);continue}n!=="$or"&&e.some(i=>i.property===n&&$(i.condition,o))||(t[n]=o)}return Object.keys(t).length>0?t:void 0},P=(r,e)=>r.properties.find(({id:t,name:n})=>t===e||n===e),O=(r,e,t)=>{const n=P(e,t);if(n)return r.properties?r.properties[n.name]??r.properties[n.id]:n.id},I=(r,e,t)=>r.properties===void 0||O(r,e,t)!==void 0,R=(r,e,t)=>{if(!r||e.properties===void 0)return;const n=a(r);if(!n)return"query contains an unsupported logical expression";const o=n.find(({property:i})=>!I(e,t,i));return o?`query property is not available: ${o.property}`:void 0},h=r=>[...typeof r.pred=="string"?[r.pred]:r.pred&&typeof r.pred=="object"?h(r.pred):[],...r.components?.flatMap(h)??[]],w=(r,e,t,n,o)=>{if(r.match.schema!==void 0&&r.match.schema!==n)return!1;const i=typeof e.pred=="string"?P(t,e.pred):void 0,d=P(t,r.match.pred),s=e.pred===r.match.pred||!!(i&&d&&i.id===d.id);if(e.operator!==r.match.operator||!s||e.components||e.args&&Object.keys(e.args).length>0)return!1;const c=a(r.match.query);return c!==void 0&&c.every(u=>o.some(p=>p.property===u.property&&$(p.condition,u.condition)))},D=(r,e,t)=>{if(t.predMappings===void 0&&t.properties===void 0)return{mapping:t,logicform:r,predRewrites:[],priority:t.priority??0,specificity:0};const n=a(r.query);if(!n&&t.predMappings)return"query contains $or or another unsupported logical expression";const o=v(r),i=[],d=[];for(const[u,p]of(r.preds??[]).entries()){const f=a(p.query);if(!f&&t.predMappings)return`PredItem ${u} query contains an unsupported logical expression`;if(t.predMappings){const q=[...n??[],...f??[]],A=t.predMappings.filter(m=>w(m,p,e,p.schema??e.id,q));if(A.length===0)return`PredItem ${u} is not mapped`;if(A.length>1)return`PredItem ${u} matches multiple mapping rules`;const g=A[0],j=a(g.match.query)??[];d.push(...j);const l=o.preds?.[u];if(l){const m=b(l.query,j);m?l.query=m:delete l.query}i.push({predIndex:u,column:g.column});continue}const y=h(p).find(q=>!I(t,e,q));if(y)return`PredItem ${u} property is not available: ${y}`}const s=b(o.query,d);s?o.query=s:delete o.query;const c=R(o.query,t,e);if(c)return c;for(const[u,p]of(o.preds??[]).entries()){const f=R(p.query,t,e);if(f)return`PredItem ${u} ${f}`}for(const u of o.groupby??[]){const f=h(u).find(y=>!I(t,e,y));if(f)return`groupby property is not available: ${f}`}return{mapping:t,logicform:o,predRewrites:i,priority:t.priority??0,specificity:i.length}},E=(r,e,t)=>{const n=[],o=[];for(const s of t.list(e.id)){const c=D(r,e,s);typeof c=="string"?o.push({mappingId:C(s),reason:c}):n.push(c)}if(n.length===0){const s=e.name&&e.name!==e.id?`${e.name}(${e.id})`:e.id;throw new M("QUERY_NOT_COVERED_BY_DATA_SOURCE",`Schema\u300C${s}\u300D\u5F53\u524D\u63A5\u5165\u7684\u6570\u636E\u4E0D\u652F\u6301\u6B64\u95EE\u9898\u3002\u5982\u9700\u652F\u6301\uFF0C\u9700\u8981\u63A5\u5165\u660E\u7EC6\u5C42\u6570\u636E\uFF0C\u8BF7\u8054\u7CFB\u6570\u636E\u7BA1\u7406\u5458\u3002`,{schemaId:e.id,requiredDataLayer:"detail",rejectedMappings:o})}n.sort((s,c)=>c.priority-s.priority||c.specificity-s.specificity);const i=n[0],d=n.filter(({priority:s,specificity:c})=>s===i.priority&&c===i.specificity);if(d.length>1)throw new M("SCHEMA_MAPPING_AMBIGUOUS",`Multiple Schema mappings cover the LogicForm for ${e.id}`,{schemaId:e.id,mappingIds:d.map(({mapping:s})=>C(s))});return i};export{O as mappedPropertyColumn,E as selectSchemaMapping};
@@ -10,6 +10,8 @@ export interface FieldReferencePlan {
10
10
  readonly qualifier: string;
11
11
  readonly sql?: string;
12
12
  readonly timestampColumn?: string;
13
+ /** Source alias that supplies $TS when it differs from the Property's Schema. */
14
+ readonly timestampQualifier?: string;
13
15
  readonly isArray?: boolean;
14
16
  readonly comparable?: ComparableCategoryDefinition;
15
17
  }
@@ -55,6 +57,7 @@ export type Expression = {
55
57
  readonly sql: string;
56
58
  readonly qualifier?: string;
57
59
  readonly timestampColumn?: string;
60
+ readonly timestampQualifier?: string;
58
61
  } | {
59
62
  readonly kind: 'operator';
60
63
  readonly operator: string;
@@ -143,6 +146,8 @@ export interface GroupLimitPlan {
143
146
  export type RowIdentityPlan = {
144
147
  readonly kind: 'group';
145
148
  readonly names: readonly string[];
149
+ readonly separator: string;
150
+ readonly nullLabel: string;
146
151
  } | {
147
152
  readonly kind: 'property';
148
153
  readonly column: string;
@@ -1 +1 @@
1
- import{SemanticDBError as i}from"../../errors.js";import{resolvePropertyPath as y}from"../../schema/resolve-property-path.js";const d=(n,t,o)=>{const e=y(n,t,o);if(e.property.primal_type!=="object")return{schema:e.schema,reference:e.canonicalName,relation:!1};if(e.property.isArray)throw new i("ARRAY_HIERARCHY_RELATION_UNSUPPORTED",`$hierarchyLevel cannot group an array relation: ${e.canonicalName}`);if(!e.property.ref||!o)throw new i("SCHEMA_RESOLVER_REQUIRED",`$hierarchyLevel requires a Schema resolver for relation ${e.canonicalName}`);return{schema:o(e.property.ref),reference:e.canonicalName,relation:!0}},f={name:"$hierarchyLevel",defaultName:({operandName:n,args:t,locale:o})=>{const e=typeof t.level=="string"?t.level:"";return o.toLowerCase().startsWith("en")?`${n}${e?` ${e}`:""} level`:`${n}${e}\u5C42\u7EA7`},kind:"scalar",planInput({reference:n,args:t,schema:o,resolveSchema:e}){const h=Object.keys(t).filter(a=>a!=="level");if(h.length>0||typeof t.level!="string"||t.level.length===0)throw new i("INVALID_HIERARCHY_LEVEL_ARGS","$hierarchyLevel args must contain exactly one non-empty level string",{unsupportedArgs:h});const r=d(o,n,e),c=r.schema.hierarchy;if(!c)throw new i("SCHEMA_HIERARCHY_REQUIRED",`Schema ${r.schema.id} does not declare hierarchy`,{schemaId:r.schema.id});const l=r.schema.properties.find(({name:a})=>a===c.property);if(!l)throw new i("HIERARCHY_PROPERTY_NOT_FOUND",`Schema hierarchy.property does not exist: ${c.property}`);if(!r.relation&&l.name!==y(o,n,e).property.name)throw new i("HIERARCHY_PROPERTY_REQUIRED",`$hierarchyLevel pred must be an object relation or hierarchy property ${l.name}`);let s=0;const p=c.levels.find(a=>(s+=a.codeLength,a.name===t.level||a.synonyms?.includes(t.level)));if(!p)throw new i("HIERARCHY_LEVEL_NOT_FOUND",`Schema ${r.schema.id} does not declare hierarchy level: ${t.level}`,{schemaId:r.schema.id,level:t.level});const R=c.levels.reduce((a,u)=>a+u.codeLength,0),m=r.schema.properties.filter(({type:a})=>a==="ID");return{reference:r.relation&&m.length===1&&m[0]?.name===l.name?r.reference:r.relation?`${r.reference}_${l.name}`:r.reference,args:{level:p.name,codeLength:s,totalLength:R,storage:c.storage}}},toSQL({expression:n,args:t,sql:o}){if(!n)throw new Error("$hierarchyLevel requires a hierarchy pred");const e=t.codeLength,h=t.totalLength,r=t.storage;if(!Number.isInteger(e)||!Number.isInteger(h)||Number(e)<=0||Number(h)<Number(e)||r!=="compact"&&r!=="zero-padded")throw new i("INVALID_HIERARCHY_LEVEL_PLAN","$hierarchyLevel must be resolved by the planner before SQL compilation");const c=o.substring(n,1,Number(e)),l=Number(h)-Number(e);return r==="zero-padded"&&l>0?o.concat([c,o.value("0".repeat(l))]):c},getOutputProperty({name:n}){return{name:n,type:"string",primal_type:"string",isArray:!1}}};export{f as hierarchyLevelOperator};
1
+ import{SemanticDBError as i}from"../../errors.js";import{resolvePropertyPath as y}from"../../schema/resolve-property-path.js";const d=(n,t,o)=>{const e=y(n,t,o);if(e.property.primal_type!=="entity"&&e.property.primal_type!=="object")return{schema:e.schema,reference:e.canonicalName,relation:!1};if(e.property.isArray)throw new i("ARRAY_HIERARCHY_RELATION_UNSUPPORTED",`$hierarchyLevel cannot group an array relation: ${e.canonicalName}`);if(!e.property.ref||!o)throw new i("SCHEMA_RESOLVER_REQUIRED",`$hierarchyLevel requires a Schema resolver for relation ${e.canonicalName}`);return{schema:o(e.property.ref),reference:e.canonicalName,relation:!0}},f={name:"$hierarchyLevel",defaultName:({operandName:n,args:t,locale:o})=>{const e=typeof t.level=="string"?t.level:"";return o.toLowerCase().startsWith("en")?`${n}${e?` ${e}`:""} level`:`${n}${e}\u5C42\u7EA7`},kind:"scalar",planInput({reference:n,args:t,schema:o,resolveSchema:e}){const h=Object.keys(t).filter(a=>a!=="level");if(h.length>0||typeof t.level!="string"||t.level.length===0)throw new i("INVALID_HIERARCHY_LEVEL_ARGS","$hierarchyLevel args must contain exactly one non-empty level string",{unsupportedArgs:h});const r=d(o,n,e),c=r.schema.hierarchy;if(!c)throw new i("SCHEMA_HIERARCHY_REQUIRED",`Schema ${r.schema.id} does not declare hierarchy`,{schemaId:r.schema.id});const l=r.schema.properties.find(({name:a})=>a===c.property);if(!l)throw new i("HIERARCHY_PROPERTY_NOT_FOUND",`Schema hierarchy.property does not exist: ${c.property}`);if(!r.relation&&l.name!==y(o,n,e).property.name)throw new i("HIERARCHY_PROPERTY_REQUIRED",`$hierarchyLevel pred must be an object relation or hierarchy property ${l.name}`);let s=0;const p=c.levels.find(a=>(s+=a.codeLength,a.name===t.level||a.synonyms?.includes(t.level)));if(!p)throw new i("HIERARCHY_LEVEL_NOT_FOUND",`Schema ${r.schema.id} does not declare hierarchy level: ${t.level}`,{schemaId:r.schema.id,level:t.level});const R=c.levels.reduce((a,u)=>a+u.codeLength,0),m=r.schema.properties.filter(({type:a})=>a==="ID");return{reference:r.relation&&m.length===1&&m[0]?.name===l.name?r.reference:r.relation?`${r.reference}_${l.name}`:r.reference,args:{level:p.name,codeLength:s,totalLength:R,storage:c.storage}}},toSQL({expression:n,args:t,sql:o}){if(!n)throw new Error("$hierarchyLevel requires a hierarchy pred");const e=t.codeLength,h=t.totalLength,r=t.storage;if(!Number.isInteger(e)||!Number.isInteger(h)||Number(e)<=0||Number(h)<Number(e)||r!=="compact"&&r!=="zero-padded")throw new i("INVALID_HIERARCHY_LEVEL_PLAN","$hierarchyLevel must be resolved by the planner before SQL compilation");const c=o.substring(n,1,Number(e)),l=Number(h)-Number(e);return r==="zero-padded"&&l>0?o.concat([c,o.value("0".repeat(l))]):c},getOutputProperty({name:n}){return{name:n,type:"string",primal_type:"string",isArray:!1}}};export{f as hierarchyLevelOperator};
@@ -1 +1 @@
1
- import{SemanticDBError as o}from"../errors.js";const p=(a,n)=>a.properties.some(i=>i!==n&&i.id===n.name)?n.id:n.name,l=(a,n)=>a.properties.flatMap(i=>[...n.startsWith(`${i.id}_`)?[{property:i,prefix:i.id}]:[],...i.name!==i.id&&n.startsWith(`${i.name}_`)?[{property:i,prefix:i.name}]:[]]).sort((i,s)=>s.prefix.length-i.prefix.length||(s.prefix===s.property.id?1:0)-(i.prefix===i.property.id?1:0)),c=(a,n,i)=>{const s=a.properties.find(t=>t.id===n)??a.properties.find(t=>t.name===n);if(s)return{canonicalName:p(a,s),canonicalId:s.id,schema:a,property:s,relations:[]};for(const{property:t,prefix:d}of l(a,n)){if(t.primal_type!=="object"||!t.ref)continue;if(!i)throw new o("SCHEMA_RESOLVER_REQUIRED",`Resolving property path ${n} requires a Schema resolver`);const e=i(t.ref),r=c(e,n.slice(d.length+1),i);return{canonicalName:`${p(a,t)}_${r.canonicalName}`,canonicalId:`${t.id}_${r.canonicalId}`,schema:r.schema,property:r.property,relations:[{sourceSchema:a,property:t,targetSchema:e},...r.relations]}}throw new o("PROPERTY_NOT_FOUND",`Property path not found in ${a.id}: ${n}`,{schemaId:a.id,property:n})};export{c as resolvePropertyPath};
1
+ import{SemanticDBError as r}from"../errors.js";const c=(a,n)=>a.properties.some(i=>i!==n&&i.id===n.name)?n.id:n.name,l=(a,n)=>a.properties.flatMap(i=>[...n.startsWith(`${i.id}_`)?[{property:i,prefix:i.id}]:[],...i.name!==i.id&&n.startsWith(`${i.name}_`)?[{property:i,prefix:i.name}]:[]]).sort((i,s)=>s.prefix.length-i.prefix.length||(s.prefix===s.property.id?1:0)-(i.prefix===i.property.id?1:0)),o=(a,n,i)=>{const s=a.properties.find(t=>t.id===n)??a.properties.find(t=>t.name===n);if(s)return{canonicalName:c(a,s),canonicalId:s.id,schema:a,property:s,relations:[]};for(const{property:t,prefix:d}of l(a,n)){if(t.primal_type!=="entity"&&t.primal_type!=="object"||!t.ref)continue;if(!i)throw new r("SCHEMA_RESOLVER_REQUIRED",`Resolving property path ${n} requires a Schema resolver`);const p=i(t.ref),e=o(p,n.slice(d.length+1),i);return{canonicalName:`${c(a,t)}_${e.canonicalName}`,canonicalId:`${t.id}_${e.canonicalId}`,schema:e.schema,property:e.property,relations:[{sourceSchema:a,property:t,targetSchema:p},...e.relations]}}throw new r("PROPERTY_NOT_FOUND",`\u5728 Schema\uFF1A${a.name}(${a.id})\u4E2D\u672A\u627E\u5230\u5C5E\u6027: ${n}`,{schemaId:a.id,schemaName:a.name,property:n,propertyName:n})};export{o as resolvePropertyPath};
@@ -1 +1 @@
1
- import{hydrateObjectResult as t}from"./object.js";const e={name:"entity",primalType:"object",hydrateResult:t};export{e as entitySemanticType};
1
+ import{hydrateObjectResult as t}from"./object.js";const e={name:"entity",primalType:"entity",hydrateResult:t};export{e as entitySemanticType};
@@ -1 +1 @@
1
- import{SemanticDBError as i}from"../errors.js";import{resolvePlugins as f}from"../plugins/index.js";import{validateResolvedSchema as y}from"./validate-resolved-schema.js";const c=new Set(["hour","day","week","month","quarter","year"]),A=(e,l)=>{if("_id"in e||"_sid"in e)throw new i("LEGACY_PROPERTY_ID_NOT_ALLOWED",`Property ${e.name} must use id instead of _id; _sid is not supported`,{property:e.name});if("sid"in e)throw new i("PROPERTY_SID_NOT_ALLOWED",`Property ${e.name} must not declare sid`,{property:e.name});const m=e;if(m.is_comparable!==void 0||m.comparable_order!==void 0)throw new i("LEGACY_COMPARABLE_NOT_ALLOWED",`Property ${e.name} must use comparable instead of is_comparable/comparable_order`,{property:e.name});if(e.udf!==void 0){const r=e.udf;if(typeof r!="object"||r===null||Array.isArray(r))throw new i("INVALID_PROPERTY_UDF",`Property ${e.name} udf must be an object containing only sql`,{property:e.name});const n=r,o=Object.keys(n).filter(d=>d!=="sql");if(o.length>0)throw new i("UNSUPPORTED_PROPERTY_UDF",`Property ${e.name} udf only supports sql`,{property:e.name,unsupportedKeys:o});if(typeof n.sql!="string"||n.sql.trim().length===0)throw new i("INVALID_PROPERTY_UDF",`Property ${e.name} udf.sql must be a non-empty string`,{property:e.name})}const s=l.get(e.type);if(e.primal_type!==void 0&&e.primal_type!==s.primalType)throw new i("PRIMAL_TYPE_MISMATCH",`Property ${e.name} declares primal_type ${e.primal_type}, but ${e.type} requires ${s.primalType}`,{property:e.name,type:e.type,declared:e.primal_type,expected:s.primalType});if(e.isArray!==void 0&&typeof e.isArray!="boolean")throw new i("INVALID_IS_ARRAY",`Property ${e.name} isArray must be a boolean`,{property:e.name,isArray:e.isArray});if("is_observation"in e)throw new i("LEGACY_TEMPORAL_SEMANTICS_NOT_ALLOWED",`Property ${e.name} must use Schema temporalSemantics; is_observation is not supported`,{property:e.name});if(e.comparable!==void 0){const r=e.comparable;if(typeof r!="object"||r===null||Array.isArray(r))throw new i("INVALID_COMPARABLE_CATEGORY",`Property ${e.name} comparable must contain values and direction`);const n=r;if(Object.keys(n).some(o=>o!=="values"&&o!=="direction")||!Array.isArray(n.values)||n.values.length===0||n.values.some(o=>typeof o!="string")||new Set(n.values).size!==n.values.length||n.direction!==1&&n.direction!==-1)throw new i("INVALID_COMPARABLE_CATEGORY",`Property ${e.name} comparable requires unique string values and direction 1 or -1`,{property:e.name,comparable:r});if(e.type!=="category"||e.isArray===!0)throw new i("COMPARABLE_CATEGORY_REQUIRED",`Property ${e.name} comparable is only supported by scalar category properties`,{property:e.name,type:e.type})}const t={...e,id:e.id??e.name,primal_type:s.primalType,isArray:e.isArray??!1,is_dynamic:e.udf?.sql!==void 0,...s.primalType==="date"?{granularity:e.granularity??"day"}:{}};if(s.primalType==="object"){if(typeof t.ref!="string"||t.ref.length===0)throw new i("PROPERTY_REF_REQUIRED",`Object property ${t.name} must declare ref`,{property:t.name,type:t.type})}else if(t.ref!==void 0)throw new i("PROPERTY_REF_NOT_ALLOWED",`Property ${t.name} cannot declare ref unless its primal_type is object`,{property:t.name,type:t.type});if(s.primalType==="date"){if(t.granularity!==void 0&&!c.has(t.granularity))throw new i("INVALID_GRANULARITY",`Property ${t.name} has unsupported granularity: ${t.granularity}`,{property:t.name,granularity:t.granularity})}else if(t.granularity!==void 0)throw new i("GRANULARITY_NOT_ALLOWED",`Property ${t.name} cannot declare granularity unless its primal_type is date`,{property:t.name,type:t.type});return s.validateProperty?.(t),t},P=(e,l,m=[])=>{const s=f(m);if("_id"in e||"_sid"in e)throw new i("LEGACY_SCHEMA_ID_NOT_ALLOWED","Schema must use id instead of _id; _sid is not supported");if("sid"in e)throw new i("SCHEMA_SID_NOT_ALLOWED","Schema must not declare sid");if("is_snapshot"in e||"is_observation"in e)throw new i("LEGACY_TEMPORAL_SEMANTICS_NOT_ALLOWED","Schema must use temporalSemantics instead of is_snapshot/is_observation",{schemaId:e.id});const t=e.temporalSemantics??"normal";if(t!=="normal"&&t!=="snapshot")throw new i("INVALID_TEMPORAL_SEMANTICS","Schema temporalSemantics must be normal or snapshot",{schemaId:e.id,temporalSemantics:t});const r=t;if(!e.id)throw new i("INVALID_SCHEMA_ID","Schema id must not be empty");let n=e.properties.map(a=>A(a,l));if(e.type==="entity"&&!n.some(({type:a})=>a==="timestamp")&&n.some(({type:a})=>a==="start_date")&&n.some(({type:a})=>a==="end_date")&&!n.some(({name:a})=>a==="\u65E5\u671F")){const a=n.find(({type:u})=>u==="start_date");a&&(n=[...n,{id:"__chatbi__timestamp__placeholder__",name:"\u65E5\u671F",type:"timestamp",primal_type:"date",isArray:!1,is_dynamic:!0,is_supplementary:!0,udf:{sql:a.id}}])}y(n,{temporalSemantics:r,schemaId:e.id});const o=s.flatMap(a=>a.schemaMetadata?[a.schemaMetadata]:[]);let d={};for(const a of o)d={...d,...a.resolve(e,n)};const _=e.parentProperty===void 0?void 0:n.find(({id:a,name:u})=>a===e.parentProperty||u===e.parentProperty);if(e.parentProperty!==void 0&&!_)throw new i("RECURSIVE_PARENT_PROPERTY_REQUIRED",`Schema parentProperty does not exist: ${e.parentProperty}`,{schemaId:e.id,property:e.parentProperty});const p=new Set(s.flatMap(a=>[...a.schemaMetadata?.keys??[],...a.legacySchemaKeys??[]]));return{...Object.fromEntries(Object.entries(e).filter(([a])=>a!=="parentProperty"&&!p.has(a))),temporalSemantics:r,properties:n,...d,..._?{parentProperty:_.name}:{}}};export{P as resolveSchema};
1
+ import{SemanticDBError as i}from"../errors.js";import{resolvePlugins as y}from"../plugins/index.js";import{validateResolvedSchema as f}from"./validate-resolved-schema.js";const c=new Set(["hour","day","week","month","quarter","year"]),A=(e,_)=>{if("_id"in e||"_sid"in e)throw new i("LEGACY_PROPERTY_ID_NOT_ALLOWED",`Property ${e.name} must use id instead of _id; _sid is not supported`,{property:e.name});if("sid"in e)throw new i("PROPERTY_SID_NOT_ALLOWED",`Property ${e.name} must not declare sid`,{property:e.name});const l=e;if(l.is_comparable!==void 0||l.comparable_order!==void 0)throw new i("LEGACY_COMPARABLE_NOT_ALLOWED",`Property ${e.name} must use comparable instead of is_comparable/comparable_order`,{property:e.name});if(e.udf!==void 0){const n=e.udf;if(typeof n!="object"||n===null||Array.isArray(n))throw new i("INVALID_PROPERTY_UDF",`Property ${e.name} udf must be an object containing only sql`,{property:e.name});const r=n,o=Object.keys(r).filter(m=>m!=="sql");if(o.length>0)throw new i("UNSUPPORTED_PROPERTY_UDF",`Property ${e.name} udf only supports sql`,{property:e.name,unsupportedKeys:o});if(typeof r.sql!="string"||r.sql.trim().length===0)throw new i("INVALID_PROPERTY_UDF",`Property ${e.name} udf.sql must be a non-empty string`,{property:e.name})}const s=_.get(e.type),d=e.primal_type==="object"?"entity":e.primal_type;if(d!==void 0&&d!==s.primalType)throw new i("PRIMAL_TYPE_MISMATCH",`Property ${e.name} declares primal_type ${e.primal_type}, but ${e.type} requires ${s.primalType}`,{property:e.name,type:e.type,declared:e.primal_type,expected:s.primalType});if(e.isArray!==void 0&&typeof e.isArray!="boolean")throw new i("INVALID_IS_ARRAY",`Property ${e.name} isArray must be a boolean`,{property:e.name,isArray:e.isArray});if("is_observation"in e)throw new i("LEGACY_TEMPORAL_SEMANTICS_NOT_ALLOWED",`Property ${e.name} must use Schema temporalSemantics; is_observation is not supported`,{property:e.name});if(e.comparable!==void 0){const n=e.comparable;if(typeof n!="object"||n===null||Array.isArray(n))throw new i("INVALID_COMPARABLE_CATEGORY",`Property ${e.name} comparable must contain values and direction`);const r=n;if(Object.keys(r).some(o=>o!=="values"&&o!=="direction")||!Array.isArray(r.values)||r.values.length===0||r.values.some(o=>typeof o!="string")||new Set(r.values).size!==r.values.length||r.direction!==1&&r.direction!==-1)throw new i("INVALID_COMPARABLE_CATEGORY",`Property ${e.name} comparable requires unique string values and direction 1 or -1`,{property:e.name,comparable:n});if(e.type!=="category"||e.isArray===!0)throw new i("COMPARABLE_CATEGORY_REQUIRED",`Property ${e.name} comparable is only supported by scalar category properties`,{property:e.name,type:e.type})}const a={...e,id:e.id??e.name,primal_type:s.primalType,isArray:e.isArray??!1,is_dynamic:e.udf?.sql!==void 0,...s.primalType==="date"?{granularity:e.granularity??"day"}:{}};if(s.primalType==="entity"||s.primalType==="object"){if(typeof a.ref!="string"||a.ref.length===0)throw new i("PROPERTY_REF_REQUIRED",`Entity property ${a.name} must declare ref`,{property:a.name,type:a.type})}else if(a.ref!==void 0)throw new i("PROPERTY_REF_NOT_ALLOWED",`Property ${a.name} cannot declare ref unless its primal_type is entity`,{property:a.name,type:a.type});if(s.primalType==="date"){if(a.granularity!==void 0&&!c.has(a.granularity))throw new i("INVALID_GRANULARITY",`Property ${a.name} has unsupported granularity: ${a.granularity}`,{property:a.name,granularity:a.granularity})}else if(a.granularity!==void 0)throw new i("GRANULARITY_NOT_ALLOWED",`Property ${a.name} cannot declare granularity unless its primal_type is date`,{property:a.name,type:a.type});return s.validateProperty?.(a),a},P=(e,_,l=[])=>{const s=y(l);if("_id"in e||"_sid"in e)throw new i("LEGACY_SCHEMA_ID_NOT_ALLOWED","Schema must use id instead of _id; _sid is not supported");if("sid"in e)throw new i("SCHEMA_SID_NOT_ALLOWED","Schema must not declare sid");if("is_snapshot"in e||"is_observation"in e)throw new i("LEGACY_TEMPORAL_SEMANTICS_NOT_ALLOWED","Schema must use temporalSemantics instead of is_snapshot/is_observation",{schemaId:e.id});const d=e.temporalSemantics??"normal";if(d!=="normal"&&d!=="snapshot")throw new i("INVALID_TEMPORAL_SEMANTICS","Schema temporalSemantics must be normal or snapshot",{schemaId:e.id,temporalSemantics:d});const a=d;if(!e.id)throw new i("INVALID_SCHEMA_ID","Schema id must not be empty");let n=e.properties.map(t=>A(t,_));if(e.type==="entity"&&!n.some(({type:t})=>t==="timestamp")&&n.some(({type:t})=>t==="start_date")&&n.some(({type:t})=>t==="end_date")&&!n.some(({name:t})=>t==="\u65E5\u671F")){const t=n.find(({type:u})=>u==="start_date");t&&(n=[...n,{id:"__chatbi__timestamp__placeholder__",name:"\u65E5\u671F",type:"timestamp",primal_type:"date",isArray:!1,is_dynamic:!0,is_supplementary:!0,udf:{sql:t.id}}])}f(n,{temporalSemantics:a,schemaId:e.id});const r=s.flatMap(t=>t.schemaMetadata?[t.schemaMetadata]:[]);let o={};for(const t of r)o={...o,...t.resolve(e,n)};const m=e.parentProperty===void 0?void 0:n.find(({id:t,name:u})=>t===e.parentProperty||u===e.parentProperty);if(e.parentProperty!==void 0&&!m)throw new i("RECURSIVE_PARENT_PROPERTY_REQUIRED",`Schema parentProperty does not exist: ${e.parentProperty}`,{schemaId:e.id,property:e.parentProperty});const p=new Set(s.flatMap(t=>[...t.schemaMetadata?.keys??[],...t.legacySchemaKeys??[]]));return{...Object.fromEntries(Object.entries(e).filter(([t])=>t!=="parentProperty"&&!p.has(t))),temporalSemantics:a,properties:n,...o,...m?{parentProperty:m.name}:{}}};export{P as resolveSchema};
@@ -1,6 +1,7 @@
1
1
  import type { PropertyTypeDraft, SchemaTypeDraft } from 'semanticdb-core';
2
2
  import type { LogicformType, PredItemType } from '../logicform-types';
3
- export type PrimalType = 'string' | 'number' | 'boolean' | 'date' | 'object';
3
+ /** Canonical primitive types use `entity`; `object` remains a legacy input alias. */
4
+ export type PrimalType = 'string' | 'number' | 'boolean' | 'date' | 'entity' | 'object';
4
5
  export type TimeGranularity = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year';
5
6
  export type TemporalSemantics = 'normal' | 'snapshot';
6
7
  export interface PropertySqlDefinition {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "semanticdb-v2",
3
- "version": "2.0.0-alpha.1",
3
+ "version": "2.0.0-alpha.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",