1

EDIT

It seems my issue is when this select statement returns null (which is the case I'm trying to handle - when it returns null, I want my new value to be -999). How can I go about doing this if it errors out whenever a null is found?

ORIGINAL

I have read every other SO post I could find regarding this error, but none of which seemed to address the root of my issue.

The error is pretty straightforward - one of my arguments within my EXECUTE statement is null. Great. However, I print out each of the values that make up my EXECUTE statement right before it gets called, and I can clearly see that none of the values are null.

Code:

CREATE FUNCTION inform_icrm_prob_flow_query(tablename text, location_id int,
                                            product_date_str text, lead_time_start int,
                                            lead_time_end int, first_member_id int,
                                            last_member_id int, dest_file text)
RETURNS void AS $$
DECLARE
  count int;
  product_date TIMESTAMPTZ;
  interval_lead_time_start text;
  interval_lead_time_end text;
  curr_value double precision;
  query text;
BEGIN
  product_date := product_date_str::TIMESTAMPTZ;
  count := first_member_id;
  curr_value := 0;

  interval_lead_time_start :=  ''''|| product_date ||'''::timestamptz +
                               interval '''||lead_time_start||' hours''';
  interval_lead_time_end :=  ''''|| product_date ||'''::timestamptz +
                             interval '''||lead_time_end||' hours'' -
                             interval ''6 hours''';

  --create our temporary table and populate it's date column
  EXECUTE 'CREATE TEMPORARY TABLE temp_table_icrm_prob_flow AS 
             SELECT * FROM generate_series('||interval_lead_time_start || ',' || 
                                            interval_lead_time_end || ', ''6 hours'') 
                           AS date_valid';
  LOOP
    EXIT WHEN count > last_member_id;
    IF NOT EXISTS(
      SELECT 'date_valid'
      FROM information_schema.columns
      WHERE table_name='temp_table_icrm_prob_flow'
        and column_name='value'||count||'') 
    THEN
      EXECUTE 'ALTER TABLE temp_table_icrm_prob_flow ADD COLUMN value' || count 
               || ' double precision DEFAULT -999';
    END IF;

    raise notice 'tablename: %', tablename;
    raise notice 'location_id: %', location_id;
    raise notice 'product_date: %', product_date;
    raise notice 'count: %', count;

    query := 'SELECT value FROM '|| tablename ||' 
              INNER JOIN temp_table_icrm_prob_flow
              ON (temp_table_icrm_prob_flow.date_valid = '|| tablename ||'.date_valid)
              WHERE '|| tablename ||'.id_location = '|| location_id ||'
                AND '|| tablename ||'.date_product = '''|| product_date ||'''
                AND '|| tablename ||'.id_member = '|| count ||'';

    EXECUTE query INTO curr_value;

    EXECUTE 'UPDATE temp_table_icrm_prob_flow 
             SET value'|| count ||' = COALESCE('|| curr_value ||', -999)';

    count := count + 1;
  END LOOP;

  EXECUTE 'ALTER TABLE temp_table_icrm_prob_flow DROP COLUMN date_valid';

  EXECUTE 'COPY temp_table_icrm_prob_flow TO '''||dest_file||''' DELIMITER '','' CSV';

  EXECUTE 'DROP TABLE temp_table_icrm_prob_flow';
END;
$$ LANGUAGE plpgsql;

Output:

NOTICE:  tablename: inform_tseries_data_basin_proc_fcst_prob_flow
NOTICE:  location_id: 38
NOTICE:  product_date: 2015-02-05 12:00:00+00
NOTICE:  count: 1
ERROR:  query string argument of EXECUTE is null
CONTEXT:  PL/pgSQL function inform_icrm_prob_flow_query(text,integer,text,integer,integer,integer,integer,text) line 38 at EXECUTE

If none of the variables I am passing in are null, and the only other thing referenced is a temp table that I know exists, what could be causing this error?

Note: when changing my query to:

query := 'SELECT value FROM '|| tablename ||' WHERE '|| tablename ||'.id_location = '|| location_id ||' AND '|| tablename ||'.date_product = '''|| product_date ||''' AND '|| tablename ||'.id_member = '|| count ||' AND temp_table_icrm_prob_flow.date_va    lid = '|| tablename ||'.date_valid';

I get the following error:

NOTICE:  tablename: inform_tseries_data_basin_proc_fcst_prob_flow
NOTICE:  location_id: 38
NOTICE:  product_date: 2015-02-05 12:00:00+00
NOTICE:  count: 1
ERROR:  missing FROM-clause entry for table "temp_table_icrm_prob_flow"
LINE 1: ..._data_basin_proc_fcst_prob_flow.id_member = 1 AND temp_table...
                                                         ^
QUERY:  SELECT value FROM inform_tseries_data_basin_proc_fcst_prob_flow WHERE inform_tseries_data_basin_proc_fcst_prob_flow.id_location = 38 AND inform_tseries_data_basin_proc_fcst_prob_flow.date_product = '2015-02-05 12:00:00+00' AND inform_tseries_data_basin_proc_fcst_prob_flow.id_member = 1 AND temp_table_icrm_prob_flow.date_valid = inform_tseries_data_basin_proc_fcst_prob_flow.date_valid
CONTEXT:  PL/pgSQL function inform_icrm_prob_flow_query(text,integer,text,integer,integer,integer,integer,text) line 35 at EXECUTE
7
  • Check curr_value after EXECUTE query INTO curr_value; Commented Jun 29, 2016 at 22:27
  • curr_value is unsurprisingly null Commented Jun 29, 2016 at 22:33
  • So we are found your problem. Use execute '...' into strict ...; to ensure that the query returns the data. Commented Jun 29, 2016 at 22:45
  • That seems to have worked. Thank you! Commented Jun 29, 2016 at 22:54
  • @Abelisto As per the docs: If the STRICT option is given, an error is reported unless the query produces exactly one row. You solve a symptom, not a problem. Commented Jun 29, 2016 at 23:28

1 Answer 1

12

Sorry for small offtopic. Your code is pretty unreadable (and SQL injecttion vulnerable). There are some techniques, that you can use:

  1. Use clause USING of EXECUTE statement for usual parameters.

    DO $$
    DECLARE
      tablename text := 'mytab';
      from_date date := CURRENT_DATE;
    BEGIN
       EXECUTE 'INSERT INTO ' || quote_ident(tablename) || ' VALUES($1)'
          USING from_date;
    END
    $$;
    

    This code will be safe (due using quote_ident function), little bit faster (due using binary value of from_date variable - removed multiple string<->date conversions and little bit more readable (because string expression is shorter).

  2. Use function format. The building query string will be shorter and more readable (table aliases helps too):

    query := format('
    SELECT value
       FROM %I _dtn
            INNER JOIN temp_table_icrm_prob_flow t ON t.date_valid = _dtn.date_valid
      WHERE _dtn.id_location = $1
        AND _dtn.date_product = $2
        AND _dtd.id_member = $3'
                     , tablename);
     EXECUTE query INTO curr_value USING location_id, product_date, count;
    

Using variables named like important SQL keywords and identifier is wrong idea - names count, values are wrong.

The error message is clean - you are using the identifier temp_table_icrm_prob_flow.date_valid, but the table temp_table_icrm_prob_flow is not mentioned in query. The query missing JOIN part.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.