1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 #include <vdb/extern.h>
28 
29 #define TRACK_REFERENCES 0
30 
31 #define KONST const
32 #include "schema-priv.h"
33 #include "schema-expr.h"
34 #include "schema-parse.h"
35 #include "cursor-priv.h"
36 #include "column-priv.h"
37 #include "prod-priv.h"
38 #include "prod-expr.h"
39 #include "phys-priv.h"
40 #include "view-priv.h"
41 #undef KONST
42 
43 #include <vdb/schema.h>
44 #include <vdb/cursor.h>
45 #include <vdb/xform.h>
46 #include <klib/symbol.h>
47 #include <klib/debug.h>
48 #include <klib/log.h>
49 #include <klib/rc.h>
50 #include <sysalloc.h>
51 
52 #include <stdlib.h>
53 #include <string.h>
54 #include <assert.h>
55 
56 
57 /*--------------------------------------------------------------------------
58  * VProdResolve
59  */
60 
61 /* CastExpr
62  *  inserts an explicit cast operation
63  *
64  *  in "C" style languages ( okay, ALGOL style ), a cast expression is
65  *  used as a means of coercing the rh expression type to a fixed type,
66  *  which is then assigned to the lh side with normal typecasting rules.
67  *  specifically, performs an intermediate assignment allowing truncation
68  *  to reshape and potentially reformat the rh value ( e.g. float to char ).
69  *
70  *  in "C++" there were new cast operators introduced to indicate more nuance:
71  *    static_cast      - permits up and down-casts with compiler check
72  *    const_cast       - modifies cv qualifiers [ not applicable to VDB ]
73  *    dynamic_cast     - permits up and down-casts with runtime check
74  *    reinterpret_cast - permits casts between unrelated classes with
75  *                       compatible binary forms ( a la "C" style ).
76  *
77  *  in "VDB", the compiler preserves lh and rh type information until
78  *  productions are resolved. assignments always involve implicit casts
79  *  that permit casting from sub to super-type. our explicit cast expression
80  *  performs something analogous to the C++ "static_cast" in that it allows
81  *  direct up and down casts, as well as sibling and cousin casts.
82  *
83  *  specifically, LH = ( T ) RH when T is a sub-type of LH and:
84  *    a) RH is a sub-type of T [ implicit rule ]
85  *    b) T is a sub-type of RH [ downcast ]
86  *    c) T and RH share a common ancestor
87  *  in all cases, LH, T and RH must all have identical element sizes
88  *
89  *  implicit typecasting rules allow LH or RH to be a typeset. the types
90  *  are initially refined to the intersection between LH and RH
91  *    a) TYPE    x TYPE
92  *    b) TYPESET x TYPE
93  *    c) TYPE    x TYPESET
94  *    d) TYPESET x TYPESET
95  *  in the latter case, the intersection may produce more than one possible
96  *  result, which would incur an error when evaluating the expression. a
97  *  cast operator will remove the ambiguity.
98  *
99  *  the rh expression may involve a column name, with type overloading. this
100  *  creates the same effect as a TYPESET. a cast operator can clarify an ambigous
101  *  assignment, and in the case of downcasts, make it possible.
102  *
103  * NB
104  *  to perform a C++ style reinterpret_cast, use the "cast" function
105  */
106 static
VProdResolveCastExpr(const VProdResolve * self,VProduction ** out,const SBinExpr * expr)107 rc_t VProdResolveCastExpr ( const VProdResolve *self, VProduction **out, const SBinExpr *expr )
108 {
109     /* extract cast type */
110     VFormatdecl cast;
111     rc_t rc = STypeExprResolveAsFormatdecl
112         ( ( const STypeExpr* ) expr -> left, self -> schema, & cast, self -> cx_bind );
113     if ( rc == 0 )
114     {
115         /* resolve rh expression */
116         VTypedesc desc;
117         VFormatdecl fd = cast;
118         rc = VProdResolveExpr ( self, out, & desc,
119             & fd, expr -> right, true );
120         if ( rc != 0 || * out == NULL )
121             return rc;
122 
123         /* casting mode allowed returned production to be:
124            a) identical type
125            b) sub-type
126            c) super-type
127            d) have common parent
128 
129            in all cases, the sizeof rh production element
130            matches "cast" size */
131         rc = VSimpleProdMake ( out, self -> owned, self -> curs, prodSimpleCast,
132             "cast", & cast, & desc, NULL, * out, self -> chain );
133     }
134 
135     return rc;
136 }
137 
138 /* ParamExpr
139  *  resolve a simple parameter by name
140  */
VProdResolveParamExpr(const VProdResolve * self,VProduction ** out,const KSymbol * sym)141 LIB_EXPORT rc_t CC VProdResolveParamExpr ( const VProdResolve *self, VProduction **out, const KSymbol *sym )
142 {
143     const SProduction *sprod = sym -> u . obj;
144     VProduction *vprod = VCursorCacheGet ( self -> cache, & sprod -> cid );
145     if ( vprod != NULL )
146     {
147         * out = vprod;
148         return 0;
149     }
150 
151     PLOGMSG ( klogWarn, ( klogWarn, "unknown parameter '$(param)' used in expression"
152                           , "param=%.*s"
153                           , ( int ) sprod -> name -> name . size
154                           , sprod -> name -> name . addr ));
155     return 0;
156 }
157 
158 
159 /* ProdExpr
160  *  resolve a simple production by name
161  *  create/return a VSimpleProd object
162  */
VProdResolveSProduction(const VProdResolve * self,VProduction ** out,const SProduction * sprod)163 rc_t VProdResolveSProduction ( const VProdResolve *self, VProduction **out, const SProduction *sprod )
164 {
165     rc_t rc;
166     VFormatdecl fd;
167 
168     /* check cache */
169     VProduction *vprod = VCursorCacheGet ( self -> cache, & sprod -> cid );
170     if ( vprod != NULL )
171     {
172         /* return valid or failed production */
173         * out = vprod;
174         return 0;
175     }
176 
177     /* pre-fail */
178     rc = VCursorCacheSet ( self -> cache, & sprod -> cid, FAILED_PRODUCTION );
179     if ( rc == 0 )
180     {
181         /* resolve production type */
182         if ( sprod -> trigger )
183             memset ( & fd, 0, sizeof fd );
184         else
185         {
186             rc = STypeExprResolveAsFormatdecl
187                 ( ( const STypeExpr* ) sprod -> fd, self -> schema, & fd, self -> cx_bind );
188         }
189     }
190     if ( rc == 0 )
191     {
192         /* resolve assignment expression */
193         VTypedesc desc;
194         rc = VProdResolveExpr ( self, out, & desc,
195             & fd, sprod -> expr, false );
196         if ( rc == 0 && * out != NULL )
197         {
198             const char *name = sprod -> name -> name . addr;
199             assert ( name [ sprod -> name -> name . size ] == 0 );
200             rc = VSimpleProdMake ( out, self -> owned, self -> curs, prodSimpleCast,
201                 name, & fd, & desc, & sprod -> cid, * out, self -> chain );
202             if ( rc == 0 )
203             {
204                 void *ignore;
205                 rc = VCursorCacheSwap ( self -> cache, & sprod -> cid, * out, & ignore );
206             }
207         }
208     }
209 
210     return rc;
211 }
212 
213 static
VProdResolveProdExpr(const VProdResolve * self,VProduction ** out,const KSymbol * sym)214 rc_t VProdResolveProdExpr ( const VProdResolve *self, VProduction **out, const KSymbol *sym )
215 {
216     const SProduction *sprod = sym -> u . obj;
217     if ( ! sprod -> trigger )
218         return VProdResolveSProduction ( self, out, sprod );
219 
220     PLOGMSG ( klogWarn, ( klogWarn, "trigger production '$(trig)' used in expression"
221                           , "trig=%.*s"
222                           , ( int ) sym -> name . size
223                           , sym -> name . addr ));
224 
225     return 0;
226 }
227 
228 
229 /* ColumnExpr
230  *  the trick about resolving a column reference is in its type
231  */
232 typedef struct SColumnBestFit SColumnBestFit;
233 struct SColumnBestFit
234 {
235     BSTNode n;
236     const SColumn *scol;
237     VTypedecl td;
238     uint32_t distance;
239 };
240 
241 static
VProdResolveBestColumn(const VProdResolve * self,VProduction ** out,const BSTree * ordered,bool alt)242 rc_t VProdResolveBestColumn ( const VProdResolve *self,
243     VProduction **out, const BSTree *ordered, bool alt )
244 {
245     rc_t rc;
246 
247     /* walk all candidtes */
248     const SColumnBestFit *n = ( const SColumnBestFit* ) BSTreeFirst ( ordered );
249     for ( rc = 0; n != NULL; n = ( const SColumnBestFit* ) BSTNodeNext ( & n -> n ) )
250     {
251         /* look for open column */
252         const SColumn *scol = n -> scol;
253 
254         /* resolve the column as appropriate */
255         rc = VProdResolveColumn ( self, out, scol, alt );
256         if ( rc != 0 || * out != NULL )
257             break;
258     }
259 
260     return rc;
261 }
262 
263 static
order_column(const BSTNode * item,const BSTNode * n)264 int64_t CC order_column ( const BSTNode *item, const BSTNode *n )
265 {
266     const SColumnBestFit *a = ( const SColumnBestFit* ) item;
267     const SColumnBestFit *b = ( const SColumnBestFit* ) n;
268     if ( a -> distance != b -> distance )
269         return (int64_t) a -> distance - (int64_t) b -> distance;
270     return VCtxIdCmp ( & a -> scol -> cid, & b -> scol -> cid );
271 }
272 
273 static
VProdResolveColExpr(const VProdResolve * self,VProduction ** out,VFormatdecl * fd,const SSymExpr * x,bool casting)274 rc_t VProdResolveColExpr ( const VProdResolve *self, VProduction **out,
275     VFormatdecl *fd, const SSymExpr *x, bool casting )
276 {
277     rc_t rc;
278     const SNameOverload *sname;
279     const KSymbol *sym = x -> _sym;
280 
281     BSTree ordered;
282     uint32_t i, count;
283     SColumnBestFit buff [ 16 ], * nodes = buff;
284     /* fail if "fd" has a format */
285     if ( fd -> fmt != 0 )
286     {
287         PLOGMSG ( klogWarn, ( klogWarn, "illegal cast of column '$(name)'"
288                    , "name=%.*s"
289                    , ( int ) sym -> name . size
290                    , sym -> name . addr ));
291         return 0;
292     }
293 
294     /* allocate nodes for indexing columns */
295     sname = sym -> u . obj;
296     count = VectorLength ( & sname -> items );
297     if ( count > sizeof buff / sizeof buff [ 0 ] )
298     {
299         nodes = malloc ( sizeof * nodes * count );
300         if ( nodes == NULL )
301             return RC ( rcVDB, rcProduction, rcResolving, rcMemory, rcExhausted );
302     }
303 
304     /* insert columns into ordered tree */
305     BSTreeInit ( & ordered );
306     for ( i = VectorStart ( & sname -> items ), count += i; i < count; ++ i )
307     {
308         /* get SColumn */
309         nodes [ i ] . scol = ( const void* ) VectorGet ( & sname -> items, i );
310 
311         /* perform type cast and measure distance */
312         if ( casting ?
313              VTypedeclCommonAncestor ( & nodes [ i ] . scol -> td, self -> schema,
314                  & fd -> td, & nodes [ i ] . td, & nodes [ i ] . distance ) :
315              VTypedeclToTypedecl ( & nodes [ i ] . scol -> td, self -> schema,
316                  & fd -> td, & nodes [ i ] . td, & nodes [ i ] . distance ) )
317         {
318             BSTreeInsert ( & ordered, & nodes [ i ] . n, order_column );
319         }
320     }
321 
322     /* try to resolve each in order */
323     rc = VProdResolveBestColumn ( self, out, & ordered, x -> alt );
324 
325     if ( nodes != buff )
326         free ( nodes );
327 
328     return rc;
329 }
330 
331 
332 /* PhysExpr
333  */
VProdResolveSPhysMember(const VProdResolve * self,VProduction ** out,const SPhysMember * smbr)334 rc_t VProdResolveSPhysMember ( const VProdResolve *self,
335     VProduction **out, const SPhysMember *smbr )
336 {
337     rc_t rc;
338     VCursor *curs;
339     VPhysical *phys;
340 
341     curs = self -> curs;
342     phys = VCursorCacheGet ( VCursorPhysicalColumns ( curs ), & smbr -> cid );
343     if ( phys != NULL )
344     {
345         /* this guy should be readable, but it is not guaranteed */
346         if ( phys != FAILED_PHYSICAL )
347             * out = phys -> out;
348         return 0;
349     }
350 
351     /* pre-fail */
352     rc = VCursorCacheSet ( VCursorPhysicalColumns ( curs ), & smbr -> cid, FAILED_PHYSICAL );
353     if ( rc == 0 )
354     {
355         /* create physical object */
356         rc = VPhysicalMake ( & phys, curs, smbr );
357         if ( rc == 0 )
358         {
359             /* build physical */
360             rc = VProdResolvePhysical ( self, phys );
361             if ( rc == 0 && phys -> out > FAILED_PRODUCTION && phys -> b2p > FAILED_PRODUCTION )
362             {
363                 /* set success */
364                 void *ignore;
365                 rc = VCursorCacheSwap ( VCursorPhysicalColumns ( curs ), & smbr -> cid, phys, & ignore );
366                 if ( rc == 0 )
367                 {
368                     * out = phys -> out;
369                     return 0;
370                 }
371             }
372             if ( GetRCState ( rc ) == rcUndefined )
373                 rc = 0;
374 
375             VPhysicalWhack ( phys, NULL );
376         }
377     }
378 
379     return rc;
380 }
381 
382 static
VProdResolvePhysExpr(const VProdResolve * self,VProduction ** out,const KSymbol * sym)383 rc_t VProdResolvePhysExpr ( const VProdResolve *self,
384     VProduction **out, const KSymbol *sym )
385 {
386     if ( self -> chain == chainEncoding )
387     {
388         assert ( ! VCursorIsReadOnly ( self -> curs ) );
389         PLOGMSG ( klogWarn, ( klogWarn, "illegal access of physical column '$(name)'"
390                    , "name=%.*s"
391                    , ( int ) sym -> name . size
392                    , sym -> name . addr ));
393         return 0;
394     }
395 
396     return VProdResolveSPhysMember ( self, out, sym -> u . obj );
397 }
398 
399 
400 /* FwdExpr
401  *  handle a forwarded symbol in expression
402  *  if the symbol is "virtual", check for its override
403  *  map actual symbol type to expression handler
404  */
405 static
VProdResolveFwdExpr(const VProdResolve * self,VProduction ** out,VFormatdecl * fd,const SSymExpr * x,bool casting)406 rc_t VProdResolveFwdExpr ( const VProdResolve *self, VProduction **out,
407     VFormatdecl *fd, const SSymExpr *x, bool casting )
408 {
409     /* virtual names that appear in parent table
410        expressions may be overridden in children */
411     const KSymbol *sym = x -> _sym;
412     if ( sym -> type == eVirtual )
413     {
414         /* most derived table/view class */
415         const KSymbol *sym2 = sym;
416         sym = VCursorFindOverride ( self -> curs, ( const VCtxId* ) & sym -> u . fwd );
417         if ( sym == NULL )
418         {
419             PLOGMSG ( klogWarn, ( klogWarn, "virtual reference '$(fwd)' not found in overrides table"
420                        , "fwd=%.*s"
421                        , ( int ) sym2 -> name . size
422                        , sym2 -> name . addr ));
423             return 0;
424         }
425     }
426 
427     /* test symbol type */
428     switch ( sym -> type )
429     {
430     case eProduction:
431         return VProdResolveProdExpr ( self, out, sym );
432     case ePhysMember:
433         return VProdResolvePhysExpr ( self, out, sym );
434     case eColumn:
435         return VProdResolveColExpr ( self, out, fd, x, casting );
436     }
437 
438     VDB_DEBUG (("%s: unresolved forward reference '%S'",
439                  __func__, &sym->name));
440 
441     return 0;
442 }
443 
444 static
VProdResolveMembExpr(const VProdResolve * p_self,VProduction ** p_out,VFormatdecl * p_fd,const SMembExpr * p_x,bool p_casting)445 rc_t VProdResolveMembExpr ( const VProdResolve *    p_self,
446                             VProduction **          p_out,
447                             VFormatdecl *           p_fd,
448                             const SMembExpr *       p_x,
449                             bool                    p_casting )
450 {
451     /* we know we are in a view */
452     const KSymbol * object = VectorGet ( & p_x -> view -> params, p_x -> paramId );
453     assert ( p_self -> view );
454     assert ( object != NULL );
455 
456     /* p_x -> object is a table/view parameter of the view the member-expresison is in.
457         Locate the VTable/VView bound to the corresponding parameter of the view,
458         make a SSymExpr pointing to p_x -> member, call ProdResolveColExpr/VProdResolveProdExpr in the context of the VTable/VView */
459 
460     if ( p_x -> member -> type == eColumn || p_x -> member -> type == eProduction )
461     {
462         VProdResolve pr = * p_self;
463 		const void * boundObj;
464         pr . name = & object -> name;
465         boundObj = VViewGetBoundObject ( pr . view, p_x -> view, p_x -> paramId );
466         if ( boundObj != 0 )
467         {
468             switch ( object -> type )
469             {
470             case eTable:
471                 pr . primary_table = boundObj;
472                 pr . view = NULL;
473                 break;
474             case eView:
475                 /*TODO: should we update pr . primary_table ? */
476                 pr . view = boundObj;
477                 break;
478             default:
479                 return RC ( rcVDB, rcProduction, rcResolving, rcMessage, rcUnsupported );
480             }
481 
482             {
483                 const SExpression * ref;
484                 rc_t rc = SSymExprMake( & ref,
485                                         p_x -> member,
486                                         p_x -> member -> type == eColumn ? eColExpr : eProdExpr );
487                 if ( rc == 0 )
488                 {
489                     VProduction * vmember;
490                     rc = VProdResolveExpr ( & pr, & vmember, NULL, p_fd, ref, p_casting );
491                     SExpressionWhack ( ref );
492                     if ( rc == 0 )
493                     {
494                         if ( p_x -> rowId == NULL )
495                         {   /* simple member: tbl . col */
496                             * p_out = vmember;
497                             return 0;
498                         }
499                         else
500                         {   /* member with a pivot: tbl [ expr ] . col */
501                             VProduction * rowId;
502                             VFormatdecl fd = { { 0, 0 }, 0 };
503                             rc = VProdResolveExpr ( p_self, & rowId, NULL, & fd, p_x -> rowId, p_casting );
504                             if ( rc == 0 )
505                             {
506                                 VPivotProd * ret;
507                                 rc = VPivotProdMake ( & ret, p_self -> owned, vmember, rowId, p_x -> member -> name . addr, p_self -> chain );
508                                 if ( rc == 0 )
509                                 {
510                                     * p_out = & ret -> dad;
511                                     return 0;
512                                 }
513                                 VProductionWhack ( rowId, p_self -> owned );
514                             }
515                             VProductionWhack ( vmember, p_self -> owned );
516                         }
517                     }
518                 }
519                 return rc;
520             }
521         }
522     }
523     return RC ( rcVDB, rcProduction, rcResolving, rcMessage, rcUnsupported );
524 }
525 
526 
527 /* ResolveExpr
528  *  resolves expression and returns resulting production object
529  *
530  *  "out" [ IN/OUT, INITIALLY NULL ] - return parameter for production.
531  *  assmed to be preset to NULL on input.
532  *  invalid on output with non-zero return code
533  *  non-NULL on successful resolution with zero return code
534  *
535  *  "fd" [ IN/OUT ] - resultant type of assignment expression
536  *  on input gives acceptance criteria, i.e. a fmtdecl, typeset or wildcard
537  *  invalid on output with non-zero return code or NULL "out"
538  *  otherwise, gives actual assigned type on output
539  *
540  *  "casting" [ IN ] - true if performing explicit type casting
541  *  see VProdResolveCastExpr for rules
542  *
543  *  "desc" [ OUT, NULL OKAY ] - resultant type description of "out"
544  *  invalid on non-zero return or NULL "out"
545  *  contains description of fd->td on success
546  *
547  *  "cache" [ IN ] - modifiable vector of VProductions by id within
548  *  a given scope: table or script function.
549  *
550  *  "param" [ IN, NULL OKAY ] - modifiable vector of VProduction params
551  *  by id - only valid within script function
552  *
553  *  "expr" [ IN ] - expression to be evaluated
554  *
555  *  returns non-zero if a non-recoverable error occurs, 0 otherwise.
556  *  resolution is only successful on zero return code and non-NULL out.
557  */
558 #if _DEBUGGING
559 #include "schema-dump.h"
560 
561 static size_t xsz;
562 static char xbuffer [ 4096 ];
563 
564 static
VProdResolveCapture(void * data,const void * buffer,size_t size)565 rc_t CC VProdResolveCapture ( void *data, const void *buffer, size_t size )
566 {
567     if ( xsz + size >= sizeof xbuffer )
568     {
569         LOGERR (klogFatal, -1, "( xsz + size >= sizeof xbuffer )");
570         return -1;
571     }
572     memmove ( & xbuffer [ xsz ], buffer, size );
573     xsz += size;
574     return 0;
575 }
576 
577 static
VProdResolvePrintExpr(const VProdResolve * self,const SExpression * expr)578 const char *VProdResolvePrintExpr ( const VProdResolve *self, const SExpression *expr )
579 {
580     SDumper dumper;
581     SDumperInit ( & dumper, self -> schema, sdmPrint, VProdResolveCapture, NULL );
582 
583     xsz = 0;
584     SDumperPrint ( & dumper, "%E", expr );
585     SDumperWhack ( & dumper );
586 
587     xbuffer [ xsz ] = 0;
588     return xbuffer;
589 }
590 
591 static int indent_level = 0;
592 #endif
593 
VProdResolveExpr(const VProdResolve * self,VProduction ** out,VTypedesc * desc,VFormatdecl * fd,const SExpression * expr,bool casting)594 rc_t VProdResolveExpr ( const VProdResolve *self,
595     VProduction **out, VTypedesc *desc, VFormatdecl *fd,
596     const SExpression *expr, bool casting )
597 {
598     rc_t rc;
599     VProduction *prod;
600 
601     if ( expr == NULL )
602     {
603         /* report NULL expression, but don't die */
604         PLOGMSG ( klogWarn, ( klogWarn, "NULL expression in '$(tbl)' table schema"
605                    , "tbl=%.*s"
606                    , ( int ) self -> name -> size
607                    , self -> name -> addr ));
608         return 0;
609     }
610 
611     prod = NULL;
612     *out = NULL;
613 
614 #if _DEBUGGING
615     ++ indent_level;
616     VDB_DEBUG (( "%*cresolving expression '%s'\n", indent_level, ' ',
617                  VProdResolvePrintExpr ( self, expr ) ));
618 #endif
619 
620     switch ( expr -> var )
621     {
622     case eParamExpr:
623         /* a script function is making reference to a parameter */
624         rc = VProdResolveParamExpr ( self, & prod, ( ( const SSymExpr* ) expr ) -> _sym );
625     assert (rc != -1);
626         break;
627 
628     case eProdExpr:
629         /* return a simple production */
630         rc = VProdResolveProdExpr ( self, & prod, ( ( const SSymExpr* ) expr ) -> _sym );
631     assert (rc != -1);
632         break;
633 
634     case eFwdExpr:
635         /* handle an implicit or overridden reference */
636         rc = VProdResolveFwdExpr ( self, & prod, fd, ( const SSymExpr* ) expr, casting );
637     assert (rc != -1);
638         break;
639 
640     case eColExpr:
641         /* return a column production */
642         rc = VProdResolveColExpr ( self, & prod, fd, ( const SSymExpr* ) expr, casting );
643     assert (rc != -1);
644         break;
645 
646     case ePhysExpr:
647         /* return a physical production */
648         rc = VProdResolvePhysExpr ( self, & prod, ( ( const SSymExpr* ) expr ) -> _sym );
649     assert (rc != -1);
650         break;
651 
652     case eScriptExpr:
653         /* create a script function */
654         rc = VProdResolveScriptExpr ( self, & prod, fd, ( const SFuncExpr* ) expr );
655     assert (rc != -1);
656         break;
657 
658     case eFuncExpr:
659         /* create an external function */
660         rc = VProdResolveFuncExpr ( self, & prod, fd, ( const SFuncExpr* ) expr );
661     assert (rc != -1);
662         break;
663 
664     case eCastExpr:
665         /* perform an explicit cast */
666         rc = VProdResolveCastExpr ( self, & prod, ( const SBinExpr* ) expr );
667     assert (rc != -1);
668         break;
669 
670     case eCondExpr:
671         /* run left and right expressions in order until exit condition */
672         rc = VProdResolveExpr ( self, out, desc, fd, ( ( const SBinExpr* ) expr ) -> left, casting );
673     assert (rc != -1);
674         if ( ( rc == 0 && * out == NULL ) || self -> discover_writable_columns )
675         {
676             rc = VProdResolveExpr ( self, out, desc, fd, ( ( const SBinExpr* ) expr ) -> right, casting );
677     assert (rc != -1);
678         }
679 #if _DEBUGGING
680         -- indent_level;
681 #endif
682         return rc;
683 
684     case eMembExpr:
685         /* return a column or a production */
686         rc = VProdResolveMembExpr ( self, & prod, fd, ( const SMembExpr* ) expr, casting );
687     assert (rc != -1);
688         break;
689 
690     default:
691         /* report bad expression, but don't die */
692         PLOGMSG ( klogWarn, ( klogWarn, "unrecognized expression in '$(tbl)' table schema"
693                    , "tbl=%.*s"
694                    , ( int ) self -> name -> size
695                    , self -> name -> addr ));
696 #if _DEBUGGING
697         -- indent_level;
698 #endif
699         return 0;
700     }
701 
702     /* guard against returns of NULL or FAILED_PRODUCTION */
703     if ( rc == 0 && prod > FAILED_PRODUCTION )
704     {
705         VDB_DEBUG ( ("%*cresolved expression  '%s'\n", indent_level, ' ', VProdResolvePrintExpr ( self, expr ) ) );
706 
707         /* returned production must be on requested chain */
708         if ( ( prod -> chain & self -> chain ) == 0 )
709         {
710             rc = RC ( rcVDB, rcProduction, rcResolving, rcSchema, rcInconsistent );
711             VDB_DEBUG ( ( "%*cPRODUCTION RESOLVED ON WRONG FORK: %R\n", indent_level, ' ', rc ) );
712         }
713         else
714         {
715             /* fix uncommitted production chain */
716             if ( prod -> chain == chainUncommitted )
717                 prod -> chain = self -> chain;
718 
719             /* test for type compatibility - modifies "fd" */
720             if ( casting ?
721                  VFormatdeclCommonAncestor ( & prod -> fd, self -> schema, fd, fd, NULL ) :
722                  VFormatdeclToFormatdecl ( & prod -> fd, self -> schema, fd, fd, NULL ) )
723             {
724                 /* if no type description is requested, we're done */
725                 if ( desc == NULL )
726                     * out = prod;
727                 else
728                 {
729                     /* otherwise, create a type description */
730                     rc = VSchemaDescribeTypedecl ( self -> schema, desc, & fd -> td );
731     assert (rc != -1);
732                     if ( rc != 0 )
733                         VDB_DEBUG ( ( "%*cREQUESTED TYPE CANNOT BE DESCRIBED: %R\n", indent_level, ' ', rc ) );
734                     else
735                         * out = prod;
736                 }
737             }
738             else
739             {
740 #if _DEBUGGING
741                 char from [ 128 ] = "", to [ 128 ] = "";
742                 VTypedeclToText ( & prod -> fd . td, self -> schema, from, sizeof from );
743                 VTypedeclToText ( & fd -> td, self -> schema, to, sizeof to );
744                 VDB_DEBUG ( ( "%*cexpression '%s' cannot be %s from '%s' to '%s'\n"
745                               , indent_level, ' '
746                               , VProdResolvePrintExpr ( self, expr )
747                               , casting ? "cast" : "typed"
748                               , from
749                               , to
750                              )
751                     );
752 #endif
753             }
754         }
755     }
756     else if ( rc != 0 )
757     {
758         VDB_DEBUG ( ( "failed to resolve expression '%s' prod %p %R\n", VProdResolvePrintExpr ( self, expr ), prod, rc ) );
759     }
760     else if ( prod == NULL )
761     {
762         VDB_DEBUG ( ( "expression '%s' was not resolved\n", VProdResolvePrintExpr ( self, expr ) ) );
763     }
764     else
765     {
766         VDB_DEBUG ( ( "expression '%s' returned FAILED_PRODUCTION\n", VProdResolvePrintExpr ( self, expr ) ) );
767     }
768 
769 #if _DEBUGGING
770     -- indent_level;
771 #endif
772 
773     return rc;
774 }
775 
776 
777 /* ResolveColumn
778  *  resolves a column from read/validate expression
779  */
VProdResolveColumnRead(const VProdResolve * self,VProduction ** out,const SColumn * scol)780 rc_t VProdResolveColumnRead ( const VProdResolve *self,
781     VProduction **out, const SColumn *scol )
782 {
783     rc_t rc;
784     VFormatdecl fd;
785     const char *name;
786     VCursor *curs;
787     VColumn *vcol;
788 
789     VDB_DEBUG ( ( "resolving column '%N' read expression.\n", scol -> name ) );
790 
791     /* potential error if self is NULL */
792     curs = self -> curs;
793     if ( out == NULL )
794     {
795         rc =  RC(rcVDB, rcProduction, rcResolving, rcParam, rcNull);
796         VDB_DEBUG ( ( "result NULL for column '%N'; no output can be produced by '%s' rc %R\n",
797                       scol -> name, __func__, rc ) );
798         return rc;
799     }
800 
801     /* fetch the column */
802     vcol = VCursorCacheGet ( VCursorColumns ( curs ), & scol -> cid );
803     if ( vcol == NULL )
804     {
805         VDB_DEBUG ( ( "failed to fetch NULL for column '%N'; no output was produced by '%s'\n",
806                       scol -> name, __func__ ) );
807         return 0;
808     }
809 
810     /* if the read production is in place, return it */
811     if ( vcol -> in != NULL )
812     {
813         if ( vcol -> in != FAILED_PRODUCTION )
814             * out = vcol -> in;
815         return 0;
816     }
817 
818     /* pre-fail */
819     vcol -> in = FAILED_PRODUCTION;
820 
821     /* production resolution works with fmtdecl */
822     fd . td = scol -> td;
823     fd . fmt = 0;
824 
825     /* resolve the expression */
826     rc = VProdResolveExpr ( self, out, & vcol -> desc, & fd, scol -> read, false );
827     assert (rc != -1);
828 
829     if ( rc != 0 || *out == NULL )
830         return rc;
831 
832     /* repair incomplete column declarations */
833     if ( scol -> td . type_id == 0 )
834     {
835         if ( fd . td . type_id == 0 )
836         {
837             rc = RC ( rcVDB, rcColumn, rcResolving, rcType, rcUndefined );
838             VDB_DEBUG (("failed to repair incomplete declaration for column '%N' rc %R\n",
839                         scol -> name, rc));
840             return rc;
841         }
842         ( ( SColumn* ) scol ) -> td = fd . td;
843     }
844 
845     /* create a simple prod to manage fd and desc */
846     name = scol -> name -> name . addr;
847     assert ( name [ scol -> name -> name . size ] == 0 );
848     rc = VSimpleProdMake ( out, self -> owned, self -> curs, prodSimpleCast,
849         name, & fd, & vcol -> desc, NULL, * out, self -> chain );
850 
851     assert (rc != -1);
852     if ( rc != 0 )
853     {
854         VDB_DEBUG (("failed to create a simple prod to manage fd and desc for column '%N', rc %R\n",
855                     scol -> name, rc));
856         return rc;
857     }
858 
859     /* return column input - this is input to the column
860        that is intended to be read from the cursor */
861     vcol -> in = * out;
862     return rc;
863 }
864 
865 /* ResolvePhysical
866  *  resolves a physical column
867  */
VProdResolvePhysicalRead(const VProdResolve * self,VPhysical * phys)868 rc_t VProdResolvePhysicalRead ( const VProdResolve *self, VPhysical *phys )
869 {
870     rc_t rc;
871     VTypedesc desc;
872     VFormatdecl fd;
873     VProduction *prod;
874     VFunctionProd *bs;
875     VCursor *curs = self -> curs;
876 
877     const char *name;
878     const SExpression *enc;
879     const SPhysMember *smbr;
880 
881     /* a write cursor would have opened this already */
882     if ( VCursorIsReadOnly ( curs ) )
883     {
884         /* open the physical column for read */
885         rc = VPhysicalOpenRead ( phys, ( VSchema* ) self -> schema, self -> primary_table );
886         if ( rc != 0 )
887         {
888             /* trying to open a column that doesn't exist
889                is not an error, but it didn't resolve */
890             if ( GetRCState ( rc ) == rcNotFound )
891                 return 0;
892 
893             return rc;
894         }
895     }
896 
897     /* should be completely resolved */
898     smbr = phys -> smbr;
899     if ( smbr -> td . type_id == 0 )
900         return 0;
901 
902     /* production name */
903     name = smbr -> name -> name . addr;
904 
905     /* type information */
906     fd . td = smbr -> td;
907     fd . fmt = 0;
908     rc = VSchemaDescribeTypedecl ( self -> schema, & desc, & fd . td );
909     if ( rc != 0 )
910         return rc;
911 
912     /* create output adapter */
913     rc = VPhysicalProdMake ( & prod, self -> owned,
914         curs, phys, prodPhysicalOut, name, & fd, & desc );
915     if ( rc != 0 )
916         return rc;
917 
918     /* create byte-swap function with transparent type information */
919     rc = VFunctionProdMake ( & bs, self -> owned,
920         curs, prodFuncByteswap, name, & fd, & desc, chainDecoding );
921     if ( rc != 0 )
922         return rc;
923 
924     /* set adapter as input to byte swap */
925     rc = VectorAppend ( & bs -> parms, NULL, prod );
926     if ( rc != 0 )
927         return rc;
928 
929     /* take byte-swap function as output */
930     phys -> out = & bs -> dad;
931 
932     /* NB - we now have byte-order native output via an adapter
933        it remains to create a decoding path for physical blobs */
934 
935 
936     /* create adapter */
937     rc = VPhysicalProdMake ( & prod, self -> owned,
938         curs, phys, prodPhysicalKCol, name, & fd, & desc );
939     if ( rc != 0 )
940         return rc;
941 
942     /* create serial-to-blob stage */
943     rc = VSimpleProdMake ( & prod, self -> owned, self->curs,
944         prodSimpleSerial2Blob, name, & fd, & desc, NULL, prod, chainDecoding );
945     if ( rc != 0 )
946         return rc;
947 
948     /* determine physical encoding */
949     enc = phys -> enc;
950     if ( enc == NULL )
951         enc = smbr -> type;
952 
953     /* if unencoded */
954     if ( enc == NULL )
955         phys -> b2p = prod;
956     else
957     {
958         /* the adapter type should be undefined */
959         memset ( & prod -> fd, 0, sizeof prod -> fd );
960         prod -> desc . intrinsic_bits = prod -> desc . intrinsic_dim = 1;
961         prod -> desc . domain = 0;
962 
963         /* create decoding production */
964         rc = VProdResolveEncodingExpr ( self, & phys -> b2p,
965             prod, ( const SPhysEncExpr* ) enc );
966     }
967 
968     return rc;
969 }
970