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