Home > Software design >  Use postgres constants with pg_prepare/pg_execute
Use postgres constants with pg_prepare/pg_execute

Time:09-30

Supposing this table:

CREATE TABLE log
(
  user smallint NOT NULL,
  moment timestamp without time zone NOT NULL DEFAULT (clock_timestamp())::timestamp without time zone,
  details text NOT NULL,
  CONSTRAINT log_pk PRIMARY KEY (user, moment)
);

To add a record to the table, I would use the following code:

<?php

  $dbconn = pg_connect('dbname=system');

  $query = <<<EOQ
INSERT INTO
  log
VALUES
  (
    $1,
    DEFAULT,
    $3
  )
EOQ;

// ...

With the 'DEFAULT' constant as the second parameter, but for a "collection of bizarre reasons" I need to be able to run it like this:

<?php

  $dbconn = pg_connect('dbname=system');

  $query = <<<EOQ
INSERT INTO
  log
VALUES
  (
    $1,
    $2,
    $3
  )
EOQ;

  $result = pg_prepare($dbconn, '', $query);

  $data=
    [
      10,
      'DEFAULT',
      'IP: 127.0.0.1'
    ];

  $result = pg_execute($dbconn, '', $data);

The problem is that, in this way, the constant is received as text and php raise the error 'PHP Warning: pg_execute(): Query failed: ERROR: invalid input syntax for type timestamp: "DEFAULT"'.

Is there any way to use postgres constants as parameters with pg_prepare/pg_execute?

PS: I'm using php 8 and postgres 13.4

EDIT:

I also tried using this format:


// ...

  $query = <<<EOQ
INSERT INTO
  acessos
VALUES
  (
    $1,
    CASE WHEN $2='DEFAULT' THEN
      DEFAULT
    ELSE
      $2
    END,
    $3
  )
EOQ;

// ...

but I got another error message, 'PHP Warning: pg_prepare(): Query failed: ERROR: DEFAULT is not allowed in this context'...

CodePudding user response:

DEFAULT is an SQL keyword, so part of the SQL statement, and you cannot use a parameter for it. You'll have to use two different INSERT statements in your PHP code, one that inserts moment, and one that doesn't.

The alternative is to use dynamic SQL, but I think that would be less readable and more complicated (and you have to be careful to avoid SQL injection).

CodePudding user response:

A "collection of bizarre reasons" calls for a bizarre solution. If you have to supply 3 parameters but only use 2 then I suggest to change the query so that it will silently ignore the second one.

$query = <<<EOQ
INSERT INTO log
VALUES
(
 case when $2 is null or $2 is not null then $1 end, -- const.true but $2 is 'used'
 DEFAULT,
 $3
)
EOQ;

or, better, use the default expression explicitly

$query = <<<EOQ
INSERT INTO
  acessos
VALUES
  (
    $1,
    CASE WHEN $2 ~* '^DEFAULT$' THEN clock_timestamp() ELSE $2::timestamp END,
    $3
  )
EOQ;
  • Related