skip to Main Content

In my Postgres DB, I recently added the following calculated field value to the SELECT SQL query when users login:

SUM(m.file_size) AS byte_count

Because media.file_size (aka m.file_size) has an int8 data type, I naturally assumed that byte_count would be returned with the same. But lo and behold, it was being returned as a string.

So I modified the SQL code like this:

SUM(m.file_size)::bigint AS byte_count

That still returned a string so I tried ::int8 but still a string. Then I tried:

CAST(SUM(m.file_size) AS bigint) AS byte_count

but still a string. So finally I tried this:

SUM(m.file_size)::int AS byte_count

This now returns a number (good!) but my fear is that as a user’s byte total climbs above the capacity of int, that I’m going to run into problems. In fact, I put a very large number into a related max_bytes field – 223372036854775808 and sure enough, got an "integer out of range" error.

Is there some other way to cast the calculated field as an 8 byte bigint?

As per @Adrian Claver in the comments:

  1. I’m doing a console.log of the output from the server and can clearly see when the value is a number or a string.
  2. I’m using ExpressJS for my server code.
  3. I’m running Postgres 14.4 on my MacBook

In my users table is this field:

max_bytes  int8

In my signInUser function in my server code, is this calculated column:

COALESCE(SUM(m.file_size)::int, 0) AS byte_count

In my media table is this field:
file_size int8

3

Answers


  1. Chosen as BEST ANSWER

    The current version of the SELECT statement includes the following:

    u.max_bytes::int
    COALESCE(SUM(m.file_size)::int, 0) AS byte_count
    

    which returns the following, in this example, as per a console.log:

    {
      user_id: 'e8e4144d-f610-4c7f-a6a6-77355e16c297',
      user_name: 'JayNug',
      roles: [ 'FreeTrial' ],
      first_name: 'Jay',
      last_name: 'Nuggehalli',
      date_created: 2022-09-23T23:11:26.854Z,
      date_deleted: null,
      date_suspended: null,
      email: '[email protected]',
      last_updated: 2022-09-25T00:09:11.565Z,
      max_bytes: 104857600,
      verified: true,
      last_signin: 2025-01-11T14:01:27.190Z,
      media_count: 2,
      public_count: 2,
      private_count: 0,
      byte_count: 2109055
    }
    

    But then, if I change the aforementioned SQL code back to what it was before:

    u.max_bytes
    COALESCE(SUM(m.file_size), 0) AS byte_count
    

    and changed the max_bytes value for this user to: 922337203685477580. Here's the result

    {
      user_id: 'e8e4144d-f610-4c7f-a6a6-77355e16c297',
      user_name: 'JayNug',
      roles: [ 'FreeTrial' ],
      first_name: 'Jay',
      last_name: 'Nuggehalli',
      date_created: 2022-09-23T23:11:26.854Z,
      date_deleted: null,
      date_suspended: null,
      email: '[email protected]',
      last_updated: 2022-09-25T00:09:11.565Z,
      max_bytes: '922337203685477580',
      verified: true,
      last_signin: 2025-01-11T14:12:33.598Z,
      media_count: '2',
      public_count: 2,
      private_count: 0,
      byte_count: '2109055'
    }
    

    To move forward, I think I have no choice but to return max_bytes and byte_count as strings and then cast them as numbers on the client side.


  2. Hm, without seeing the definition of the table, it’s hard to confirm if the type actually is of type int8. I.E., if I make an assumption with a table design as follows:

    CREATE TABLE MEDIA (
      emp_id SERIAL PRIMARY KEY,
      file_size int8 GENERATED ALWAYS as (emp_id * 2) STORED
    );
    
    -- insert
    INSERT INTO MEDIA VALUES (1);
    INSERT INTO MEDIA VALUES (2);
    INSERT INTO MEDIA VALUES (3);
    

    And then I select the values and confirm their type like such:

    -- fetch 
    SELECT emp_id,
    pg_typeof(emp_id) as e_type,
    sum(file_size) as bytes,
    pg_typeof(file_size) as f_type
    FROM MEDIA
    group by 1
    

    We can observe the following results:

    Output:
    
    CREATE TABLE
    INSERT 0 1
    INSERT 0 1
    INSERT 0 1
     emp_id | e_type  | bytes | f_type 
    -------+---------+-------+--------
         2 | integer |     4 | bigint
         3 | integer |     6 | bigint
         1 | integer |     2 | bigint
    (3 rows)
    

    As you can see, the type of the calculated column is indeed int8, bigint being an alias for int8.

    Even if I defined a column as a string and cast it within the calculated column (which I do not recommend doing, ever.) It still returns the accurate results:

    -- create
    CREATE TABLE MEDIA2 (
      some_int integer,
      some_str varchar,
      file_size int8 GENERATED ALWAYS as (some_int * some_str::int8) STORED
    );
    
    -- insert
    INSERT INTO MEDIA2 VALUES (1, '1');
    INSERT INTO MEDIA2 VALUES (2, '2');
    INSERT INTO MEDIA2 VALUES (3, '3');
    
    -- fetch 
    SELECT some_int,
    pg_typeof(some_int) as i_type,
    some_str,
    pg_typeof(some_str) as s_type,
    sum(file_size) as bytes,
    pg_typeof(file_size) as f_type
    FROM MEDIA2
    group by some_int, some_str, file_size
    
     some_int | i_type  | some_str |      s_type       | bytes | f_type 
    ----------+---------+----------+-------------------+-------+--------
            1 | integer | 1        | character varying |     1 | bigint
            3 | integer | 3        | character varying |     9 | bigint
            2 | integer | 2        | character varying |     4 | bigint
    (3 rows)
    

    So, can you confirm how your calculated column is declared in your table definition ? Obviously this is a simple example but, I can’t reproduce your results. It could potentially be with your calculated column definition and the types it’s using.

    It’s important to note that the pg_typeof() function is returning the type of the data as stored in the table. However, after summing, it is converted to type numeric as documented in Postgres Documentation sum ( bigint ) → numeric Functions Aggregate:

    -- fetch 
    SELECT emp_id,
    pg_typeof(emp_id) as e_type,
    sum(file_size) as bytes,
    pg_typeof(sum(file_size)) as f_type
    FROM MEDIA
    group by 1;
    
     emp_id | e_type  | bytes | f_type  
    --------+---------+-------+---------
          2 | integer |     4 | numeric
          3 | integer |     6 | numeric
          1 | integer |     2 | numeric
    (3 rows)
    

    To answer your question, which you technically already did, you can use the following methods to cast:

    CAST ( value AS target_type ) or value::target_type

    Login or Signup to reply.
  3. Just a quick follow-up and thank you to all that tried to help . . .

    Not today, but soon I will upgrade my local Postgres from v14.4 to the latest v17.2. Maybe that will correct it.

    Today, I did upgrade ExpressJS on my server from 4.18.1 to 4.21.2.

    In the client code, I’m performing this on-the-fly conversion:

    const newSession: SessionType = {
      ...sessionContainer.state,
        user: {
          ...userData,
          maxBytes: Number(userData.maxBytes),
          byteCount: Number(userData.byteCount)
        }
      };
    
      sessionContainer.setState(newSession);
    };
    

    And indeed, the two values in question are correctly being converted from strings to numbers.

    Once again, thank you all for your great help!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search