Beispiel #1
0
/* CastExpr
 *  inserts an explicit cast operation
 *
 *  in "C" style languages ( okay, ALGOL style ), a cast expression is
 *  used as a means of coercing the rh expression type to a fixed type,
 *  which is then assigned to the lh side with normal typecasting rules.
 *  specifically, performs an intermediate assignment allowing truncation
 *  to reshape and potentially reformat the rh value ( e.g. float to char ).
 *
 *  in "C++" there were new cast operators introduced to indicate more nuance:
 *    static_cast      - permits up and down-casts with compiler check
 *    const_cast       - modifies cv qualifiers [ not applicable to VDB ]
 *    dynamic_cast     - permits up and down-casts with runtime check
 *    reinterpret_cast - permits casts between unrelated classes with
 *                       compatible binary forms ( a la "C" style ).
 *
 *  in "VDB", the compiler preserves lh and rh type information until
 *  productions are resolved. assignments always involve implicit casts
 *  that permit casting from sub to super-type. our explicit cast expression
 *  performs something analogous to the C++ "static_cast" in that it allows
 *  direct up and down casts, as well as sibling and cousin casts.
 *
 *  specifically, LH = ( T ) RH when T is a sub-type of LH and:
 *    a) RH is a sub-type of T [ implicit rule ]
 *    b) T is a sub-type of RH [ downcast ]
 *    c) T and RH share a common ancestor
 *  in all cases, LH, T and RH must all have identical element sizes
 *
 *  implicit typecasting rules allow LH or RH to be a typeset. the types
 *  are initially refined to the intersection between LH and RH
 *    a) TYPE    x TYPE
 *    b) TYPESET x TYPE
 *    c) TYPE    x TYPESET
 *    d) TYPESET x TYPESET
 *  in the latter case, the intersection may produce more than one possible
 *  result, which would incur an error when evaluating the expression. a
 *  cast operator will remove the ambiguity.
 *
 *  the rh expression may involve a column name, with type overloading. this
 *  creates the same effect as a TYPESET. a cast operator can clarify an ambigous
 *  assignment, and in the case of downcasts, make it possible.
 *
 * NB
 *  to perform a C++ style reinterpret_cast, use the "cast" function
 */
static
rc_t VProdResolveCastExpr ( const VProdResolve *self, VProduction **out, const SBinExpr *expr )
{
    /* extract cast type */
    VFormatdecl cast;
    rc_t rc = STypeExprResolveAsFormatdecl
        ( ( const STypeExpr* ) expr -> left, self -> schema, & cast );
    if ( rc == 0 )
    {
        /* resolve rh expression */
        VTypedesc desc;
        VFormatdecl fd = cast;
        rc = VProdResolveExpr ( self, out, & desc,
            & fd, expr -> right, true );
        if ( rc != 0 || * out == NULL )
            return rc;

        /* casting mode allowed returned production to be:
           a) identical type
           b) sub-type
           c) super-type
           d) have common parent

           in all cases, the sizeof rh production element
           matches "cast" size */
        rc = VSimpleProdMake ( out, self -> owned, self -> curs, prodSimpleCast,
            "cast", & cast, & desc, NULL, * out, self -> chain );
    }

    return rc;
}
Beispiel #2
0
/* ProdExpr
 *  resolve a simple production by name
 *  create/return a VSimpleProd object
 */
rc_t VProdResolveSProduction ( const VProdResolve *self, VProduction **out, const SProduction *sprod )
{
    rc_t rc;
    VFormatdecl fd;

    /* check cache */
    VProduction *vprod = VCursorCacheGet ( self -> cache, & sprod -> cid );
    if ( vprod != NULL )
    {
        /* return valid or failed production */
        * out = vprod;
        return 0;
    }

    /* pre-fail */
    rc = VCursorCacheSet ( self -> cache, & sprod -> cid, FAILED_PRODUCTION );
    if ( rc == 0 )
    {
        /* resolve production type */
        if ( sprod -> trigger )
            memset ( & fd, 0, sizeof fd );
        else
        {
            rc = STypeExprResolveAsFormatdecl
                ( ( const STypeExpr* ) sprod -> fd, self -> schema, & fd );
        }
    }
    if ( rc == 0 )
    {
        /* resolve assignment expression */
        VTypedesc desc;
        rc = VProdResolveExpr ( self, out, & desc,
            & fd, sprod -> expr, false );
        if ( rc == 0 && * out != NULL )
        {
            const char *name = sprod -> name -> name . addr;
            assert ( name [ sprod -> name -> name . size ] == 0 );
            rc = VSimpleProdMake ( out, self -> owned, self -> curs, prodSimpleCast, 
                name, & fd, & desc, & sprod -> cid, * out, self -> chain );
            if ( rc == 0 )
            {
                void *ignore;
                rc = VCursorCacheSwap ( self -> cache, & sprod -> cid, * out, & ignore );
            }
        }
    }

    return rc;
}
Beispiel #3
0
/* ResolveColumn
 *  resolves a column using read/write expression
 */
rc_t VProdResolveColumnRoot ( const VProdResolve *self,
    VProduction **out, const SColumn *scol )
{
    rc_t rc;
    WColumn *wcol;
    VTypedesc desc;
    const char *name;
    VCursor *curs = self -> curs;
    VProduction * in;

    * out = NULL;

    /* decide upon behavior */
    if ( curs -> read_only )
    {
        rc = VProdResolveColumnRead ( self, out, scol );
        if ( rc == 0 && * out <= FAILED_PRODUCTION )
            return RC ( rcVDB, rcCursor, rcOpening, rcColumn, rcUndefined );
        return rc;
    }

    /* write-only cursor must have existing column */
    wcol = VCursorCacheGet ( & curs -> col, & scol -> cid );
    if ( wcol == NULL )
        return 0;

    /* not intended to be reentrant */
    assert ( wcol -> val == NULL );

    /* evaluate input expression */
    if ( scol -> validate == NULL )
    {
        /* use normal read expression */
        rc = VProdResolveColumnRead ( self, &in, scol );
    }
    else
    {
        VFormatdecl fd;
        
        /* create fmtdecl from typedecl */
        memset ( & fd, 0, sizeof fd );

        VDB_DEBUG ( ( "resolving column '%N' validate expression.\n", scol -> name ) );

        /* use validation expression */
        rc = VProdResolveExpr ( self, &in, & desc, & fd, scol -> validate, false );
    }

    /* check failures */
    if ( rc != 0 )
    {
        VDB_DEBUG ( ( "failed to resolve column '%N' - %R.\n", scol -> name, rc ) );
        return rc;
    }
    if ( in <= FAILED_PRODUCTION )
    {
        VDB_DEBUG ( ( "failed to resolve column '%N' - NULL or failed production.\n", scol -> name ) );
        return RC ( rcVDB, rcCursor, rcOpening, rcColumn, rcUndefined );
    }

    /* column name */
    name = scol -> name -> name . addr;

    /* pick up production */
    if ( scol -> validate != NULL )
    {
        rc = VSimpleProdMake ( & wcol -> val, self -> owned,
            prodSimpleCast, name, NULL, NULL, NULL, in, chainDecoding );
        if ( rc != 0 )
            return rc;
    }

    /* create implicit comparison function */
    else
    {
        /* need an output production */
        if ( wcol -> out == NULL )
        {
            rc = VColumnProdMake ( & wcol -> out, self -> owned,
                & wcol -> dad, prodColumnOut, name );
            if ( rc != 0 )
                return rc;
        }

        /* create comparison func */
        rc = VFunctionProdMakeBuiltInComp ( & wcol -> val, self -> owned,
            name, self, wcol -> out, in );
        if ( rc != 0 )
            return rc;
    }

    /* install trigger */
    rc = VectorAppend ( & curs -> trig, NULL, wcol -> val );
    if ( rc == 0 )
        * out = wcol -> val;

    return rc;
}
Beispiel #4
0
/* ResolvePhysical
 *  resolves a physical column
 */
static
rc_t VProdResolvePhysicalWrite ( const VProdResolve *self, VPhysical *phys )
{
    VTypedesc desc;
    VFormatdecl fd;
    VProdResolve pr;
    VProduction *prod;
    VCursor *curs = self -> curs;

    const char *name;
    const SExpression *enc;
    const SPhysMember *smbr;

    /* open the physical column for write
       load column metadata/schema, complete
       physical member description. */
    rc_t rc = VPhysicalOpenWrite ( phys,
        ( VSchema* ) self -> schema, curs -> tbl );
    if ( rc != 0 )
        return rc;

    /* there are two conditions under which a physical member
       definition would be incommplete prior to opening the
       column: 1) if the physical column were only forwarded
       within table schema, or 2) if the column were added as
       the result of a file system scan.

       for the column to be writable, it must have had a complete
       member definition from table schema, with a type and an
       assignment expression, or it must have been added as the
       result of an fs scan with a simple reciprocal expression.
       the test for these two cases is for a resolved typedecl
       and an assignment expression. */

    /* nothing more to do if column does not exist
       and member was undeclared, or is declared read-only */
    smbr = phys -> smbr;
    if ( smbr -> td . type_id == 0 || smbr -> expr == NULL )
        return 0;

    /* build fmtdecl */
    fd . td = smbr -> td;
    fd . fmt = 0;

    /* shift to encode chain */
    pr = * self;
    pr . chain = chainEncoding;

    /* resolve the input expression */
    rc = VProdResolveExpr ( & pr, & phys -> in, & desc, & fd, smbr -> expr, false );
    if ( rc == 0 && phys -> in == NULL )
        return RC ( rcVDB, rcCursor, rcOpening, rcColumn, rcUndefined );

    /* NB - at this point, fd and desc
       represent the column's well-defined type */

    /* member name */
    name = smbr -> name -> name . addr;

    /* physical encoding */
    enc = phys -> enc;
    if ( enc == NULL )
        enc = smbr -> type;

    /* build encoding schema in steps:
         in <- page-to-blob
    */
    rc = VSimpleProdMake ( & prod, pr . owned,
        prodSimplePage2Blob, name, & fd, & desc, NULL, phys -> in, chainEncoding );
    if ( rc == 0 && enc != NULL )
    {
        /* in <- p2b <- encoding-func */
        pr . blobbing = true;
        rc = VProdResolveEncodingExpr ( & pr, & prod,
            prod, ( const SPhysEncExpr* ) enc );
        if ( rc == 0 )
        {
            fd = prod -> fd;
            desc = prod -> desc;
        }
    }
    if ( rc == 0 )
    {
        rc = VSimpleProdMake ( & phys -> b2s, pr . owned,
            prodSimpleBlob2Serial, name, & fd, & desc, NULL, prod, chainEncoding );
    }

    return rc;
}
Beispiel #5
0
/* ResolveColumn
 *  resolves a column from read/validate expression
 */
rc_t VProdResolveColumnRead ( const VProdResolve *self,
    VProduction **out, const SColumn *scol )
{
    rc_t rc;
    VFormatdecl fd;
    const char *name;
    VCursor *curs;
    VColumn *vcol;

    VDB_DEBUG ( ( "resolving column '%N' read expression.\n", scol -> name ) );

    /* potential error if self is NULL */
    curs = self -> curs;
    if ( out == NULL )
    {
        rc =  RC(rcVDB, rcProduction, rcResolving, rcParam, rcNull);
        VDB_DEBUG ( ( "result NULL for column '%N'; no output can be produced by '%s' rc %R\n",
                      scol -> name, __func__, rc ) );
        return rc;
    }
    
    /* fetch the column */
    vcol = VCursorCacheGet ( & curs -> col, & scol -> cid );
    if ( vcol == NULL )
    {
        VDB_DEBUG ( ( "failed to fetch NULL for column '%N'; no output was produced by '%s'\n",
                      scol -> name, __func__ ) );
        return 0;
    }

    /* if the read production is in place, return it */
    if ( vcol -> in != NULL )
    {
        if ( vcol -> in != FAILED_PRODUCTION )
            * out = vcol -> in;
        return 0;
    }

    /* pre-fail */
    vcol -> in = FAILED_PRODUCTION;

    /* production resolution works with fmtdecl */
    fd . td = scol -> td;
    fd . fmt = 0;

    /* resolve the expression */
    rc = VProdResolveExpr ( self, out, & vcol -> desc, & fd, scol -> read, false );
    assert (rc != -1);

    if ( rc != 0 || *out == NULL )
        return rc;

    /* repair incomplete column declarations */
    if ( scol -> td . type_id == 0 )
    {
        if ( fd . td . type_id == 0 )
        {
            rc = RC ( rcVDB, rcColumn, rcResolving, rcType, rcUndefined );
            VDB_DEBUG (("failed to repair incomplete declaration for column '%N' rc %R\n",
                        scol -> name, rc));
            return rc;
        }
        ( ( SColumn* ) scol ) -> td = fd . td;
    }

    /* create a simple prod to manage fd and desc */
    name = scol -> name -> name . addr;
    assert ( name [ scol -> name -> name . size ] == 0 );
    rc = VSimpleProdMake ( out, self -> owned, self->curs, prodSimpleCast,
        name, & fd, & vcol -> desc, NULL, * out, self -> chain );

    assert (rc != -1);
    if ( rc != 0 )
    {
        VDB_DEBUG (("failed to create a simple prod to manage fd and desc for column '%N', rc %R\n",
                    scol -> name, rc));
        return rc;
    }

    /* return column input - this is input to the column
       that is intended to be read from the cursor */
    vcol -> in = * out;
    return rc;
}
Beispiel #6
0
rc_t VProdResolveExpr ( const VProdResolve *self,
    VProduction **out, VTypedesc *desc, VFormatdecl *fd,
    const SExpression *expr, bool casting )
{
    rc_t rc;
    VProduction *prod;

    if ( expr == NULL )
    {
        /* report NULL expression, but don't die */
        PLOGMSG ( klogWarn, ( klogWarn, "NULL expression in '$(tbl)' table schema"
                   , "tbl=%.*s"
                   , ( int ) self -> stbl -> name -> name . size
                   , self -> stbl -> name -> name . addr ));
        return 0;
    }

    prod = NULL;
    *out = NULL;

#if _DEBUGGING
    ++ indent_level;
    VDB_DEBUG (( "%*cresolving expression '%s'\n", indent_level, ' ',
                 VProdResolvePrintExpr ( self, expr ) ));
#endif

    switch ( expr -> var )
    {
    case eParamExpr:
        /* a script function is making reference to a parameter */
        rc = VProdResolveParamExpr ( self, & prod, ( ( const SSymExpr* ) expr ) -> _sym );
    assert (rc != -1);
        break;

    case eProdExpr:
        /* return a simple production */
        rc = VProdResolveProdExpr ( self, & prod, ( ( const SSymExpr* ) expr ) -> _sym );
    assert (rc != -1);
        break;

    case eFwdExpr:
        /* handle an implicit or overridden reference */
        rc = VProdResolveFwdExpr ( self, & prod, fd, ( const SSymExpr* ) expr, casting );
    assert (rc != -1);
        break;

    case eColExpr:
        /* return a column production */
        rc = VProdResolveColExpr ( self, & prod, fd, ( const SSymExpr* ) expr, casting );
    assert (rc != -1);
        break;

    case ePhysExpr:
        /* return a physical production */
        rc = VProdResolvePhysExpr ( self, & prod, ( ( const SSymExpr* ) expr ) -> _sym );
    assert (rc != -1);
        break;

    case eScriptExpr:
        /* create a script function */
        rc = VProdResolveScriptExpr ( self, & prod, fd, ( const SFuncExpr* ) expr );
    assert (rc != -1);
        break;

    case eFuncExpr:
        /* create an external function */
        rc = VProdResolveFuncExpr ( self, & prod, fd, ( const SFuncExpr* ) expr );
    assert (rc != -1);
        break;

    case eCastExpr:
        /* perform an explicit cast */
        rc = VProdResolveCastExpr ( self, & prod, ( const SBinExpr* ) expr );
    assert (rc != -1);
        break;
        
    case eCondExpr:
        /* run left and right expressions in order until exit condition */
        rc = VProdResolveExpr ( self, out, desc, fd, ( ( const SBinExpr* ) expr ) -> left, casting );
    assert (rc != -1);
        if ( ( rc == 0 && * out == NULL ) || self -> discover_writable_columns )
        {
            rc = VProdResolveExpr ( self, out, desc, fd, ( ( const SBinExpr* ) expr ) -> right, casting );
    assert (rc != -1);
        }
#if _DEBUGGING
        -- indent_level;
#endif
        return rc;

    default:
        /* report bad expression, but don't die */
        PLOGMSG ( klogWarn, ( klogWarn, "unrecognized expression in '$(tbl)' table schema"
                   , "tbl=%.*s"
                   , ( int ) self -> stbl -> name -> name . size
                   , self -> stbl -> name -> name . addr ));
#if _DEBUGGING
        -- indent_level;
#endif
        return 0;
    }

    /* guard against returns of NULL or FAILED_PRODUCTION */
    if ( rc == 0 && prod > FAILED_PRODUCTION )
    {
        VDB_DEBUG ( ("%*cresolved expression  '%s'\n", indent_level, ' ', VProdResolvePrintExpr ( self, expr ) ) );

        /* returned production must be on requested chain */
        if ( ( prod -> chain & self -> chain ) == 0 )
        {
            rc = RC ( rcVDB, rcProduction, rcResolving, rcSchema, rcInconsistent );
            VDB_DEBUG ( ( "%*cPRODUCTION RESOLVED ON WRONG FORK: %R\n", indent_level, ' ', rc ) );
        }
        else
        {
            /* fix uncommitted production chain */
            if ( prod -> chain == chainUncommitted )
                prod -> chain = self -> chain;

            /* test for type compatibility - modifies "fd" */
            if ( casting ?
                 VFormatdeclCommonAncestor ( & prod -> fd, self -> schema, fd, fd, NULL ) :
                 VFormatdeclToFormatdecl ( & prod -> fd, self -> schema, fd, fd, NULL ) )
            {
                /* if no type description is requested, we're done */
                if ( desc == NULL )
                    * out = prod;
                else
                {
                    /* otherwise, create a type description */
                    rc = VSchemaDescribeTypedecl ( self -> schema, desc, & fd -> td );
    assert (rc != -1);
                    if ( rc != 0 )
                        VDB_DEBUG ( ( "%*cREQUESTED TYPE CANNOT BE DESCRIBED: %R\n", indent_level, ' ', rc ) );
                    else
                        * out = prod;
                }
            }
            else
            {
#if _DEBUGGING
                char from [ 128 ] = "", to [ 128 ] = "";
                VTypedeclToText ( & prod -> fd . td, self -> schema, from, sizeof from );
                VTypedeclToText ( & fd -> td, self -> schema, to, sizeof to );
                VDB_DEBUG ( ( "%*cexpression '%s' cannot be %s from '%s' to '%s'\n"
                              , indent_level, ' '
                              , VProdResolvePrintExpr ( self, expr )
                              , casting ? "cast" : "typed"
                              , from
                              , to
                             )
                    );
#endif
            }
        }
    }
    else if ( rc != 0 )
    {
        VDB_DEBUG ( ( "failed to resolve expression '%s' prod %p %R\n", VProdResolvePrintExpr ( self, expr ), prod, rc ) );
    }
    else if ( prod == NULL )
    {
        VDB_DEBUG ( ( "expression '%s' was not resolved\n", VProdResolvePrintExpr ( self, expr ) ) );
    }
    else
    {
        VDB_DEBUG ( ( "expression '%s' returned FAILED_PRODUCTION\n", VProdResolvePrintExpr ( self, expr ) ) );
    }

#if _DEBUGGING
    -- indent_level;
#endif

    return rc;
}