Uploaded image for project: 'Calcite'
  1. Calcite
  2. CALCITE-4493

Postgres/Mysql String Literal Equality Creates incorrect results.

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Open
    • Major
    • Resolution: Unresolved
    • None
    • None
    • None

    Description

      Calcite always interprets String Literals as CHAR type.  This is normally not an issue since if are literal is being compared to VARCHAR column, then the literal is cast to a VARCHAR.  VARCHAR and CHAR equality have slight different behavior.  CHAR ignores trailing whitespace when comparing, where VARCHAR does not.  Postgres defaults the a strings literals type as unkown in a given expression, but will default them to text if there evaluated to row.

      Postgres treats string literals types as unknown or TEXT.  Postgres TEXT follows VARCHAR semantics.

      RelToSqlConverterTest.java

      @Test void testDefaultTypeStringLiteralType() {
        RuleSet rules = RuleSets.ofList(CoreRules.PROJECT_VALUES_MERGE, CoreRules.PROJECT_REDUCE_EXPRESSIONS);
        HepProgramBuilder builder = new HepProgramBuilder();
        builder.addRuleClass(ProjectToWindowRule.class);
        HepPlanner hepPlanner = new HepPlanner(builder.build());
        sql("SELECT 'foo ' = 'foo'")
            .optimize(rules, hepPlanner)
            .withPostgresql().ok("SELECT *\nFROM (VALUES (TRUE)) AS \"t\" (\"EXPR$0\")");
      }
      

       This is generating incorrect results since postgres would treat the literals TEXT which follow the VARCHAR semantics and treats trailing whitespace as significant.

      postgres=# SELECT pg_typeof(a), pg_typeof('b') FROM (VALUES('foo')) AS t(a);
       pg_typeof | pg_typeof 
      -----------+-----------
       text      | unknown
      (1 row)
      

       

      postgres=# SELECT 'foo ' = 'foo';
       ?column----------
       f
      (1 row)
      

      Concerning other dialects, I ran the following query:

      SELECT
      case when t.a = t.b then 'true' else 'false' end as example1, --example 1 No Type Value literal equality
      case when 'foo' = 'foo ' then 'true' else 'false' end as example2, --example 2 No Type literal comparison
      case when t.a = 'foo ' then 'true' else 'false' end as example3, --example 3 No Type Value to No Type Literal
      case when t.c = 'foo ' then 'true' else 'false' end as example4, --example 4 Typed Value to No Type Literal
      CASE WHEN t.b = CAST('foo' AS CHAR(3)) then 'true' else 'false' end as example5, --example 5 No Type Value to Typed Literal
      '|' || CAST('a ' AS VARCHAR(2)) || '|' as example6
      FROM (
              select 'foo' a, 'foo ' b, CAST('foo' AS CHAR(3)) c --FROM DUAL 
      ) t
      ;

       

      DB a = b ‘foo’ = ‘foo ‘ a = ‘foo ‘ c = ‘foo ‘ b = CAST(‘foo’ AS CHAR(3)) CAST(‘a ‘ TO VARCHAR(2)
      Dremio false false false false false |a |
      Postgres false false false true false |a |
      Oracle false false false true true |a |
      MySQL false false false false false |a |
      SQL Server true true true true true |a |
      Snowflake false false false false false |a |
      ISO SQL true true true true true |a|

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              jamesstarr James Starr
              Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

                Created:
                Updated:

                Time Tracking

                  Estimated:
                  Original Estimate - Not Specified
                  Not Specified
                  Remaining:
                  Remaining Estimate - 0h
                  0h
                  Logged:
                  Time Spent - 20m
                  20m