@@ -4020,6 +4020,12 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
4020
4020
long tcount ;
4021
4021
int rc ;
4022
4022
PLpgSQL_expr * expr = stmt -> sqlstmt ;
4023
+ int too_many_rows_level = 0 ;
4024
+
4025
+ if (plpgsql_extra_errors & PLPGSQL_XCHECK_TOOMANYROWS )
4026
+ too_many_rows_level = ERROR ;
4027
+ else if (plpgsql_extra_warnings & PLPGSQL_XCHECK_TOOMANYROWS )
4028
+ too_many_rows_level = WARNING ;
4023
4029
4024
4030
/*
4025
4031
* On the first call for this statement generate the plan, and detect
@@ -4059,9 +4065,10 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
4059
4065
4060
4066
/*
4061
4067
* If we have INTO, then we only need one row back ... but if we have INTO
4062
- * STRICT, ask for two rows, so that we can verify the statement returns
4063
- * only one. INSERT/UPDATE/DELETE are always treated strictly. Without
4064
- * INTO, just run the statement to completion (tcount = 0).
4068
+ * STRICT or extra check too_many_rows, ask for two rows, so that we can
4069
+ * verify the statement returns only one. INSERT/UPDATE/DELETE are always
4070
+ * treated strictly. Without INTO, just run the statement to completion
4071
+ * (tcount = 0).
4065
4072
*
4066
4073
* We could just ask for two rows always when using INTO, but there are
4067
4074
* some cases where demanding the extra row costs significant time, eg by
@@ -4070,7 +4077,7 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
4070
4077
*/
4071
4078
if (stmt -> into )
4072
4079
{
4073
- if (stmt -> strict || stmt -> mod_stmt )
4080
+ if (stmt -> strict || stmt -> mod_stmt || too_many_rows_level )
4074
4081
tcount = 2 ;
4075
4082
else
4076
4083
tcount = 1 ;
@@ -4187,19 +4194,23 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
4187
4194
}
4188
4195
else
4189
4196
{
4190
- if (n > 1 && (stmt -> strict || stmt -> mod_stmt ))
4197
+ if (n > 1 && (stmt -> strict || stmt -> mod_stmt || too_many_rows_level ))
4191
4198
{
4192
4199
char * errdetail ;
4200
+ int errlevel ;
4193
4201
4194
4202
if (estate -> func -> print_strict_params )
4195
4203
errdetail = format_expr_params (estate , expr );
4196
4204
else
4197
4205
errdetail = NULL ;
4198
4206
4199
- ereport (ERROR ,
4207
+ errlevel = (stmt -> strict || stmt -> mod_stmt ) ? ERROR : too_many_rows_level ;
4208
+
4209
+ ereport (errlevel ,
4200
4210
(errcode (ERRCODE_TOO_MANY_ROWS ),
4201
4211
errmsg ("query returned more than one row" ),
4202
- errdetail ? errdetail_internal ("parameters: %s" , errdetail ) : 0 ));
4212
+ errdetail ? errdetail_internal ("parameters: %s" , errdetail ) : 0 ,
4213
+ errhint ("Make sure the query returns a single row, or use LIMIT 1" )));
4203
4214
}
4204
4215
/* Put the first result row into the target */
4205
4216
exec_move_row (estate , target , tuptab -> vals [0 ], tuptab -> tupdesc );
@@ -6835,6 +6846,19 @@ exec_move_row_from_fields(PLpgSQL_execstate *estate,
6835
6846
int td_natts = tupdesc ? tupdesc -> natts : 0 ;
6836
6847
int fnum ;
6837
6848
int anum ;
6849
+ int strict_multiassignment_level = 0 ;
6850
+
6851
+ /*
6852
+ * The extra check strict strict_multi_assignment can be active,
6853
+ * only when input tupdesc is specified.
6854
+ */
6855
+ if (tupdesc != NULL )
6856
+ {
6857
+ if (plpgsql_extra_errors & PLPGSQL_XCHECK_STRICTMULTIASSIGNMENT )
6858
+ strict_multiassignment_level = ERROR ;
6859
+ else if (plpgsql_extra_warnings & PLPGSQL_XCHECK_STRICTMULTIASSIGNMENT )
6860
+ strict_multiassignment_level = WARNING ;
6861
+ }
6838
6862
6839
6863
/* Handle RECORD-target case */
6840
6864
if (target -> dtype == PLPGSQL_DTYPE_REC )
@@ -6913,10 +6937,23 @@ exec_move_row_from_fields(PLpgSQL_execstate *estate,
6913
6937
}
6914
6938
else
6915
6939
{
6940
+ /* no source for destination column */
6916
6941
value = (Datum ) 0 ;
6917
6942
isnull = true;
6918
6943
valtype = UNKNOWNOID ;
6919
6944
valtypmod = -1 ;
6945
+
6946
+ /* When source value is missing */
6947
+ if (strict_multiassignment_level )
6948
+ ereport (strict_multiassignment_level ,
6949
+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
6950
+ errmsg ("number of source and target fields in assignment do not match" ),
6951
+ /* translator: %s represents a name of an extra check */
6952
+ errdetail ("%s check of %s is active." ,
6953
+ "strict_multi_assignment" ,
6954
+ strict_multiassignment_level == ERROR ? "extra_errors" :
6955
+ "extra_warnings" ),
6956
+ errhint ("Make sure the query returns the exact list of columns." )));
6920
6957
}
6921
6958
6922
6959
/* Cast the new value to the right type, if needed. */
@@ -6930,6 +6967,29 @@ exec_move_row_from_fields(PLpgSQL_execstate *estate,
6930
6967
newnulls [fnum ] = isnull ;
6931
6968
}
6932
6969
6970
+ /*
6971
+ * When strict_multiassignment extra check is active, then ensure
6972
+ * there are no unassigned source attributes.
6973
+ */
6974
+ if (strict_multiassignment_level && anum < td_natts )
6975
+ {
6976
+ /* skip dropped columns in the source descriptor */
6977
+ while (anum < td_natts &&
6978
+ TupleDescAttr (tupdesc , anum )-> attisdropped )
6979
+ anum ++ ;
6980
+
6981
+ if (anum < td_natts )
6982
+ ereport (strict_multiassignment_level ,
6983
+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
6984
+ errmsg ("number of source and target fields in assignment do not match" ),
6985
+ /* translator: %s represents a name of an extra check */
6986
+ errdetail ("%s check of %s is active." ,
6987
+ "strict_multi_assignment" ,
6988
+ strict_multiassignment_level == ERROR ? "extra_errors" :
6989
+ "extra_warnings" ),
6990
+ errhint ("Make sure the query returns the exact list of columns." )));
6991
+ }
6992
+
6933
6993
values = newvalues ;
6934
6994
nulls = newnulls ;
6935
6995
}
@@ -6986,16 +7046,50 @@ exec_move_row_from_fields(PLpgSQL_execstate *estate,
6986
7046
}
6987
7047
else
6988
7048
{
7049
+ /* no source for destination column */
6989
7050
value = (Datum ) 0 ;
6990
7051
isnull = true;
6991
7052
valtype = UNKNOWNOID ;
6992
7053
valtypmod = -1 ;
7054
+
7055
+ if (strict_multiassignment_level )
7056
+ ereport (strict_multiassignment_level ,
7057
+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
7058
+ errmsg ("number of source and target fields in assignment do not match" ),
7059
+ /* translator: %s represents a name of an extra check */
7060
+ errdetail ("%s check of %s is active." ,
7061
+ "strict_multi_assignment" ,
7062
+ strict_multiassignment_level == ERROR ? "extra_errors" :
7063
+ "extra_warnings" ),
7064
+ errhint ("Make sure the query returns the exact list of columns." )));
6993
7065
}
6994
7066
6995
7067
exec_assign_value (estate , (PLpgSQL_datum * ) var ,
6996
7068
value , isnull , valtype , valtypmod );
6997
7069
}
6998
7070
7071
+ /*
7072
+ * When strict_multiassignment extra check is active, ensure there
7073
+ * are no unassigned source attributes.
7074
+ */
7075
+ if (strict_multiassignment_level && anum < td_natts )
7076
+ {
7077
+ while (anum < td_natts &&
7078
+ TupleDescAttr (tupdesc , anum )-> attisdropped )
7079
+ anum ++ ; /* skip dropped column in tuple */
7080
+
7081
+ if (anum < td_natts )
7082
+ ereport (strict_multiassignment_level ,
7083
+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
7084
+ errmsg ("number of source and target fields in assignment do not match" ),
7085
+ /* translator: %s represents a name of an extra check */
7086
+ errdetail ("%s check of %s is active." ,
7087
+ "strict_multi_assignment" ,
7088
+ strict_multiassignment_level == ERROR ? "extra_errors" :
7089
+ "extra_warnings" ),
7090
+ errhint ("Make sure the query returns the exact list of columns." )));
7091
+ }
7092
+
6999
7093
return ;
7000
7094
}
7001
7095
0 commit comments