|
8 | 8 | *
|
9 | 9 | *
|
10 | 10 | * IDENTIFICATION
|
11 |
| - * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.261 2010/01/11 15:31:04 tgl Exp $ |
| 11 | + * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.262 2010/02/18 18:41:47 tgl Exp $ |
12 | 12 | *
|
13 | 13 | *-------------------------------------------------------------------------
|
14 | 14 | */
|
|
61 | 61 | staticDatumExecEvalArrayRef(ArrayRefExprState*astate,
|
62 | 62 | ExprContext*econtext,
|
63 | 63 | bool*isNull,ExprDoneCond*isDone);
|
| 64 | +staticboolisAssignmentIndirectionExpr(ExprState*exprstate); |
64 | 65 | staticDatumExecEvalAggref(AggrefExprState*aggref,
|
65 | 66 | ExprContext*econtext,
|
66 | 67 | bool*isNull,ExprDoneCond*isDone);
|
@@ -349,22 +350,74 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
|
349 | 350 | if (isAssignment)
|
350 | 351 | {
|
351 | 352 | DatumsourceData;
|
| 353 | +Datumsave_datum; |
| 354 | +boolsave_isNull; |
352 | 355 |
|
353 | 356 | /*
|
354 |
| - * Evaluate the value to be assigned into the array. |
| 357 | + * We might have a nested-assignment situation, in which the |
| 358 | + * refassgnexpr is itself a FieldStore or ArrayRef that needs to |
| 359 | + * obtain and modify the previous value of the array element or slice |
| 360 | + * being replaced. If so, we have to extract that value from the |
| 361 | + * array and pass it down via the econtext's caseValue. It's safe to |
| 362 | + * reuse the CASE mechanism because there cannot be a CASE between |
| 363 | + * here and where the value would be needed, and an array assignment |
| 364 | + * can't be within a CASE either. (So saving and restoring the |
| 365 | + * caseValue is just paranoia, but let's do it anyway.) |
355 | 366 | *
|
356 |
| - * XXX At some point we'll need to look into making the old value of |
357 |
| - * the array element available via CaseTestExpr, as is done by |
358 |
| - * ExecEvalFieldStore.This is not needed now but will be needed to |
359 |
| - * support arrays of composite types; in an assignment to a field of |
360 |
| - * an array member, the parser would generate a FieldStore that |
361 |
| - * expects to fetch its input tuple via CaseTestExpr. |
| 367 | + * Since fetching the old element might be a nontrivial expense, do it |
| 368 | + * only if the argument appears to actually need it. |
| 369 | + */ |
| 370 | +save_datum=econtext->caseValue_datum; |
| 371 | +save_isNull=econtext->caseValue_isNull; |
| 372 | + |
| 373 | +if (isAssignmentIndirectionExpr(astate->refassgnexpr)) |
| 374 | +{ |
| 375 | +if (*isNull) |
| 376 | +{ |
| 377 | +/* whole array is null, so any element or slice is too */ |
| 378 | +econtext->caseValue_datum= (Datum)0; |
| 379 | +econtext->caseValue_isNull= true; |
| 380 | +} |
| 381 | +elseif (lIndex==NULL) |
| 382 | +{ |
| 383 | +econtext->caseValue_datum=array_ref(array_source,i, |
| 384 | +upper.indx, |
| 385 | +astate->refattrlength, |
| 386 | +astate->refelemlength, |
| 387 | +astate->refelembyval, |
| 388 | +astate->refelemalign, |
| 389 | +&econtext->caseValue_isNull); |
| 390 | +} |
| 391 | +else |
| 392 | +{ |
| 393 | +resultArray=array_get_slice(array_source,i, |
| 394 | +upper.indx,lower.indx, |
| 395 | +astate->refattrlength, |
| 396 | +astate->refelemlength, |
| 397 | +astate->refelembyval, |
| 398 | +astate->refelemalign); |
| 399 | +econtext->caseValue_datum=PointerGetDatum(resultArray); |
| 400 | +econtext->caseValue_isNull= false; |
| 401 | +} |
| 402 | +} |
| 403 | +else |
| 404 | +{ |
| 405 | +/* argument shouldn't need caseValue, but for safety set it null */ |
| 406 | +econtext->caseValue_datum= (Datum)0; |
| 407 | +econtext->caseValue_isNull= true; |
| 408 | +} |
| 409 | + |
| 410 | +/* |
| 411 | + * Evaluate the value to be assigned into the array. |
362 | 412 | */
|
363 | 413 | sourceData=ExecEvalExpr(astate->refassgnexpr,
|
364 | 414 | econtext,
|
365 | 415 | &eisnull,
|
366 | 416 | NULL);
|
367 | 417 |
|
| 418 | +econtext->caseValue_datum=save_datum; |
| 419 | +econtext->caseValue_isNull=save_isNull; |
| 420 | + |
368 | 421 | /*
|
369 | 422 | * For an assignment to a fixed-length array type, both the original
|
370 | 423 | * array and the value to be assigned into it must be non-NULL, else
|
@@ -426,6 +479,34 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
|
426 | 479 | }
|
427 | 480 | }
|
428 | 481 |
|
| 482 | +/* |
| 483 | + * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef |
| 484 | + * that might need the old element value passed down? |
| 485 | + * |
| 486 | + * (We could use this in ExecEvalFieldStore too, but in that case passing |
| 487 | + * the old value is so cheap there's no need.) |
| 488 | + */ |
| 489 | +staticbool |
| 490 | +isAssignmentIndirectionExpr(ExprState*exprstate) |
| 491 | +{ |
| 492 | +if (exprstate==NULL) |
| 493 | +return false;/* just paranoia */ |
| 494 | +if (IsA(exprstate,FieldStoreState)) |
| 495 | +{ |
| 496 | +FieldStore*fstore= (FieldStore*)exprstate->expr; |
| 497 | + |
| 498 | +if (fstore->arg&&IsA(fstore->arg,CaseTestExpr)) |
| 499 | +return true; |
| 500 | +} |
| 501 | +elseif (IsA(exprstate,ArrayRefExprState)) |
| 502 | +{ |
| 503 | +ArrayRef*arrayRef= (ArrayRef*)exprstate->expr; |
| 504 | + |
| 505 | +if (arrayRef->refexpr&&IsA(arrayRef->refexpr,CaseTestExpr)) |
| 506 | +return true; |
| 507 | +} |
| 508 | +return false; |
| 509 | +} |
429 | 510 |
|
430 | 511 | /* ----------------------------------------------------------------
|
431 | 512 | *ExecEvalAggref
|
@@ -3902,10 +3983,12 @@ ExecEvalFieldStore(FieldStoreState *fstate,
|
3902 | 3983 |
|
3903 | 3984 | /*
|
3904 | 3985 | * Use the CaseTestExpr mechanism to pass down the old value of the
|
3905 |
| - * field being replaced; this is useful in case we have a nested field |
3906 |
| - * update situation. It's safe to reuse the CASE mechanism because |
3907 |
| - * there cannot be a CASE between here and where the value would be |
3908 |
| - * needed. |
| 3986 | + * field being replaced; this is needed in case the newval is itself a |
| 3987 | + * FieldStore or ArrayRef that has to obtain and modify the old value. |
| 3988 | + * It's safe to reuse the CASE mechanism because there cannot be a |
| 3989 | + * CASE between here and where the value would be needed, and a field |
| 3990 | + * assignment can't be within a CASE either. (So saving and restoring |
| 3991 | + * the caseValue is just paranoia, but let's do it anyway.) |
3909 | 3992 | */
|
3910 | 3993 | econtext->caseValue_datum=values[fieldnum-1];
|
3911 | 3994 | econtext->caseValue_isNull=isnull[fieldnum-1];
|
|