@@ -1262,6 +1262,112 @@ function validate_binary_operations(output, expressions, operator) {
12621262 await table . delete ( ) ;
12631263 } ) ;
12641264
1265+ test ( "coalesce returns first non-null arg" , async function ( ) {
1266+ const table = await perspective . table ( {
1267+ a : "integer" ,
1268+ b : "integer" ,
1269+ } ) ;
1270+
1271+ const view = await table . view ( {
1272+ expressions : {
1273+ coalesce_ab : 'coalesce("a", "b")' ,
1274+ coalesce_ab_default : 'coalesce("a", "b", 99)' ,
1275+ } ,
1276+ } ) ;
1277+
1278+ await table . update ( {
1279+ a : [ 1 , null , null , 4 ] ,
1280+ b : [ 10 , 20 , null , 40 ] ,
1281+ } ) ;
1282+
1283+ const result = await view . to_columns ( ) ;
1284+ expect ( result [ "coalesce_ab" ] ) . toEqual ( [ 1 , 20 , null , 4 ] ) ;
1285+ expect ( result [ "coalesce_ab_default" ] ) . toEqual ( [ 1 , 20 , 99 , 4 ] ) ;
1286+ await view . delete ( ) ;
1287+ await table . delete ( ) ;
1288+ } ) ;
1289+
1290+ test ( "coalesce promotes mixed numeric inputs to float" , async function ( ) {
1291+ const table = await perspective . table ( {
1292+ i : "integer" ,
1293+ f : "float" ,
1294+ } ) ;
1295+
1296+ const view = await table . view ( {
1297+ expressions : {
1298+ coalesce_if : 'coalesce("i", "f")' ,
1299+ coalesce_if_default : 'coalesce("i", "f", 0.5)' ,
1300+ } ,
1301+ } ) ;
1302+
1303+ await table . update ( {
1304+ i : [ 1 , null , null , 4 ] ,
1305+ f : [ null , 2.5 , null , 4.5 ] ,
1306+ } ) ;
1307+
1308+ const result = await view . to_columns ( ) ;
1309+ const schema = await view . expression_schema ( ) ;
1310+ expect ( schema [ "coalesce_if" ] ) . toEqual ( "float" ) ;
1311+ expect ( schema [ "coalesce_if_default" ] ) . toEqual ( "float" ) ;
1312+ expect ( result [ "coalesce_if" ] ) . toEqual ( [ 1 , 2.5 , null , 4 ] ) ;
1313+ expect ( result [ "coalesce_if_default" ] ) . toEqual ( [ 1 , 2.5 , 0.5 , 4 ] ) ;
1314+
1315+ await view . delete ( ) ;
1316+ await table . delete ( ) ;
1317+ } ) ;
1318+
1319+ test ( "coalesce with all-null inputs returns null" , async function ( ) {
1320+ const table = await perspective . table ( {
1321+ a : "integer" ,
1322+ b : "integer" ,
1323+ } ) ;
1324+
1325+ const view = await table . view ( {
1326+ expressions : { coalesce_nulls : 'coalesce("a", "b")' } ,
1327+ } ) ;
1328+
1329+ await table . update ( {
1330+ a : [ null , null , null ] ,
1331+ b : [ null , null , null ] ,
1332+ } ) ;
1333+
1334+ const result = await view . to_columns ( ) ;
1335+ expect ( result [ "coalesce_nulls" ] ) . toEqual ( [ null , null , null ] ) ;
1336+ await view . delete ( ) ;
1337+ await table . delete ( ) ;
1338+ } ) ;
1339+
1340+ test ( "coalesce fails validation for incompatible types" , async function ( ) {
1341+ const table = await perspective . table ( {
1342+ a : "integer" ,
1343+ b : "string" ,
1344+ } ) ;
1345+
1346+ const validated = await table . validate_expressions ( [
1347+ 'coalesce("a", "b")' ,
1348+ "coalesce(\"a\", 'fallback')" ,
1349+ ] ) ;
1350+
1351+ expect ( validated . expression_schema ) . toEqual ( { } ) ;
1352+ expect ( validated . errors [ 'coalesce("a", "b")' ] ) . toEqual ( {
1353+ column : 0 ,
1354+ line : 0 ,
1355+ error_message :
1356+ "Type Error - inputs do not resolve to a valid expression." ,
1357+ } ) ;
1358+
1359+ expect ( validated . errors [ "coalesce(\"a\", 'fallback')" ] ) . toEqual (
1360+ {
1361+ column : 0 ,
1362+ line : 0 ,
1363+ error_message :
1364+ "Type Error - inputs do not resolve to a valid expression." ,
1365+ } ,
1366+ ) ;
1367+
1368+ await table . delete ( ) ;
1369+ } ) ;
1370+
12651371 test ( "null" , async function ( ) {
12661372 const table = await perspective . table ( {
12671373 a : "integer" ,
0 commit comments