@gqlite/gql2sql
Advanced tools
+2
-3
@@ -1,5 +0,4 @@ | ||
| import{createRequire as B}from"node:module";var M=B(import.meta.url);import{parse as w}from"graphql";function v(E){if(E.length===0)return[];return E.map((H)=>{let z={};for(let I in H){let W=H[I];if(typeof W==="string"&&(W.startsWith("{")||W.startsWith("[")))try{let Y=JSON.parse(W);if(Array.isArray(Y))z[I]=Y.filter(($)=>$!==null);else z[I]=Y}catch{z[I]=W}else z[I]=W}return z})}class k{schema;paramCounter=0;constructor(E){this.schema=E}convert(E){try{let H=w(E),z=this.extractQueryContext(H),{sql:I,params:W}=this.buildSQLQuery(z);return{sql:I,params:W}}catch(H){let z=H instanceof Error?H.message:String(H);throw new Error(`GraphQL to SQL conversion failed: ${z}`)}}async convertAndExecute(E,H){try{let{sql:z,params:I}=this.convert(E),W=this.executeQuery(z,I,H);return v(W)}catch(z){let I=z instanceof Error?z.message:String(z);throw new Error(`GraphQL to SQL conversion failed: ${I}`)}}extractQueryContext(E){let H=E.definitions[0];if(!H||H.kind!=="OperationDefinition"||H.operation!=="query")throw new Error("Only query operations are supported");let z=H.selectionSet.selections[0];if(z.kind!=="Field")throw new Error("Invalid query structure");let I=this.getTableName(z.name.value),W="t0";return{tableName:I,alias:W,selections:this.extractSelections(z.selectionSet,z.name.value),joins:[],where:this.extractWhereConditions(z.arguments||[],I,W),orderBy:this.extractOrderBy(z.arguments||[]),limit:this.extractLimit(z.arguments||[]),offset:this.extractOffset(z.arguments||[])}}extractSelections(E,H){if(!E)return[];let z=this.schema[H];if(!z)return[];return E.selections.filter((I)=>I.kind==="Field").map((I)=>{let W=I.name.value,Y=z[W],$=I.selectionSet&&I.selectionSet.selections.length>0,V=!!(Y?.relation&&$),X,U,D,Z;if(V&&I.arguments&&I.arguments.length>0){let _=`nested_${W}`;X=this.extractWhereConditions(I.arguments,Y?.relation?.table||H,_),U=this.extractOrderBy(I.arguments),D=this.extractLimit(I.arguments),Z=this.extractOffset(I.arguments)}return{fieldName:W,isRelation:V,relationInfo:Y?.relation,whereConditions:X,orderBy:U,limit:D,offset:Z,nestedSelections:V&&Y?.relation?this.extractSelections(I.selectionSet,Y.relation.table):void 0}})}extractWhereConditions(E,H,z){let I=[];for(let W of E)if(W.name.value==="where"&&W.value.kind==="ObjectValue")for(let Y of W.value.fields){let $=this.sanitizeFieldName(Y.name.value);if(Y.value.kind==="StringValue"||Y.value.kind==="IntValue"||Y.value.kind==="FloatValue"){let V=`param_${++this.paramCounter}`,X=Y.value.value;if(Y.value.kind==="IntValue")X=Number.parseInt(X,10);else if(Y.value.kind==="FloatValue")X=Number.parseFloat(X);I.push({field:`${z}.${$}`,operator:"=",value:X,paramName:V})}else if(Y.value.kind==="Variable"){let V=Y.value.name.value;I.push({field:`${z}.${$}`,operator:"=",value:`$${V}`,paramName:V})}else if(Y.value.kind==="ObjectValue")for(let V of Y.value.fields){let X=V.name.value;if(V.value.kind==="StringValue"||V.value.kind==="IntValue"||V.value.kind==="FloatValue"){let U=`param_${++this.paramCounter}`,D=this.mapGraphQLOperatorToSQL(X),Z=V.value.value;if(V.value.kind==="IntValue")Z=Number.parseInt(Z,10);else if(V.value.kind==="FloatValue")Z=Number.parseFloat(Z);I.push({field:`${z}.${$}`,operator:D,value:Z,paramName:U})}else if(V.value.kind==="Variable"){let U=V.value.name.value,D=this.mapGraphQLOperatorToSQL(X);I.push({field:`${z}.${$}`,operator:D,value:`$${U}`,paramName:U})}}}else if(W.name.value!=="orderBy"&&W.name.value!=="limit"&&W.name.value!=="offset"){let Y=this.sanitizeFieldName(W.name.value);if(W.value.kind==="StringValue"||W.value.kind==="IntValue"||W.value.kind==="FloatValue"){let $=`param_${++this.paramCounter}`,V=W.value.value;if(W.value.kind==="IntValue")V=Number.parseInt(V,10);else if(W.value.kind==="FloatValue")V=Number.parseFloat(V);I.push({field:`${z}.${Y}`,operator:"=",value:V,paramName:$})}else if(W.value.kind==="Variable"){let $=W.value.name.value;I.push({field:`${z}.${Y}`,operator:"=",value:`$${$}`,paramName:$})}}return I}mapGraphQLOperatorToSQL(E){return{eq:"=",ne:"!=",gt:">",gte:">=",lt:"<",lte:"<=",like:"LIKE",ilike:"LIKE",in:"IN",notIn:"NOT IN"}[E]||"="}extractOrderBy(E){let H=[];for(let z of E)if(z.name.value==="orderBy"&&z.value.kind==="ObjectValue"){for(let I of z.value.fields)if(I.value.kind==="EnumValue")H.push({field:this.sanitizeFieldName(I.name.value),direction:I.value.value==="DESC"?"DESC":"ASC"})}return H}extractLimit(E){let H=E.find((z)=>z.name.value==="limit");if(H&&H.value.kind==="IntValue"){let z=Number.parseInt(H.value.value);return Math.min(Math.max(z,1),1000)}return}extractOffset(E){let H=E.find((z)=>z.name.value==="offset");if(H&&H.value.kind==="IntValue"){let z=Number.parseInt(H.value.value);return Math.max(z,0)}return}buildSQLQuery(E){let H=this.buildJoins(E),z=new Map,I=this.buildSelectFields(E,H,z),W=this.buildWhereClause(E.where,z),Y=this.buildOrderByClause(E.orderBy,E.alias),$=this.buildLimitClause(E.limit,E.offset),V=this.buildGroupByClause(E,H),X=`SELECT ${I} FROM ${E.tableName} AS ${E.alias} ${H.map((Z)=>`${Z.joinType} JOIN ${Z.table} AS ${Z.alias} ON ${Z.condition}`).join(` | ||
| `)} ${W?`WHERE ${W} `:""}${V?`GROUP BY ${V} `:""}${Y?`ORDER BY ${Y} `:""}`+`${$}`.trim().replace(/\s+/g," "),U={},D=0;for(let[Z,_]of z.entries())U[D.toString()]=_,D++;return{sql:X,params:U}}collectNestedParams(E,H){for(let z of E){if(z.whereConditions)H.push(...z.whereConditions.map((I)=>I.value));if(z.nestedSelections)this.collectNestedParams(z.nestedSelections,H)}}buildGroupByClause(E,H){if(E.selections.some((I)=>I.isRelation&&I.relationInfo?.type==="one-to-many"))return`${E.alias}.id`;return""}buildJoins(E){let H=[];return this.collectJoins(E.selections,E.tableName,E.alias,H,new Map,"",0),H}collectJoins(E,H,z,I,W=new Map,Y="",$=0){let V=I.length+1;for(let X of E)if(X.isRelation&&X.relationInfo){let U=Y?`${Y}.${X.fieldName}`:X.fieldName,D=`${U}`,Z=W.get(D),_=$>0&&X.relationInfo.type==="one-to-many";if(!Z&&!_){Z=`t${V++}`,W.set(D,Z);let G=this.buildJoinCondition(z,Z,X.relationInfo);I.push({table:X.relationInfo.table,alias:Z,joinType:"LEFT",condition:G,path:U})}if(X.nestedSelections&&X.nestedSelections.length>0&&!_)this.collectJoins(X.nestedSelections,X.relationInfo.table,Z||z,I,W,U,$+1)}}buildJoinCondition(E,H,z){switch(z.type){case"one-to-many":return`${E}.id = ${H}.${z.foreignKey}`;case"many-to-one":return`${E}.${z.foreignKey} = ${H}.id`;case"many-to-many":throw new Error("Many-to-many relations require junction table handling");default:throw new Error(`Unsupported relation type: ${z.type}`)}}buildSelectFields(E,H,z){let I=[],W=new Map(H.map((Y)=>[Y.table,Y.alias]));return this.buildSelectFieldsRecursive(E.selections,E.tableName,E.alias,I,W,H,z,""),I.join(", ")}buildSelectFieldsRecursive(E,H,z,I,W,Y,$,V=""){for(let X of E){let U=X.fieldName,D=V?`${V}_${U}`:U;if(X.isRelation&&X.relationInfo){let Z=X.relationInfo.table,_=this.findJoinAlias(Y,Z,z);if(_)if(X.nestedSelections&&X.nestedSelections.length>0)if((X.relationInfo.type==="one-to-many"||X.whereConditions&&X.whereConditions.length>0)&&X.relationInfo.type==="one-to-many"){let O=this.buildSubqueryFields(X.nestedSelections,X.relationInfo.table,0,!0,"sub",$);if(O){let{whereClause:K,paramMap:R}=this.buildSubqueryWhereClause("sub",X.relationInfo.foreignKey,z,X.whereConditions,$);for(let[J,P]of R.entries())$.set(J,P);let Q="",L="";if(X.orderBy&&X.orderBy.length>0)Q=` ORDER BY ${X.orderBy.map((P)=>{return`sub.${this.sanitizeFieldName(P.field)} ${P.direction}`}).join(", ")}`;if(X.limit!==void 0||X.offset!==void 0)L=this.buildLimitClause(X.limit,X.offset);let T=L||Q?(()=>{let J=this.buildSubqueryColumnList(X.nestedSelections,"sub");return`(SELECT json_group_array(json_object(${O})) FROM (SELECT ${J} FROM ${X.relationInfo.table} sub WHERE ${K}${Q}${L?` ${L}`:""}) sub)`})():`(SELECT json_group_array(json_object(${O})) FROM ${X.relationInfo.table} sub WHERE ${K})`;I.push(`${T} AS ${this.sanitizeFieldName(D)}`)}}else{let O=this.buildJsonFields(X.nestedSelections,Z,_,Y,$,0);if(X.relationInfo.type==="one-to-many")I.push(`json_group_array(CASE WHEN ${_}.id IS NOT NULL THEN json_object(${O}) ELSE NULL END ) AS ${this.sanitizeFieldName(D)}`);else I.push(`CASE WHEN ${_}.id IS NOT NULL THEN json_object(${O}) ELSE NULL END AS ${this.sanitizeFieldName(D)}`)}else if(X.relationInfo.type==="many-to-one"){let G=this.schema[Z];if(G){let O=Object.keys(G).filter((K)=>!G[K]?.relation).map((K)=>`'${K}', ${_}.${this.sanitizeFieldName(K)}`).join(", ");I.push(`CASE WHEN ${_}.id IS NOT NULL THEN json_object(${O}) ELSE NULL END AS ${this.sanitizeFieldName(D)}`)}}else{let G=this.schema[Z];if(G){let K=`(SELECT json_group_array(json_object(${Object.keys(G).filter((R)=>!G[R]?.relation).map((R)=>`'${R}', sub.${this.sanitizeFieldName(R)}`).join(", ")})) FROM ${Z} sub WHERE sub.${X.relationInfo.foreignKey} = ${z}.id)`;I.push(`${K} AS ${this.sanitizeFieldName(D)}`)}}}else I.push(`${z}.${this.sanitizeFieldName(U)} AS ${this.sanitizeFieldName(D)}`)}}findJoinAlias(E,H,z){return E.find((W)=>W.table===H&&W.condition.includes(z))?.alias||null}buildJsonFields(E,H,z,I,W,Y=0){let $=[];for(let V of E){let X=V.fieldName;if(V.isRelation&&V.relationInfo&&V.nestedSelections){let U=this.findJoinAlias(I,V.relationInfo.table,z);if(V.relationInfo.type==="one-to-many"){if(Y>0){let D=this.buildSubqueryFields(V.nestedSelections,V.relationInfo.table,0,!0,"sub",W);if(D){let{whereClause:Z,paramMap:_}=this.buildSubqueryWhereClause("sub",V.relationInfo.foreignKey,z,V.whereConditions);for(let[R,Q]of _.entries())W.set(R,Q);let G="",O="";if(V.orderBy&&V.orderBy.length>0)G=` ORDER BY ${V.orderBy.map((Q)=>{return`sub.${this.sanitizeFieldName(Q.field)} ${Q.direction}`}).join(", ")}`;if(V.limit!==void 0||V.offset!==void 0)O=this.buildLimitClause(V.limit,V.offset);let K=O||G?(()=>{let R=D.split(", ").filter((Q,L)=>L%2===1).join(", ");return`(SELECT json_group_array(json_object(${D})) FROM (SELECT ${R} FROM ${V.relationInfo.table} sub WHERE ${Z}${G}${O?` ${O}`:""}) sub)`})():`(SELECT json_group_array(json_object(${D})) FROM ${V.relationInfo.table} sub WHERE ${Z})`;$.push(`'${X}', ${K}`)}}else if(U){let D=this.buildJsonFields(V.nestedSelections,V.relationInfo.table,U,I,W,Y+1);$.push(`'${X}', json_group_array(json_object(${D}))`)}}else if(U){let D=this.buildJsonFields(V.nestedSelections,V.relationInfo.table,U,I,W,Y+1);$.push(`'${X}', json_object(${D})`)}}else $.push(`'${X}', ${z}.${this.sanitizeFieldName(X)}`)}return $.join(", ")}buildSubqueryWhereClause(E,H,z,I,W=new Map){let Y=[`${E}.${H} = ${z}.id`];if(I&&I.length>0)for(let V of I){let X=V.field.replace(/^[^.]+\./,`${E}.`),U=`param_${E}_${V.field.replace(".","_")}_${W.size}`;Y.push(`${X} ${V.operator} $${U}`),W.set(U,V.value)}return{whereClause:Y.join(" AND "),paramMap:W}}buildSubqueryColumnList(E,H){let z=[];for(let I of E)if(!I.isRelation)z.push(`${H}.${this.sanitizeFieldName(I.fieldName)}`);else if(I.relationInfo){let W=I.relationInfo.foreignKey;if(W)z.push(`${H}.${this.sanitizeFieldName(W)}`)}return z.join(", ")}buildSubqueryFields(E,H,z=0,I=!1,W="sub",Y=new Map){let $=[],V=3,X=8;for(let U of E){let D=U.fieldName;if(U.isRelation&&U.relationInfo&&U.nestedSelections)if(U.relationInfo.type==="one-to-many"){if(I){let Z=U.nestedSelections.filter((_)=>{if(!_.isRelation)return!0;if(_.relationInfo?.type==="many-to-one")return!0;return!1});if(Z.length>0){let _=this.buildSubqueryFields(Z,U.relationInfo.table,z+1,!0,"nested_sub");if(_){let{whereClause:G,paramMap:O}=this.buildSubqueryWhereClause("nested_sub",U.relationInfo.foreignKey,"sub",U.whereConditions);for(let[R,Q]of O.entries())Y.set(R,Q);let K=`(SELECT json_group_array(json_object(${_})) FROM ${U.relationInfo.table} nested_sub WHERE ${G})`;$.push(`'${D}', ${K}`)}}continue}if(z>=3){let Z=this.getScalarFields(U.nestedSelections);if(Z.length>0){let _=Z.map((L)=>`'${L}', nested_sub.${this.sanitizeFieldName(L)}`).join(", "),{whereClause:G,paramMap:O}=this.buildSubqueryWhereClause("nested_sub",U.relationInfo.foreignKey,"sub",U.whereConditions);for(let[L,T]of O.entries())Y.set(L,T);let K="",R="";if(U.orderBy&&U.orderBy.length>0)K=` ORDER BY ${U.orderBy.map((T)=>{return`nested_sub.${this.sanitizeFieldName(T.field)} ${T.direction}`}).join(", ")}`;if(U.limit!==void 0||U.offset!==void 0)R=this.buildLimitClause(U.limit,U.offset);let Q=R||K?(()=>{let L=_.split(", ").filter((T,J)=>J%2===1).join(", ");return`(SELECT json_group_array(json_object(${_})) FROM (SELECT ${L} FROM ${U.relationInfo.table} nested_sub WHERE ${G}${K}${R?` ${R}`:""}) nested_sub)`})():`(SELECT json_group_array(json_object(${_})) FROM ${U.relationInfo.table} nested_sub WHERE ${G})`;$.push(`'${D}', ${Q}`)}}else{let Z=U.nestedSelections;if(z>=2)Z=U.nestedSelections.filter((_)=>{if(!_.isRelation)return!0;if(_.relationInfo?.type==="many-to-one")return!0;if(_.relationInfo?.type==="one-to-many"){if(z<=3)return!0}return!1});if(Z.length>0){let _=this.buildSubqueryFields(Z,U.relationInfo.table,z+1,!0,"nested_sub");if(_){let{whereClause:G,paramMap:O}=this.buildSubqueryWhereClause("nested_sub",U.relationInfo.foreignKey,"sub",U.whereConditions);for(let[L,T]of O.entries())Y.set(L,T);let K="",R="";if(U.orderBy&&U.orderBy.length>0)K=` ORDER BY ${U.orderBy.map((T)=>{return`nested_sub.${this.sanitizeFieldName(T.field)} ${T.direction}`}).join(", ")}`;if(U.limit!==void 0||U.offset!==void 0)R=this.buildLimitClause(U.limit,U.offset);let Q=R||K?(()=>{let L=this.buildSubqueryColumnList(Z,"nested_sub");return`(SELECT json_group_array(json_object(${_})) FROM (SELECT ${L} FROM ${U.relationInfo.table} nested_sub WHERE ${G}${K}${R?` ${R}`:""}) nested_sub)`})():`(SELECT json_group_array(json_object(${_})) FROM ${U.relationInfo.table} nested_sub WHERE ${G})`;$.push(`'${D}', ${Q}`)}}}}else if(z>=8){let Z=this.getScalarFields(U.nestedSelections);if(Z.length>0){let _=Z.map((G)=>`'${G}', related.${this.sanitizeFieldName(G)}`).join(", ");$.push(`'${D}', (SELECT json_object(${_}) FROM ${U.relationInfo.table} related WHERE related.id = sub.${U.relationInfo.foreignKey})`)}}else{let Z=U.nestedSelections;if(I)Z=U.nestedSelections.filter((_)=>{if(!_.isRelation)return!0;if(_.relationInfo?.type==="many-to-one")return!0;if(_.relationInfo?.type==="one-to-many"){if(z<=3)return!0}return!1});if(Z.length>0||U.nestedSelections.some((_)=>!_.isRelation)){let _=Z.length>0?Z:U.nestedSelections.filter((O)=>!O.isRelation),G=this.buildSubqueryFields(_,U.relationInfo.table,z+1,!1,"related");if(G)$.push(`'${D}', (SELECT json_object(${G}) FROM ${U.relationInfo.table} related WHERE related.id = sub.${U.relationInfo.foreignKey})`)}}else $.push(`'${D}', ${W}.${this.sanitizeFieldName(D)}`)}return $.join(", ")}filterSelectionsForArrayNesting(E,H){return E.filter((z)=>{if(!z.isRelation)return!0;if(z.relationInfo?.type==="one-to-many")return!1;if(z.relationInfo?.type==="many-to-one")return!0;return!1})}limitSelectionsToScalarsAndSimpleRelations(E){return E.filter((H)=>{if(!H.isRelation)return!0;if(H.relationInfo?.type==="many-to-one"&&H.nestedSelections)return H.nestedSelections.every((I)=>!I.isRelation);return!1})}getScalarFields(E){return E.filter((H)=>!H.isRelation).map((H)=>H.fieldName)}buildWhereClause(E,H){if(E.length===0)return"";return E.map((z)=>{if(H)return H.set(z.paramName,z.value),`${z.field} ${z.operator} ?`;return`${z.field} ${z.operator} ?`}).join(" AND ")}buildOrderByClause(E,H="t0"){if(E.length===0)return"";return E.map((z)=>`${H}.${z.field} ${z.direction}`).join(", ")}buildLimitClause(E,H){let z="";if(E!==void 0){if(z+=`LIMIT ${E}`,H!==void 0)z+=` OFFSET ${H}`}else if(H!==void 0)z+=`LIMIT 999999 OFFSET ${H}`;return z}executeQuery(E,H,z){try{let I=z.query(E);if(Object.keys(H).length===0)return I.all();let W=[],Y=Object.keys(H).sort(($,V)=>Number.parseInt($)-Number.parseInt(V));for(let $ of Y){let V=H[$];if(typeof V==="string"||typeof V==="number")W.push(V);else W.push(String(V))}return I.all(...W)}catch(I){throw console.error("SQL execution error:",I),console.error("SQL:",E),console.error("Params:",H),I}}sanitizeFieldName(E){if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(E))throw new Error(`Invalid field name: ${E}`);return E}getTableName(E){return this.sanitizeFieldName(E).toLowerCase()}}async function F(E,H,z){return await new k(H).convertAndExecute(E,z)}var g={users:{id:{type:"INTEGER"},name:{type:"TEXT"},email:{type:"TEXT"},posts:{type:"ARRAY",relation:{table:"posts",foreignKey:"user_id",type:"one-to-many"}}},posts:{id:{type:"INTEGER"},title:{type:"TEXT"},content:{type:"TEXT"},user_id:{type:"INTEGER"},user:{type:"OBJECT",relation:{table:"users",foreignKey:"user_id",type:"many-to-one"}}}};export{v as transformResult,F as graphqlToSqlite,g as exampleSchema,k as GraphQLToSQLiteConverter}; | ||
| import{createRequire as B}from"node:module";var M=B(import.meta.url);import{parse as w}from"graphql";function v(E){if(E.length===0)return[];return E.map((H)=>{let z={};for(let I in H){let W=H[I];if(typeof W==="string"&&(W.startsWith("{")||W.startsWith("[")))try{let Y=JSON.parse(W);if(Array.isArray(Y))z[I]=Y.filter(($)=>$!==null);else z[I]=Y}catch{z[I]=W}else z[I]=W}return z})}class k{schema;paramCounter=0;constructor(E){this.schema=E}convert(E){try{let H=w(E),z=this.extractQueryContext(H),{sql:I,params:W}=this.buildSQLQuery(z);return{sql:I,params:W}}catch(H){let z=H instanceof Error?H.message:String(H);throw new Error(`GraphQL to SQL conversion failed: ${z}`)}}async convertAndExecute(E,H){try{let{sql:z,params:I}=this.convert(E),W=this.executeQuery(z,I,H);return v(W)}catch(z){let I=z instanceof Error?z.message:String(z);throw new Error(`GraphQL to SQL conversion failed: ${I}`)}}extractQueryContext(E){let H=E.definitions[0];if(!H||H.kind!=="OperationDefinition"||H.operation!=="query")throw new Error("Only query operations are supported");let z=H.selectionSet.selections[0];if(z.kind!=="Field")throw new Error("Invalid query structure");let I=this.getTableName(z.name.value),W="t0";return{tableName:I,alias:W,selections:this.extractSelections(z.selectionSet,z.name.value),joins:[],where:this.extractWhereConditions(z.arguments||[],I,W),orderBy:this.extractOrderBy(z.arguments||[]),limit:this.extractLimit(z.arguments||[]),offset:this.extractOffset(z.arguments||[])}}extractSelections(E,H){if(!E)return[];let z=this.schema[H];if(!z)return[];return E.selections.filter((I)=>I.kind==="Field").map((I)=>{let W=I.name.value,Y=z[W],$=I.selectionSet&&I.selectionSet.selections.length>0,V=!!(Y?.relation&&$),X,U,D,Z;if(V&&I.arguments&&I.arguments.length>0){let _=`nested_${W}`;X=this.extractWhereConditions(I.arguments,Y?.relation?.table||H,_),U=this.extractOrderBy(I.arguments),D=this.extractLimit(I.arguments),Z=this.extractOffset(I.arguments)}return{fieldName:W,isRelation:V,relationInfo:Y?.relation,whereConditions:X,orderBy:U,limit:D,offset:Z,nestedSelections:V&&Y?.relation?this.extractSelections(I.selectionSet,Y.relation.table):void 0}})}extractWhereConditions(E,H,z){let I=[];for(let W of E)if(W.name.value==="where"&&W.value.kind==="ObjectValue")for(let Y of W.value.fields){let $=this.sanitizeFieldName(Y.name.value);if(Y.value.kind==="StringValue"||Y.value.kind==="IntValue"||Y.value.kind==="FloatValue"){let V=`param_${++this.paramCounter}`,X=Y.value.value;if(Y.value.kind==="IntValue")X=Number.parseInt(X,10);else if(Y.value.kind==="FloatValue")X=Number.parseFloat(X);I.push({field:`${z}.${$}`,operator:"=",value:X,paramName:V})}else if(Y.value.kind==="Variable"){let V=Y.value.name.value;I.push({field:`${z}.${$}`,operator:"=",value:`$${V}`,paramName:V})}else if(Y.value.kind==="ObjectValue")for(let V of Y.value.fields){let X=V.name.value;if(V.value.kind==="StringValue"||V.value.kind==="IntValue"||V.value.kind==="FloatValue"){let U=`param_${++this.paramCounter}`,D=this.mapGraphQLOperatorToSQL(X),Z=V.value.value;if(V.value.kind==="IntValue")Z=Number.parseInt(Z,10);else if(V.value.kind==="FloatValue")Z=Number.parseFloat(Z);I.push({field:`${z}.${$}`,operator:D,value:Z,paramName:U})}else if(V.value.kind==="Variable"){let U=V.value.name.value,D=this.mapGraphQLOperatorToSQL(X);I.push({field:`${z}.${$}`,operator:D,value:`$${U}`,paramName:U})}}}else if(W.name.value!=="orderBy"&&W.name.value!=="limit"&&W.name.value!=="offset"){let Y=this.sanitizeFieldName(W.name.value);if(W.value.kind==="StringValue"||W.value.kind==="IntValue"||W.value.kind==="FloatValue"){let $=`param_${++this.paramCounter}`,V=W.value.value;if(W.value.kind==="IntValue")V=Number.parseInt(V,10);else if(W.value.kind==="FloatValue")V=Number.parseFloat(V);I.push({field:`${z}.${Y}`,operator:"=",value:V,paramName:$})}else if(W.value.kind==="Variable"){let $=W.value.name.value;I.push({field:`${z}.${Y}`,operator:"=",value:`$${$}`,paramName:$})}}return I}mapGraphQLOperatorToSQL(E){return{eq:"=",ne:"!=",gt:">",gte:">=",lt:"<",lte:"<=",like:"LIKE",ilike:"LIKE",in:"IN",notIn:"NOT IN"}[E]||"="}extractOrderBy(E){let H=[];for(let z of E)if(z.name.value==="orderBy"&&z.value.kind==="ObjectValue"){for(let I of z.value.fields)if(I.value.kind==="EnumValue")H.push({field:this.sanitizeFieldName(I.name.value),direction:I.value.value==="DESC"?"DESC":"ASC"})}return H}extractLimit(E){let H=E.find((z)=>z.name.value==="limit");if(H&&H.value.kind==="IntValue"){let z=Number.parseInt(H.value.value);return Math.min(Math.max(z,1),1000)}return}extractOffset(E){let H=E.find((z)=>z.name.value==="offset");if(H&&H.value.kind==="IntValue"){let z=Number.parseInt(H.value.value);return Math.max(z,0)}return}buildSQLQuery(E){let H=this.buildJoins(E),z=new Map,I=this.buildSelectFields(E,H,z),W=this.buildWhereClause(E.where,z),Y=this.buildOrderByClause(E.orderBy,E.alias),$=this.buildLimitClause(E.limit,E.offset),V=this.buildGroupByClause(E,H),X=`SELECT ${I} FROM ${E.tableName} AS ${E.alias} ${H.map((Z)=>`${Z.joinType} JOIN ${Z.table} AS ${Z.alias} ON ${Z.condition} `).join("")}${W?`WHERE ${W} `:""}${V?`GROUP BY ${V} `:""}${Y?`ORDER BY ${Y} `:""}`+`${$}`.trim().replace(/\s+/g," "),U={},D=0;for(let[Z,_]of z.entries())U[D.toString()]=_,D++;return{sql:X,params:U}}collectNestedParams(E,H){for(let z of E){if(z.whereConditions)H.push(...z.whereConditions.map((I)=>I.value));if(z.nestedSelections)this.collectNestedParams(z.nestedSelections,H)}}buildGroupByClause(E,H){if(E.selections.some((I)=>I.isRelation&&I.relationInfo?.type==="one-to-many"))return`${E.alias}.id`;return""}buildJoins(E){let H=[];return this.collectJoins(E.selections,E.tableName,E.alias,H,new Map,"",0),H}collectJoins(E,H,z,I,W=new Map,Y="",$=0){let V=I.length+1;for(let X of E)if(X.isRelation&&X.relationInfo){let U=Y?`${Y}.${X.fieldName}`:X.fieldName,D=`${U}`,Z=W.get(D),_=$>0&&X.relationInfo.type==="one-to-many";if(!Z&&!_){Z=`t${V++}`,W.set(D,Z);let G=this.buildJoinCondition(z,Z,X.relationInfo);I.push({table:X.relationInfo.table,alias:Z,joinType:"LEFT",condition:G,path:U})}if(X.nestedSelections&&X.nestedSelections.length>0&&!_)this.collectJoins(X.nestedSelections,X.relationInfo.table,Z||z,I,W,U,$+1)}}buildJoinCondition(E,H,z){switch(z.type){case"one-to-many":return`${E}.id = ${H}.${z.foreignKey}`;case"many-to-one":return`${E}.${z.foreignKey} = ${H}.id`;case"many-to-many":throw new Error("Many-to-many relations require junction table handling");default:throw new Error(`Unsupported relation type: ${z.type}`)}}buildSelectFields(E,H,z){let I=[],W=new Map(H.map((Y)=>[Y.table,Y.alias]));return this.buildSelectFieldsRecursive(E.selections,E.tableName,E.alias,I,W,H,z,""),I.join(", ")}buildSelectFieldsRecursive(E,H,z,I,W,Y,$,V=""){for(let X of E){let U=X.fieldName,D=V?`${V}_${U}`:U;if(X.isRelation&&X.relationInfo){let Z=X.relationInfo.table,_=this.findJoinAlias(Y,Z,z);if(_)if(X.nestedSelections&&X.nestedSelections.length>0)if((X.relationInfo.type==="one-to-many"||X.whereConditions&&X.whereConditions.length>0)&&X.relationInfo.type==="one-to-many"){let O=this.buildSubqueryFields(X.nestedSelections,X.relationInfo.table,0,!0,"sub",$);if(O){let{whereClause:K,paramMap:R}=this.buildSubqueryWhereClause("sub",X.relationInfo.foreignKey,z,X.whereConditions,$);for(let[J,P]of R.entries())$.set(J,P);let Q="",L="";if(X.orderBy&&X.orderBy.length>0)Q=` ORDER BY ${X.orderBy.map((P)=>{return`sub.${this.sanitizeFieldName(P.field)} ${P.direction}`}).join(", ")}`;if(X.limit!==void 0||X.offset!==void 0)L=this.buildLimitClause(X.limit,X.offset);let T=L||Q?(()=>{let J=this.buildSubqueryColumnList(X.nestedSelections,"sub");return`(SELECT json_group_array(json_object(${O})) FROM (SELECT ${J} FROM ${X.relationInfo.table} sub WHERE ${K}${Q}${L?` ${L}`:""}) sub)`})():`(SELECT json_group_array(json_object(${O})) FROM ${X.relationInfo.table} sub WHERE ${K})`;I.push(`${T} AS ${this.sanitizeFieldName(D)}`)}}else{let O=this.buildJsonFields(X.nestedSelections,Z,_,Y,$,0);if(X.relationInfo.type==="one-to-many")I.push(`json_group_array(CASE WHEN ${_}.id IS NOT NULL THEN json_object(${O}) ELSE NULL END ) AS ${this.sanitizeFieldName(D)}`);else I.push(`CASE WHEN ${_}.id IS NOT NULL THEN json_object(${O}) ELSE NULL END AS ${this.sanitizeFieldName(D)}`)}else if(X.relationInfo.type==="many-to-one"){let G=this.schema[Z];if(G){let O=Object.keys(G).filter((K)=>!G[K]?.relation).map((K)=>`'${K}', ${_}.${this.sanitizeFieldName(K)}`).join(", ");I.push(`CASE WHEN ${_}.id IS NOT NULL THEN json_object(${O}) ELSE NULL END AS ${this.sanitizeFieldName(D)}`)}}else{let G=this.schema[Z];if(G){let K=`(SELECT json_group_array(json_object(${Object.keys(G).filter((R)=>!G[R]?.relation).map((R)=>`'${R}', sub.${this.sanitizeFieldName(R)}`).join(", ")})) FROM ${Z} sub WHERE sub.${X.relationInfo.foreignKey} = ${z}.id)`;I.push(`${K} AS ${this.sanitizeFieldName(D)}`)}}}else I.push(`${z}.${this.sanitizeFieldName(U)} AS ${this.sanitizeFieldName(D)}`)}}findJoinAlias(E,H,z){return E.find((W)=>W.table===H&&W.condition.includes(z))?.alias||null}buildJsonFields(E,H,z,I,W,Y=0){let $=[];for(let V of E){let X=V.fieldName;if(V.isRelation&&V.relationInfo&&V.nestedSelections){let U=this.findJoinAlias(I,V.relationInfo.table,z);if(V.relationInfo.type==="one-to-many"){if(Y>0){let D=this.buildSubqueryFields(V.nestedSelections,V.relationInfo.table,0,!0,"sub",W);if(D){let{whereClause:Z,paramMap:_}=this.buildSubqueryWhereClause("sub",V.relationInfo.foreignKey,z,V.whereConditions);for(let[R,Q]of _.entries())W.set(R,Q);let G="",O="";if(V.orderBy&&V.orderBy.length>0)G=` ORDER BY ${V.orderBy.map((Q)=>{return`sub.${this.sanitizeFieldName(Q.field)} ${Q.direction}`}).join(", ")}`;if(V.limit!==void 0||V.offset!==void 0)O=this.buildLimitClause(V.limit,V.offset);let K=O||G?(()=>{let R=D.split(", ").filter((Q,L)=>L%2===1).join(", ");return`(SELECT json_group_array(json_object(${D})) FROM (SELECT ${R} FROM ${V.relationInfo.table} sub WHERE ${Z}${G}${O?` ${O}`:""}) sub)`})():`(SELECT json_group_array(json_object(${D})) FROM ${V.relationInfo.table} sub WHERE ${Z})`;$.push(`'${X}', ${K}`)}}else if(U){let D=this.buildJsonFields(V.nestedSelections,V.relationInfo.table,U,I,W,Y+1);$.push(`'${X}', json_group_array(json_object(${D}))`)}}else if(U){let D=this.buildJsonFields(V.nestedSelections,V.relationInfo.table,U,I,W,Y+1);$.push(`'${X}', json_object(${D})`)}}else $.push(`'${X}', ${z}.${this.sanitizeFieldName(X)}`)}return $.join(", ")}buildSubqueryWhereClause(E,H,z,I,W=new Map){let Y=[`${E}.${H} = ${z}.id`];if(I&&I.length>0)for(let V of I){let X=V.field.replace(/^[^.]+\./,`${E}.`),U=`param_${E}_${V.field.replace(".","_")}_${W.size}`;Y.push(`${X} ${V.operator} $${U}`),W.set(U,V.value)}return{whereClause:Y.join(" AND "),paramMap:W}}buildSubqueryColumnList(E,H){let z=[];for(let I of E)if(!I.isRelation)z.push(`${H}.${this.sanitizeFieldName(I.fieldName)}`);else if(I.relationInfo){let W=I.relationInfo.foreignKey;if(W)z.push(`${H}.${this.sanitizeFieldName(W)}`)}return z.join(", ")}buildSubqueryFields(E,H,z=0,I=!1,W="sub",Y=new Map){let $=[],V=3,X=8;for(let U of E){let D=U.fieldName;if(U.isRelation&&U.relationInfo&&U.nestedSelections)if(U.relationInfo.type==="one-to-many"){if(I){let Z=U.nestedSelections.filter((_)=>{if(!_.isRelation)return!0;if(_.relationInfo?.type==="many-to-one")return!0;return!1});if(Z.length>0){let _=this.buildSubqueryFields(Z,U.relationInfo.table,z+1,!0,"nested_sub");if(_){let{whereClause:G,paramMap:O}=this.buildSubqueryWhereClause("nested_sub",U.relationInfo.foreignKey,"sub",U.whereConditions);for(let[R,Q]of O.entries())Y.set(R,Q);let K=`(SELECT json_group_array(json_object(${_})) FROM ${U.relationInfo.table} nested_sub WHERE ${G})`;$.push(`'${D}', ${K}`)}}continue}if(z>=3){let Z=this.getScalarFields(U.nestedSelections);if(Z.length>0){let _=Z.map((L)=>`'${L}', nested_sub.${this.sanitizeFieldName(L)}`).join(", "),{whereClause:G,paramMap:O}=this.buildSubqueryWhereClause("nested_sub",U.relationInfo.foreignKey,"sub",U.whereConditions);for(let[L,T]of O.entries())Y.set(L,T);let K="",R="";if(U.orderBy&&U.orderBy.length>0)K=` ORDER BY ${U.orderBy.map((T)=>{return`nested_sub.${this.sanitizeFieldName(T.field)} ${T.direction}`}).join(", ")}`;if(U.limit!==void 0||U.offset!==void 0)R=this.buildLimitClause(U.limit,U.offset);let Q=R||K?(()=>{let L=_.split(", ").filter((T,J)=>J%2===1).join(", ");return`(SELECT json_group_array(json_object(${_})) FROM (SELECT ${L} FROM ${U.relationInfo.table} nested_sub WHERE ${G}${K}${R?` ${R}`:""}) nested_sub)`})():`(SELECT json_group_array(json_object(${_})) FROM ${U.relationInfo.table} nested_sub WHERE ${G})`;$.push(`'${D}', ${Q}`)}}else{let Z=U.nestedSelections;if(z>=2)Z=U.nestedSelections.filter((_)=>{if(!_.isRelation)return!0;if(_.relationInfo?.type==="many-to-one")return!0;if(_.relationInfo?.type==="one-to-many"){if(z<=3)return!0}return!1});if(Z.length>0){let _=this.buildSubqueryFields(Z,U.relationInfo.table,z+1,!0,"nested_sub");if(_){let{whereClause:G,paramMap:O}=this.buildSubqueryWhereClause("nested_sub",U.relationInfo.foreignKey,"sub",U.whereConditions);for(let[L,T]of O.entries())Y.set(L,T);let K="",R="";if(U.orderBy&&U.orderBy.length>0)K=` ORDER BY ${U.orderBy.map((T)=>{return`nested_sub.${this.sanitizeFieldName(T.field)} ${T.direction}`}).join(", ")}`;if(U.limit!==void 0||U.offset!==void 0)R=this.buildLimitClause(U.limit,U.offset);let Q=R||K?(()=>{let L=this.buildSubqueryColumnList(Z,"nested_sub");return`(SELECT json_group_array(json_object(${_})) FROM (SELECT ${L} FROM ${U.relationInfo.table} nested_sub WHERE ${G}${K}${R?` ${R}`:""}) nested_sub)`})():`(SELECT json_group_array(json_object(${_})) FROM ${U.relationInfo.table} nested_sub WHERE ${G})`;$.push(`'${D}', ${Q}`)}}}}else if(z>=8){let Z=this.getScalarFields(U.nestedSelections);if(Z.length>0){let _=Z.map((G)=>`'${G}', related.${this.sanitizeFieldName(G)}`).join(", ");$.push(`'${D}', (SELECT json_object(${_}) FROM ${U.relationInfo.table} related WHERE related.id = sub.${U.relationInfo.foreignKey})`)}}else{let Z=U.nestedSelections;if(I)Z=U.nestedSelections.filter((_)=>{if(!_.isRelation)return!0;if(_.relationInfo?.type==="many-to-one")return!0;if(_.relationInfo?.type==="one-to-many"){if(z<=3)return!0}return!1});if(Z.length>0||U.nestedSelections.some((_)=>!_.isRelation)){let _=Z.length>0?Z:U.nestedSelections.filter((O)=>!O.isRelation),G=this.buildSubqueryFields(_,U.relationInfo.table,z+1,!1,"related");if(G)$.push(`'${D}', (SELECT json_object(${G}) FROM ${U.relationInfo.table} related WHERE related.id = sub.${U.relationInfo.foreignKey})`)}}else $.push(`'${D}', ${W}.${this.sanitizeFieldName(D)}`)}return $.join(", ")}filterSelectionsForArrayNesting(E,H){return E.filter((z)=>{if(!z.isRelation)return!0;if(z.relationInfo?.type==="one-to-many")return!1;if(z.relationInfo?.type==="many-to-one")return!0;return!1})}limitSelectionsToScalarsAndSimpleRelations(E){return E.filter((H)=>{if(!H.isRelation)return!0;if(H.relationInfo?.type==="many-to-one"&&H.nestedSelections)return H.nestedSelections.every((I)=>!I.isRelation);return!1})}getScalarFields(E){return E.filter((H)=>!H.isRelation).map((H)=>H.fieldName)}buildWhereClause(E,H){if(E.length===0)return"";return E.map((z)=>{if(H)return H.set(z.paramName,z.value),`${z.field} ${z.operator} ?`;return`${z.field} ${z.operator} ?`}).join(" AND ")}buildOrderByClause(E,H="t0"){if(E.length===0)return"";return E.map((z)=>`${H}.${z.field} ${z.direction}`).join(", ")}buildLimitClause(E,H){let z="";if(E!==void 0){if(z+=`LIMIT ${E}`,H!==void 0)z+=` OFFSET ${H}`}else if(H!==void 0)z+=`LIMIT 999999 OFFSET ${H}`;return z}executeQuery(E,H,z){try{let I=z.query(E);if(Object.keys(H).length===0)return I.all();let W=[],Y=Object.keys(H).sort(($,V)=>Number.parseInt($)-Number.parseInt(V));for(let $ of Y){let V=H[$];if(typeof V==="string"||typeof V==="number")W.push(V);else W.push(String(V))}return I.all(...W)}catch(I){throw console.error("SQL execution error:",I),console.error("SQL:",E),console.error("Params:",H),I}}sanitizeFieldName(E){if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(E))throw new Error(`Invalid field name: ${E}`);return E}getTableName(E){return this.sanitizeFieldName(E).toLowerCase()}}async function F(E,H,z){return await new k(H).convertAndExecute(E,z)}var g={users:{id:{type:"INTEGER"},name:{type:"TEXT"},email:{type:"TEXT"},posts:{type:"ARRAY",relation:{table:"posts",foreignKey:"user_id",type:"one-to-many"}}},posts:{id:{type:"INTEGER"},title:{type:"TEXT"},content:{type:"TEXT"},user_id:{type:"INTEGER"},user:{type:"OBJECT",relation:{table:"users",foreignKey:"user_id",type:"many-to-one"}}}};export{v as transformResult,F as graphqlToSqlite,g as exampleSchema,k as GraphQLToSQLiteConverter}; | ||
| //# debugId=45E61B35FCAE79BF64756E2164756E21 | ||
| //# debugId=B88600328DE424A764756E2164756E21 | ||
| //# sourceMappingURL=./publicgq2sql.js.map |
+1
-1
| { | ||
| "name": "@gqlite/gql2sql", | ||
| "description": "GraphQL => SQL (SQLite)", | ||
| "version": "0.1.1", | ||
| "version": "0.1.2", | ||
| "publishConfig": { | ||
@@ -6,0 +6,0 @@ "access": "public" |
Sorry, the diff of this file is too big to display
138924
-0.01%351
-0.28%