postgres icon indicating copy to clipboard operation
postgres copied to clipboard

how to run a subquery with a variable or call function to generate

Open Md-Anamul-Haque opened this issue 1 year ago • 4 comments

const subq = "(SELECT CASE WHEN LENGTH((SELECT COALESCE(MAX(substr(title,1)), '0') FROM todo)) >= 5 THEN LENGTH((SELECT COALESCE(MAX(substr(title,1)), '0') FROM todo)) ELSE 5 END FROM todo)"
	const result = await sql`
		${subq}
		`;
		node:internal/process/promises:289
            triggerUncaughtException(err, true /* fromPromise */);
            ^

PostgresError: syntax error at or near "$1"
    at ErrorResponse (c:\Users\anamul\Desktop\test-dirzzle\node_modules\postgres\cjs\src\connection.js:788:26)
    at handle (c:\Users\anamul\Desktop\test-dirzzle\node_modules\postgres\cjs\src\connection.js:473:7)
    at Socket.data (c:\Users\anamul\Desktop\test-dirzzle\node_modules\postgres\cjs\src\connection.js:315:9)
    at Socket.emit (node:events:514:28)
    at Socket.emit (node:domain:488:12)
    at addChunk (node:internal/streams/readable:545:12)
    at readableAddChunkPushByteMode (node:internal/streams/readable:495:3)
    at Readable.push (node:internal/streams/readable:375:5)
    at TCP.onStreamRead (node:internal/stream_base_commons:190:23)
    at cachedError (c:\Users\anamul\Desktop\test-dirzzle\node_modules\postgres\cjs\src\query.js:170:23)
    at Query (c:\Users\anamul\Desktop\test-dirzzle\node_modules\postgres\cjs\src\query.js:36:24)
    at sql2 (c:\Users\anamul\Desktop\test-dirzzle\node_modules\postgres\cjs\src\index.js:112:11)
    at main (c:\Users\anamul\Desktop\test-dirzzle\src\index.ts:53:23) {
  severity_local: 'ERROR',
  severity: 'ERROR',
  code: '42601',
  position: '4',
  file: 'scan.l',
  line: '1245',
  routine: 'scanner_yyerror'
}

		```

Md-Anamul-Haque avatar Apr 16 '24 02:04 Md-Anamul-Haque

You can turn your sub-query into a query like this:

const subq = sql`(SELECT CASE WHEN LENGTH((SELECT COALESCE(MAX(substr(title,1)), '0') FROM todo)) >= 5 THEN LENGTH((SELECT COALESCE(MAX(substr(title,1)), '0') FROM todo)) ELSE 5 END FROM todo)`
const result = await sql`
  ${subq}
`;

Essentially you can nest different queries as long as there is no await before the sub-queries. Also there is sql.unsafe if you know what you are doing.

csabaxyz avatar Apr 20 '24 23:04 csabaxyz

Will it call between the database 2 times at once?

Md-Anamul-Haque avatar Apr 27 '24 20:04 Md-Anamul-Haque

@Md-Anamul-Haque , I suggest you enable debug (logging) so you can see what SQL queries are performed

bas080 avatar Apr 27 '24 21:04 bas080

I am also having an issue with this, attempting to string together multiple subqueries in order to string aggregate to mvt tiles

export const queryLayer = ({
  z,
  x,
  y,
  layer,
  fields,
}: {
  z: string;
  x: string;
  y: string;
  layer: string;
  fields: string[];
}) => sql`
  (SELECT ST_AsMVT(q, '${layer}', 4096, 'geom') AS l FROM
        (SELECT ST_AsMvtGeom(
            geom,
            ST_TileEnvelope(${z}, ${x}, ${y}),
            4096,
            64,
            true
            ) AS geom, ${fields.join(", ")}
       FROM ${layer} WHERE (geom && ST_TileEnvelope(${z}, ${x}, ${y}))) AS q)
  `;

export const tileQuery = ({
  z,
  x,
  y,
  layers = allLayers,
}: {
  z: string;
  x: string;
  y: string;
  layers?: Array<keyof typeof layerDefs>;
}) => {
  return sql`select (${layers
    .map((layer) => queryLayer({ z, x, y, layer, fields: layerDefs[layer] }))
    .join(" || ")
    .replace(/\s+/g, " ")}) as geom`;
};

Resulting in a (functional) query such as:

  select ((SELECT ST_AsMVT(q, 'table', 4096, 'geom') AS l FROM
        (SELECT ST_AsMvtGeom(
            geom,
            ST_TileEnvelope(10, 209, 390),
            4096,
            64,
            true
            ) AS geom
         FROM table WHERE (geom && ST_TileEnvelope(10, 209, 390))) AS q)
         ||
      (SELECT ST_AsMVT(q, 'table2', 4096, 'geom') AS l FROM
        (SELECT ST_AsMvtGeom(
            geom,
            ST_TileEnvelope(10, 209, 390),
            4096,
            64,
            true
            ) AS geom
      FROM table2 WHERE (geom && ST_TileEnvelope(10, 209, 390))) AS q)
  ) as geom

But I end up with a string of unawaited promises

const tile = await tileQuery({z, x, y})

console.log({tile})

// output
{
  tile: Result(1) [ { geom: '[object Promise] || [object Promise]' } ]
}

The return type of queryLayer is postgres.PendingQuery<>

If I am reading the result correctly, it seems to be reading the subquery joined as a string to be selected as in

select ("arbitrary string") as mvt

it does work if I change queryTile to just return a string and wrap the map/join in sql.unsafe() however I would prefer to not interpolate raw values if possible

pm0u avatar Jun 25 '24 03:06 pm0u