/* FwdExpr * handle a forwarded symbol in expression * if the symbol is "virtual", check for its override * map actual symbol type to expression handler */ static rc_t VProdResolveFwdExpr ( const VProdResolve *self, VProduction **out, VFormatdecl *fd, const SSymExpr *x, bool casting ) { /* virtual names that appear in parent table expressions may be overridden in children */ const KSymbol *sym = x -> _sym; if ( sym -> type == eVirtual ) { /* most derived table class */ const STable *stbl = self -> stbl; const KSymbol *sym2 = sym; sym = STableFindOverride ( stbl, ( const VCtxId* ) & sym -> u . fwd ); if ( sym == NULL ) { PLOGMSG ( klogWarn, ( klogWarn, "virtual reference '$(fwd)' not found in overrides table" , "fwd=%.*s" , ( int ) sym2 -> name . size , sym2 -> name . addr )); return 0; } } /* test symbol type */ switch ( sym -> type ) { case eProduction: return VProdResolveProdExpr ( self, out, sym ); case ePhysMember: return VProdResolvePhysExpr ( self, out, sym ); case eColumn: return VProdResolveColExpr ( self, out, fd, x, casting ); } VDB_DEBUG (("%s: unresolved forward reference '%S'", __func__, &sym->name)); return 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; }
/* 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; }
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; }