adelsz / pgtyped

pgTyped - Typesafe SQL in TypeScript
https://pgtyped.dev
MIT License
2.97k stars 97 forks source link

Common error pattern: empty array as an argument #221

Open golergka opened 3 years ago

golergka commented 3 years ago

Over the last few months of using pgtyped, I've ran into several bugs that look something like this:

const createSomeThings = sql<ICreateSomeThings>`
    INSERT INTO some_things (column_1, column_2)
    VALUES $$things(column1, column2)
`

createThings = [] // << this causes a bug!
createSomeThings.run({ things: createThings }, db)

Because when createThings array is empty, I get an error:

error: syntax error at or near ")"
    at Parser.parseErrorMessage (/Users/golergka/Projects/fastcup/backend/cron/node_modules/pg-protocol/src/parser.ts:357:11)
    at Parser.handlePacket (/Users/golergka/Projects/fastcup/backend/cron/node_modules/pg-protocol/src/parser.ts:186:21)
    at Parser.parse (/Users/golergka/Projects/fastcup/backend/cron/node_modules/pg-protocol/src/parser.ts:101:30)
    at Socket.<anonymous> (/Users/golergka/Projects/fastcup/backend/cron/node_modules/pg-protocol/src/index.ts:7:48)
    at Socket.emit (node:events:327:20)
    at Socket.EventEmitter.emit (node:domain:486:12)
    at addChunk (node:internal/streams/readable:304:12)
    at readableAddChunk (node:internal/streams/readable:279:9)
    at Socket.Readable.push (node:internal/streams/readable:218:10)
    at TCP.onStreamRead (node:internal/stream_base_commons:192:23) {
  length: 90,
  severity: 'ERROR',
  code: '42601',
  detail: undefined,
  hint: undefined,
  position: '80',
  internalPosition: undefined,
  internalQuery: undefined,
  where: undefined,
  schema: undefined,
  table: undefined,
  column: undefined,
  dataType: undefined,
  constraint: undefined,
  file: 'scan.l',
  line: '1180',
  routine: 'scanner_yyerror',
  isPostgresError: true,
  query: '\n' +
    ' INSERT INTO some_things (column_1, column_2)\n' +
    ' VALUES ()'

(Names of things are changed to respect my employer's NDA).

This is a painful bug, especially in cases where these arrays are empty only in very rare cases. How can we prevent things like this from happening?

adelsz commented 3 years ago

That does look like inconvenient. I think a good way to solve this is to introduce a "required" spread parameter syntax. Queries that include such parameters should fail if one of the required params is an empty array.

wongjiahau commented 3 years ago

One way to solve this issue is to force the generated type to be NonEmpty<T> instead of Array<T>, where NonEmpty represents an array with at least one element.

Example of encoding NonEmpty:

type NonEmpty<T> = [T, ...T[]]

Another way to solve this is to inject empty array literal, see https://stackoverflow.com/a/33717063/6587634