@@ -1238,6 +1238,234 @@ describe('BigQuery', () => {
12381238
12391239 describe ( 'SQL parameters' , ( ) => {
12401240 describe ( 'positional' , ( ) => {
1241+ describe . only ( 'High Precision Query System Tests' , ( ) => {
1242+ let bigquery : BigQuery ;
1243+ const expectedTsValueNanoseconds = '2023-01-01T12:00:00.123456000Z' ;
1244+ const expectedTsValuePicoseconds =
1245+ '2023-01-01T12:00:00.123456789123Z' ;
1246+ const expectedErrorMessage =
1247+ 'Cannot specify both timestamp_as_int and timestamp_output_format.' ;
1248+
1249+ before ( ( ) => {
1250+ bigquery = new BigQuery ( ) ;
1251+ } ) ;
1252+
1253+ const testCases = [
1254+ {
1255+ name : 'TOF: TIMESTAMP_OUTPUT_FORMAT_UNSPECIFIED, UI64: true' ,
1256+ timestampOutputFormat : 'TIMESTAMP_OUTPUT_FORMAT_UNSPECIFIED' ,
1257+ useInt64Timestamp : true ,
1258+ expectedTsValue : expectedTsValueNanoseconds ,
1259+ } ,
1260+ {
1261+ name : 'TOF: TIMESTAMP_OUTPUT_FORMAT_UNSPECIFIED, UI64: false (default ISO8601_STRING)' ,
1262+ timestampOutputFormat : 'TIMESTAMP_OUTPUT_FORMAT_UNSPECIFIED' ,
1263+ useInt64Timestamp : false ,
1264+ expectedTsValue : expectedTsValueNanoseconds ,
1265+ } ,
1266+ /*
1267+ { // This is not a meaningful use case.
1268+ name: 'TOF: FLOAT64, UI64: true (error)',
1269+ timestampOutputFormat: 'FLOAT64',
1270+ useInt64Timestamp: true,
1271+ expectedTsValue: undefined,
1272+ expectedError: expectedErrorMessage,
1273+ },
1274+ */
1275+ {
1276+ name : 'TOF: FLOAT64, UI64: false' ,
1277+ timestampOutputFormat : 'FLOAT64' ,
1278+ useInt64Timestamp : false ,
1279+ expectedTsValue : expectedTsValueNanoseconds ,
1280+ } ,
1281+ {
1282+ name : 'TOF: INT64, UI64: true' ,
1283+ timestampOutputFormat : 'INT64' ,
1284+ useInt64Timestamp : true ,
1285+ expectedTsValue : expectedTsValueNanoseconds ,
1286+ } ,
1287+ {
1288+ name : 'TOF: INT64, UI64: false (error)' ,
1289+ timestampOutputFormat : 'INT64' ,
1290+ useInt64Timestamp : false ,
1291+ expectedTsValue : expectedTsValueNanoseconds ,
1292+ } ,
1293+ {
1294+ name : 'TOF: ISO8601_STRING, UI64: true (error)' ,
1295+ timestampOutputFormat : 'ISO8601_STRING' ,
1296+ useInt64Timestamp : true ,
1297+ expectedTsValue : undefined ,
1298+ expectedError : expectedErrorMessage ,
1299+ } ,
1300+ {
1301+ name : 'TOF: ISO8601_STRING, UI64: false' ,
1302+ timestampOutputFormat : 'ISO8601_STRING' ,
1303+ useInt64Timestamp : false ,
1304+ expectedTsValue : expectedTsValuePicoseconds ,
1305+ } ,
1306+ {
1307+ name : 'TOF: omitted, UI64: omitted (default INT64)' ,
1308+ timestampOutputFormat : undefined ,
1309+ useInt64Timestamp : undefined ,
1310+ expectedTsValue : expectedTsValuePicoseconds ,
1311+ } ,
1312+ {
1313+ name : 'TOF: omitted, UI64: true' ,
1314+ timestampOutputFormat : undefined ,
1315+ useInt64Timestamp : true ,
1316+ expectedTsValue : expectedTsValueNanoseconds ,
1317+ } ,
1318+ {
1319+ name : 'TOF: omitted, UI64: false (default ISO8601_STRING)' ,
1320+ timestampOutputFormat : undefined ,
1321+ useInt64Timestamp : false ,
1322+ expectedTsValue : expectedTsValueNanoseconds ,
1323+ } ,
1324+ {
1325+ name : 'TOF: TIMESTAMP_OUTPUT_FORMAT_UNSPECIFIED, UI64: omitted (default INT64)' ,
1326+ timestampOutputFormat : 'TIMESTAMP_OUTPUT_FORMAT_UNSPECIFIED' ,
1327+ useInt64Timestamp : undefined ,
1328+ expectedTsValue : expectedTsValueNanoseconds ,
1329+ } ,
1330+ {
1331+ name : 'TOF: FLOAT64, UI64: omitted (error)' ,
1332+ timestampOutputFormat : 'FLOAT64' ,
1333+ useInt64Timestamp : undefined ,
1334+ expectedTsValue : expectedTsValueNanoseconds ,
1335+ } ,
1336+ {
1337+ name : 'TOF: INT64, UI64: omitted' ,
1338+ timestampOutputFormat : 'INT64' ,
1339+ useInt64Timestamp : undefined ,
1340+ expectedTsValue : expectedTsValueNanoseconds ,
1341+ } ,
1342+ {
1343+ name : 'TOF: ISO8601_STRING, UI64: omitted (error)' ,
1344+ timestampOutputFormat : 'ISO8601_STRING' ,
1345+ useInt64Timestamp : undefined ,
1346+ expectedTsValue : expectedTsValuePicoseconds ,
1347+ } ,
1348+ ] ;
1349+
1350+ testCases . forEach ( testCase => {
1351+ it ( `should handle ${ testCase . name } ` , async ( ) => {
1352+ /*
1353+ The users use the new TIMESTAMP(12) type to indicate they want to
1354+ opt in to using timestampPrecision=12. The reason is that some queries
1355+ like `SELECT CAST(? as TIMESTAMP(12))` will fail if we set
1356+ timestampPrecision=12 and we don't want this code change to affect
1357+ existing users. Queries using TIMESTAMP_ADD are another example.
1358+ */
1359+ const query = {
1360+ query : 'SELECT ? as ts' ,
1361+ params : [
1362+ bigquery . timestamp ( '2023-01-01T12:00:00.123456789123Z' ) ,
1363+ ] ,
1364+ types : [ 'TIMESTAMP(12)' ] ,
1365+ } ;
1366+
1367+ const options : any = { } ;
1368+ if ( testCase . timestampOutputFormat !== undefined ) {
1369+ options [ 'formatOptions.timestampOutputFormat' ] =
1370+ testCase . timestampOutputFormat ;
1371+ }
1372+ if ( testCase . useInt64Timestamp !== undefined ) {
1373+ options [ 'formatOptions.useInt64Timestamp' ] =
1374+ testCase . useInt64Timestamp ;
1375+ }
1376+
1377+ try {
1378+ const [ rows ] = await bigquery . query ( query , options ) ;
1379+ if ( testCase . expectedError ) {
1380+ assert . fail (
1381+ `Query should have failed for ${ testCase . name } , but succeeded` ,
1382+ ) ;
1383+ }
1384+ assert . ok ( rows . length > 0 ) ;
1385+ assert . ok ( rows [ 0 ] . ts . value !== undefined ) ;
1386+ assert . strictEqual (
1387+ rows [ 0 ] . ts . value ,
1388+ testCase . expectedTsValue ,
1389+ ) ;
1390+ } catch ( err : any ) {
1391+ if ( ! testCase . expectedError ) {
1392+ throw err ;
1393+ }
1394+
1395+ const message = err . message ;
1396+ assert . strictEqual (
1397+ message ,
1398+ testCase . expectedError ,
1399+ `Expected ${ testCase . expectedError } error for ${ testCase . name } , got ${ message } (${ err . message } )` ,
1400+ ) ;
1401+ }
1402+ } ) ;
1403+ it ( `should handle nested ${ testCase . name } ` , async ( ) => {
1404+ /*
1405+ The users use the new TIMESTAMP(12) type to indicate they want to
1406+ opt in to using timestampPrecision=12. The reason is that some queries
1407+ like `SELECT CAST(? as TIMESTAMP(12))` will fail if we set
1408+ timestampPrecision=12 and we don't want this code change to affect
1409+ existing users.
1410+ */
1411+ const query = {
1412+ query : 'SELECT ? obj' ,
1413+ params : [
1414+ {
1415+ nested : {
1416+ a : bigquery . timestamp (
1417+ '2023-01-01T12:00:00.123456789123Z' ,
1418+ ) ,
1419+ } ,
1420+ } ,
1421+ ] ,
1422+ types : [
1423+ {
1424+ nested : {
1425+ a : 'TIMESTAMP(12)' ,
1426+ } ,
1427+ } ,
1428+ ] ,
1429+ } ;
1430+
1431+ const options : any = { } ;
1432+ if ( testCase . timestampOutputFormat !== undefined ) {
1433+ options [ 'formatOptions.timestampOutputFormat' ] =
1434+ testCase . timestampOutputFormat ;
1435+ }
1436+ if ( testCase . useInt64Timestamp !== undefined ) {
1437+ options [ 'formatOptions.useInt64Timestamp' ] =
1438+ testCase . useInt64Timestamp ;
1439+ }
1440+
1441+ try {
1442+ const [ rows ] = await bigquery . query ( query , options ) ;
1443+ if ( testCase . expectedError ) {
1444+ assert . fail (
1445+ `Query should have failed for ${ testCase . name } , but succeeded` ,
1446+ ) ;
1447+ }
1448+ assert . ok ( rows . length > 0 ) ;
1449+ assert . ok ( rows [ 0 ] . obj . nested . a . value !== undefined ) ;
1450+ assert . strictEqual (
1451+ rows [ 0 ] . obj . nested . a . value ,
1452+ testCase . expectedTsValue ,
1453+ ) ;
1454+ } catch ( err : any ) {
1455+ if ( ! testCase . expectedError ) {
1456+ throw err ;
1457+ }
1458+
1459+ const message = err . message ;
1460+ assert . strictEqual (
1461+ message ,
1462+ testCase . expectedError ,
1463+ `Expected ${ testCase . expectedError } error for ${ testCase . name } , got ${ message } (${ err . message } )` ,
1464+ ) ;
1465+ }
1466+ } ) ;
1467+ } ) ;
1468+ } ) ;
12411469 it ( 'should work with strings' , done => {
12421470 bigquery . query (
12431471 {
@@ -1481,7 +1709,7 @@ describe('BigQuery', () => {
14811709 done ( e ) ;
14821710 }
14831711 } ,
1484- )
1712+ ) ;
14851713 } ) ;
14861714
14871715 it ( 'should work with multiple types' , done => {
0 commit comments